package service

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/carmel/gooxml/document"
	"github.com/fonchain_enterprise/fonchain-main/pkg/config"
	"github.com/fonchain_enterprise/fonchain-main/pkg/e"
	"github.com/fonchain_enterprise/fonchain-main/pkg/model"
	"github.com/fonchain_enterprise/fonchain-main/pkg/request"
	"github.com/fonchain_enterprise/fonchain-main/pkg/response"
	"github.com/fonchain_enterprise/fonchain-main/pkg/service/aliyun"
	"github.com/fonchain_enterprise/fonchain-main/pkg/utils"
	"github.com/fonchain_enterprise/fonchain-main/pkg/utils/stringutils"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type ChatRequestData struct {
	Model            string    `json:"model"`
	MaxTokens        int       `json:"max_tokens"`
	Temperature      float64   `json:"temperature"`
	TopP             float64   `json:"top_p"`
	PresencePenalty  float64   `json:"presence_penalty"`
	FrequencyPenalty float64   `json:"frequency_penalty"`
	Messages         []Message `json:"messages"`
	Stream           bool      `json:"stream"`
	ListUuid         string    `json:"listUuid"`
	Type             string    `json:"type"`
}

type Message struct {
	Role    string      `json:"role"`
	Content interface{} `json:"content"`
}

type RequestData struct {
	Model            string  `json:"model"`
	MaxTokens        int     `json:"max_tokens"`
	Temperature      float64 `json:"temperature"`
	TopP             float64 `json:"top_p"`
	PresencePenalty  float64 `json:"presence_penalty"`
	FrequencyPenalty float64 `json:"frequency_penalty"`
}

type ChatMsg struct {
	ID      string   `json:"id"`
	Object  string   `json:"object"`
	Created int      `json:"created"`
	Model   string   `json:"model"`
	Choices []Choice `json:"choices"`
}

type Choice struct {
	Index        int    `json:"index"`
	Delta        Delta  `json:"delta"`
	FinishReason string `json:"finish_reason"`
}

type Delta struct {
	Content string `json:"content"`
}

type DefineRequest struct {
	Model            string   `json:"model"`
	Stream           bool     `json:"stream,omitempty"`
	Stop             []string `json:"stop,omitempty"`
	PresencePenalty  float32  `json:"presence_penalty,omitempty"`
	FrequencyPenalty float32  `json:"frequency_penalty,omitempty"`
	// LogitBias is must be a token id string (specified by their token ID in the tokenizer), not a word string.
	// incorrect: `"logit_bias":{"You": 6}`, correct: `"logit_bias":{"1639": 6}`
	// refs: https://platform.openai.com/docs/api-reference/chat/create#chat/create-logit_bias
	LogitBias map[string]int `json:"logit_bias,omitempty"`
	User      string         `json:"user,omitempty"`
}

type MessageContentImage struct {
	Type     string `json:"type"`
	Text     string `json:"text"`
	ImageUrl string `json:"image_url"`
}

func Completion(ctx *gin.Context) {
	var request ChatRequestData
	bodyByte, _ := io.ReadAll(ctx.Request.Body)
	ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyByte))
	_ = json.Unmarshal(bodyByte, &request)
	ctx.Header("Content-Type", "text/event-stream")
	ctx.Header("Cache-Control", "no-cache")
	ctx.Header("Connection", "keep-alive")
	//url := config.ChatGptHost + "/chat/completion"
	url := config.ChatGptHost + "/baidu-chat/chat"
	//url := "http://114.217.150.188:9011" + "/baidu-chat/chat"
	var jsonData []byte
	var err error
	request, err = NewRequest(request)
	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	if request.ListUuid == "" {
		request.ListUuid = "web"
	}
	jsonData, err = json.Marshal(request)

	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	zap.L().Info("Completion", zap.Any("url", url))
	zap.L().Info("Completion", zap.Any("jsonData", jsonData))
	resp, errA := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if errA != nil {
		zap.L().Error("Completion", zap.Error(errA))
		ResponseQuickMsg(ctx, e.Failed, errA.Error(), nil)
		return
	}

	defer resp.Body.Close()
	ctx.Stream(func(w io.Writer) bool {
		buf := make([]byte, 4096)
		for {
			n, errS := resp.Body.Read(buf)
			if n > 0 {
				msg := string(buf[:n])
				ctx.Stream(func(w io.Writer) bool {
					if strings.Contains(msg, "DONE") {
						fmt.Println(msg)
						fmt.Fprint(w, msg)
						return false
					}
					fmt.Println(msg)
					zap.L().Info("Completion", zap.Any("msg", msg))
					fmt.Fprint(w, msg)
					return false
				})
			}
			if errS != nil {
				return false
			}
		}
	})
}

func AppCompletion(ctx *gin.Context) {
	var request ChatRequestData
	err := ctx.BindJSON(&request)
	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	ctx.Header("Content-Type", "text/event-stream")
	ctx.Header("Cache-Control", "no-cache")
	ctx.Header("Connection", "keep-alive")
	url := config.ChatGptHost + "/baidu-chat/chat"
	//url := "http://127.0.0.1:9010/chat/completion"
	request, err = NewRequest(request)
	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	jsonData, err := json.Marshal(request)
	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	if request.ListUuid == "" {
		request.ListUuid = "app"
	}
	resp, errA := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, errA.Error(), nil)
		return
	}
	defer resp.Body.Close()
	/*var chatData ChatMsg
	var content string
	ctx.Stream(func(w io.Writer) bool {
		buf := make([]byte, 4096)
		for {
			n, errS := resp.Body.Read(buf)
			if errS != nil {
				return false
			}
			if n > 0 {
				msg := string(buf[:n])
				msg = strings.Replace(msg, `\n`, "", -1)
				if len(msg) > 0 && msg[len(msg)-1] == '\n' {
					msg = msg[:len(msg)-1]
				}
				re := regexp.MustCompile(`data:\s*({.+?}]})`)
				matches := re.FindAllStringSubmatch(msg, -1)
				for _, match := range matches {
					msg = match[1]
					fmt.Println("msg1111", msg)
					if !strings.Contains(msg, "[DONE]") {
						fmt.Println("msg222", msg)
						_ = json.Unmarshal([]byte(msg), &chatData)
						fmt.Println("msg333", chatData)
						for _, choice := range chatData.Choices {
							content = fmt.Sprintf("%s%s", content, choice.Delta.Content)
						}
					}
				}
			}
		}
	}
	if err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	//fmt.Println("ap-complete", string(jsonData), content)
	response.ResponseQuickMsg(ctx, e.Ok, "", map[string]interface{}{
		"content": content,
	})*/
	defer resp.Body.Close()
	ctx.Stream(func(w io.Writer) bool {
		buf := make([]byte, 4096)
		for {
			n, errS := resp.Body.Read(buf)
			if n > 0 {
				msg := string(buf[:n])
				ctx.Stream(func(w io.Writer) bool {
					if strings.Contains(msg, "DONE") {
						fmt.Println(msg)
						fmt.Fprint(w, msg)
						return false
					}
					fmt.Println(msg)
					fmt.Fprint(w, msg)
					return false
				})
			}
			if errS != nil {
				return false
			}
		}
	})
	return
}

func AudioToText(c *gin.Context) {
	err := c.Request.ParseMultipartForm(32 << 20) // 限制最大文件大小
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	file, handler, err := c.Request.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	defer file.Close()
	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)

	part, err := writer.CreateFormFile("file", handler.Filename)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	_, err = io.Copy(part, file)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	err = writer.Close()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 创建一个新的请求,将文件发送到美国接口
	client := &http.Client{}
	url := config.ChatGptHost + "/chat/audio-to-text"
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	resp, err := client.Do(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		c.JSON(http.StatusInternalServerError, gin.H{"error": resp.Status})
		return
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	var audioToTextInfo model.AudioToTextResp
	if err = json.Unmarshal(data, &audioToTextInfo); err != nil {
		response.ResponseQuickMsg(c, e.Failed, e.GetMsg(e.JsonUnmarshal), nil)
	}
	response.ResponseQuickMsg(c, e.Failed, e.GetMsg(e.Success), map[string]interface{}{
		"text": audioToTextInfo.Data.Text,
	})
	return
}

func ImageUrlToBase64(imageURL string) (base64Val string, err error) {
	resp, err := http.Get(imageURL)
	if err != nil {
		err = errors.New("读取图片错误")
		return
	}
	defer resp.Body.Close()
	imageData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		err = errors.New("读取图片内容失败")
		return
	}
	base64Val = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData)
	return
}

func NewRequest(request ChatRequestData) (resp ChatRequestData, err error) {
	resp = request
	return
	if request.Model == "gpt-4-vision-preview" {
		//bodyByte, _ := io.ReadAll(ctx.Request.Body)
		for key, messages := range request.Messages {
			var contentData []map[string]string
			contentBytes, _ := json.Marshal(messages.Content)
			_ = json.Unmarshal(contentBytes, &contentData)
			var needModify bool
			for kk, content := range contentData {
				needModify = false
				if strings.Contains(content["image_url"], "http") {
					needModify = true
					contentData[kk]["image_url_origin"] = content["image_url"]
					if content["image_url"], err = ImageUrlToBase64(content["image_url"]); err != nil {
						return
					}
					contentData[kk]["image_url"] = content["image_url"]
				}
				fmt.Println(kk)
				fmt.Println(contentData)
			}
			fmt.Println(contentData)
			fmt.Println(key)
			if needModify {
				var mm []interface{}
				cc, _ := json.Marshal(contentData)
				_ = json.Unmarshal([]byte(cc), &mm)
				request.Messages[key].Content = mm
			}
		}
	}
	resp = request
	return
}

func ChatList(ctx *gin.Context) {
	var (
		req request.ChatListReq
		err error
	)
	if err = ctx.BindJSON(&req); err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	if req.UserUuid == "" {
		if mLoginInfoAny, exists := ctx.Get("mLoginInfo"); exists {
			userInfo := mLoginInfoAny.(model.LoginInfo)
			req.UserUuid = fmt.Sprint(userInfo.ID)
		}
	}
	if req.UserUuid == "" {
		response.ResponseQuickMsg(ctx, e.Failed, e.ErrNotLogin, nil)
		return
	}
	code, body, err := utils.PostBody(config.ChatGptHost+"/chat/list", req)
	if err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	fmt.Println(code)
	var newData interface{}
	_ = json.Unmarshal([]byte(body), &newData)
	ctx.JSON(http.StatusOK, newData)
	return
}

func ChatCreate(ctx *gin.Context) {
	var req request.ChatCreateReq
	var err error
	if err = ctx.BindJSON(&req); err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	if mLoginInfoAny, exists := ctx.Get("mLoginInfo"); exists {
		userInfo := mLoginInfoAny.(model.LoginInfo)
		req.UserUuid = fmt.Sprint(userInfo.ID)
	}
	if req.GptModel == "" {
		response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	code, body, err := utils.PostBody(config.ChatGptHost+"/chat/create", req)
	if err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	fmt.Println(code)
	var newData interface{}
	_ = json.Unmarshal([]byte(body), &newData)
	ctx.JSON(http.StatusOK, newData)
	return
}

func ChatDel(ctx *gin.Context) {
	var (
		req request.DelListReq
		err error
	)
	if err = ctx.BindJSON(&req); err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	code, body, err := utils.PostBody(config.ChatGptHost+"/chat/del", req)
	if err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	fmt.Println(code)
	var newData interface{}
	_ = json.Unmarshal([]byte(body), &newData)
	ctx.JSON(http.StatusOK, newData)
	return
}

func ChatDetail(ctx *gin.Context) {
	var (
		req request.ListDetailReq
		err error
	)
	if err = ctx.BindJSON(&req); err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	var newData interface{}
	var body string
	var code int
	if req.GptModel == "gpt-4-vision-preview" {
		file, _ := os.Open("./data/mock/detail.txt")
		defer file.Close()
		bodyBytes, _ := io.ReadAll(file)
		body = string(bodyBytes)
	} else {
		code, body, err = utils.PostBody(config.ChatGptHost+"/chat/detail", req)
		if err != nil {
			response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
			return
		}
		fmt.Println(code)
	}
	_ = json.Unmarshal([]byte(body), &newData)
	ctx.JSON(http.StatusOK, newData)
	return
}

func GetFileText(ctx *gin.Context) {
	var (
		filePath string
		text     string
		subtexts []string
	)
	var wordExts string = ".doc,.docx"
	// doc提交调用API解析
	file, err := ctx.FormFile("file")
	if err != nil {
		response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorReadFile), nil)
		return
	}
	filePath = fmt.Sprintf("%s%s", model.MediaPath, file.Filename)
	fileExt := filepath.Ext(filePath)
	if !strings.Contains(wordExts, fileExt) {
		response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ERROR_INVALID_FILE_EXT), nil)
		return
	}
	if fileExt == ".doc" {
		fileUrl, _err := quickBos(file, "doc", "file", "word", "")
		if _err != nil {
			response.ResponseQuickMsg(ctx, e.Failed, _err.Error(), nil)
			return
		}
		docId, _err := aliyun.DocStruct(fileUrl, file.Filename)
		if _err != nil {
			response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorUploadFile), nil)
			return
		}
		for {
			time.Sleep(1 * time.Second)
			complete, texts, errs := aliyun.DocResult(docId)
			if errs != nil {
				response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorUploadFile), nil)
				return
			}
			if complete {
				for _, v := range texts {
					text += strings.Trim(v, "")
				}
				break
			}
		}
		fmt.Println(text)
	} else {
		err = ctx.SaveUploadedFile(file, filePath)
		if err != nil {
			response.ResponseQuickMsg(ctx, e.Failed, e.ErrorSaveFile, nil)
			return
		}
		doc, _err := document.Open(filePath)
		defer os.Remove(filePath)
		if _err != nil {
			response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorReadFile), nil)
			return
		}
		for _, p := range doc.Paragraphs() {
			for _, run := range p.Runs() {
				if strings.Trim(run.Text(), "") != "" {
					text += strings.Trim(run.Text(), "")
				}
			}
		}
	}
	subtexts = stringutils.SplitText(text, 15000)
	response.ResponseQuickMsg(ctx, e.SUCCESS, e.GetMsg(e.Ok), map[string]interface{}{
		"count":     len(subtexts),
		"paragraph": subtexts,
	})
	return
}

func CompletionText(ctx *gin.Context) {
	var request ChatRequestData
	bodyByte, _ := io.ReadAll(ctx.Request.Body)
	ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyByte))
	_ = json.Unmarshal(bodyByte, &request)
	ctx.Header("Content-Type", "text/event-stream")
	ctx.Header("Cache-Control", "no-cache")
	ctx.Header("Connection", "keep-alive")
	url := config.ChatGptHost + "/baidu-chat/chat"
	var jsonData []byte
	var err error
	request, err = NewRequest(request)
	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}
	if request.ListUuid == "" {
		request.ListUuid = "web"
	}
	jsonData, err = json.Marshal(request)

	if err != nil {
		ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil)
		return
	}
	zap.L().Info("Completion", zap.Any("url", url))
	zap.L().Info("Completion", zap.Any("jsonData", jsonData))
	resp, errA := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if errA != nil {
		zap.L().Error("Completion", zap.Error(errA))
		ResponseQuickMsg(ctx, e.Failed, errA.Error(), nil)
		return
	}

	defer resp.Body.Close()
	// 创建一个 bytes.Buffer,并将 resp.Body 中的内容复制到其中
	var buf bytes.Buffer
	_, err = io.Copy(&buf, resp.Body)
	if err != nil {
		// 错误处理
		ResponseQuickMsg(ctx, e.Failed, err.Error(), nil)
		return
	}

	// 获取所有的数据,并将其转化为字符串
	allData := buf.String()

	// 打印所有的数据
	re := regexp.MustCompile(`"content":"(.*?)"`)
	matches := re.FindAllStringSubmatch(allData, -1)
	var answer string
	for _, match := range matches {
		answer = fmt.Sprintf("%s%s", answer, match[1])
	}
	ResponseQuickMsg(ctx, e.Ok, e.GetMsg(e.Success), map[string]string{
		"answer": answer,
	})
	return
}