package core

import (
	"encoding/json"
	"fmt"
	"intent-system/pkg/config"
	"intent-system/pkg/itypes"
	"intent-system/pkg/utils"
	"net/http"
	"net/url"
	"strings"

	"github.com/civet148/gotools/randoms"
	"github.com/civet148/httpc"
	"github.com/civet148/log"
	"github.com/civet148/socketx"
	sapi "github.com/civet148/socketx/api"
	"github.com/civet148/sqlca/v2"
	"github.com/gorilla/websocket"
)

const (
	MessageId_Init               = "init"
	MessageId_CreateConversation = "createConversation"
	MessageId_SendMessage        = "sendMessage"
	MessageId_ToolCallResult     = "toolCallsResult"
)

const (
	MessageClientToolCallFunctionFinished = "INTENTION_CLIENT_TOOL_CALL_FUNCTION_FINISHED"
)

type ChatInitReq struct {
	MessageId string `json:"messageId"`
	Key       string `json:"key"`
	Secret    string `json:"secret"`
}

type ChatInitResp struct {
	Result  string `json:"result"`
	Message string `json:"message"`
	Token   string `json:"token"`
}

type ChatConversationReq struct {
	MessageId string `json:"messageId"`
	Key       string `json:"key"`
}

type ChatConversationResp struct {
	Result         string `json:"result"`
	Message        string `json:"message"`
	ConversationId string `json:"conversationId"`
}

type ChatContentMessage struct {
	MessageId      string `json:"messageId"`
	Key            string `json:"key"`
	ConversationId string `json:"conversationId"`
	Content        string `json:"content"`
}

type GatewayCore struct {
	db  *sqlca.Engine
	cfg *config.Config
}

func NewGatewayCore(cfg *config.Config, db *sqlca.Engine) *GatewayCore {
	return &GatewayCore{
		db:  db,
		cfg: cfg,
	}
}

func (m *GatewayCore) makeSessionId() string {
	strRandomTime := utils.NowRandom()
	strRandom := randoms.RandomAlphaOrNumeric(12, true, true)
	return fmt.Sprintf("%s-%s", strRandom, strRandomTime)
}

func (m *GatewayCore) WebSocketRelay(conn *websocket.Conn) (err error) {
	var ws *itypes.WebSocket
	var socket *socketx.SocketClient
	var strToken string
	strToken, err = m.httpChatInit()
	if err != nil {
		return log.Errorf(err.Error())
	}
	ws, socket, err = m.webSocketConnect(conn, strToken)
	if err != nil {
		return log.Errorf("relay to [%s] error [%s]", m.cfg.GatewayUrl, err.Error())
	}
	go m.webSocketRemoteLoop(ws, socket)
	return m.webSocketPeerLoop(ws, socket)
}

func (m *GatewayCore) makeInitHttpUrl(strUrl string) string {
	ui, err := url.Parse(strUrl)
	if err != nil {
		return fmt.Sprintf("http://%s", strUrl)
	}
	return fmt.Sprintf("http://%s", ui.Host)
}

func (m *GatewayCore) makeGatewayTokenUrl(strUrl, strToken string) string {
	ui, err := url.Parse(strUrl)
	if err != nil {
		return fmt.Sprintf("ws://%s/ws/%s", strUrl, strToken)
	}
	return fmt.Sprintf("ws://%s/ws/%s", ui.Host, strToken)
}

func (m *GatewayCore) httpChatInit() (string, error) {
	var err error
	strGatewayUrl := m.cfg.GatewayUrl
	strGatewayKey := m.cfg.GatewayKey
	strGatewaySecret := m.cfg.GatewaySecret

	strInitUrl := m.makeInitHttpUrl(strGatewayUrl)
	var header = m.makeBasicAuthHeader(strGatewayKey, strGatewaySecret)
	cli := httpc.NewHttpClient(&httpc.Option{
		Timeout: 30,
		Header:  header,
	})
	var response *httpc.Response
	log.Debugf("chat init to [%s] with header [%+v]", strInitUrl, header)
	var initReq = &ChatInitReq{
		MessageId: MessageId_Init,
		Key:       strGatewayKey,
		Secret:    strGatewaySecret,
	}
	response, err = cli.PostJson(strInitUrl, initReq)
	if response.StatusCode != http.StatusOK {
		return "", log.Errorf("chat init response status [%v] message [%s]", response.StatusCode, response.Body)
	}
	log.Debugf("chat init request [%+v] response [%s]", initReq, response.Body)
	var resp ChatInitResp
	err = response.Unmarshal(&resp)
	if err != nil {
		return "", log.Errorf("unmarshal response body to json structure error [%s]", err.Error())
	}
	if resp.Result != "True" {
		return "", log.Errorf("chat init failed with message [%s]", resp.Message)
	}
	if resp.Token == "" {
		return "", log.Errorf("chat init failed with empty token")
	}
	log.Infof("chat init response token [%s]", resp.Token)
	return resp.Token, nil
}

func (m *GatewayCore) makeBasicAuthHeader(strGatewayKey, strGatewaySecret string) http.Header {
	strBasic := socketx.BasicAuth(strGatewayKey, strGatewaySecret)
	return map[string][]string{
		itypes.HEADER_AUTHORIZATION: {strBasic},
	}
}

func (m *GatewayCore) webSocketConnect(conn *websocket.Conn, strToken string) (*itypes.WebSocket, *socketx.SocketClient, error) {
	var err error
	var ws *itypes.WebSocket
	sock := socketx.NewClient()
	strGatewayKey := m.cfg.GatewayKey
	strGatewaySecret := m.cfg.GatewaySecret
	strGatewayUrl := m.makeGatewayTokenUrl(m.cfg.GatewayUrl, strToken)
	log.Debugf("websocket connect to [%s] key [%s] secret [%s]", strGatewayUrl, strGatewayKey, strGatewaySecret)
	var header = m.makeBasicAuthHeader(strGatewayKey, strGatewaySecret)
	err = sock.Connect(strGatewayUrl, sapi.SocketOption{
		Header: header,
	})
	if err != nil {
		return nil, nil, log.Errorf("connect to [%s] with header [%+v] error [%s]", strGatewayUrl, header, err)
	}
	createReq := &ChatConversationReq{
		MessageId: MessageId_CreateConversation,
		Key:       strGatewayKey,
	}
	log.Json("create conversation request", createReq)
	_, err = sock.SendJson(createReq)
	if err != nil {
		return nil, nil, log.Errorf("send create conversation request error [%s]", err.Error())
	}
	var msg *sapi.SockMessage
	msg, err = sock.Recv(-1)
	if err != nil {
		return nil, nil, log.Errorf("receive create conversation response error [%s]", err.Error())
	}
	log.Infof("create conversation response body [%s]", msg.Data)
	var conversation ChatConversationResp
	err = json.Unmarshal(msg.Data, &conversation)
	if err != nil {
		return nil, nil, log.Errorf("unmarshal conversation response [%s] error [%s]", msg.Data, err.Error())
	}
	log.Infof("conversation response [%+v]", conversation)
	if conversation.Result == "False" {
		return nil, nil, log.Errorf("conversation create failed, message body [%s] ", msg.Data)
	}
	ws, err = itypes.NewWebSocket(conn, conversation.ConversationId)
	if err != nil {
		return nil, nil, log.Errorf("new websocket error [%s]", err)
	}
	return ws, sock, nil
}

func (m *GatewayCore) webSocketPeerLoop(ws *itypes.WebSocket, socket *socketx.SocketClient) (err error) {
	defer ws.Close()
	defer socket.Close()
	for {
		var msg []byte
		msg, err = ws.ReadMessage()
		if err != nil {
			log.Warnf("websocket [%s] read message error [%s]", ws.Id(), err.Error())
			return err
		}
		log.Debugf("websocket [%s] received msg [%s]", ws.Id(), msg)
		var strMessageId = MessageId_SendMessage
		if strings.Compare(string(msg), MessageClientToolCallFunctionFinished) == 0 {
			strMessageId = MessageId_ToolCallResult //客户端处理函数结束
		}
		var chat = ChatContentMessage{
			MessageId:      strMessageId,
			Key:            m.cfg.GatewayKey,
			ConversationId: ws.Id(),
			Content:        string(msg),
		}

		_, err = socket.SendJson(chat)
		if err != nil {
			log.Errorf("relay socket closed by remote server, error [%s]", err.Error())
			return err
		}
	}
}

func (m *GatewayCore) webSocketRemoteLoop(ws *itypes.WebSocket, socket *socketx.SocketClient) (err error) {
	defer ws.Close()
	defer socket.Close()
	for {
		var msg *sapi.SockMessage
		msg, err = socket.Recv(-1)
		if err != nil {
			log.Warnf("relay socket read message error [%s]", err.Error())
			return err
		}
		log.Debugf("websocket [%s] received msg [%s]", ws.Id(), msg.Data)
		err = ws.WriteMessage(msg.Data)
		if err != nil {
			log.Errorf("websocket write message error [%s]", err.Error())
			return err
		}
	}
}
