package wsclient

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"runtime/debug"
	"sync"
	"time"

	"git.lxtend.com/lixiangwuxian/qqbot/action"
	"git.lxtend.com/lixiangwuxian/qqbot/constants"
	"git.lxtend.com/lixiangwuxian/qqbot/handler"
	"git.lxtend.com/lixiangwuxian/qqbot/model"
	"git.lxtend.com/lixiangwuxian/qqbot/qq_message"
	"git.lxtend.com/lixiangwuxian/qqbot/util"
	"github.com/gorilla/websocket"
)

type WebSocketClient struct {
	conn   *websocket.Conn
	closed bool
	Done   chan struct{}
	lock   sync.Mutex
}

func NewWebSocketClient(scheme, host, path string) (*WebSocketClient, error) {
	u := url.URL{Scheme: scheme, Host: host, Path: path}
	log.Printf("Connecting to %s", u.String())

	conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		return nil, err
	}

	client := &WebSocketClient{
		conn:   conn,
		closed: false,
		Done:   make(chan struct{}),
	}
	defer func() {
		if r := recover(); r != nil {
			stack := debug.Stack()
			errMsg := fmt.Sprintf("[%s] Panic recovered:\nError: %v\nStack Trace:\n%s\n",
				time.Now().Format("2006-01-02 15:04:05"), r, stack)

			// 写入日志文件
			log.Print(errMsg)
			go client.receiveMessages()
		}
	}()

	action.ActionManager.SetConn(conn)

	go client.receiveMessages()

	return client, nil
}

func (c *WebSocketClient) receiveMessages() {
	defer close(c.Done)
	defer util.ReportPanicToDev()
	for !c.closed {
		_, message, err := c.conn.ReadMessage()
		if err != nil {
			log.Println("Error reading message:", err)
			return
		}
		var event model.EventMessage
		if err = json.Unmarshal(message, &event); err != nil {
			log.Println("Error unmarshalling message:", err)
			return
		}
		msg := model.Message{
			UserId:        event.UserID,
			OriginMsgId:   event.MessageID,
			RawMsg:        event.RawMessage,
			StructuredMsg: qq_message.ParseStructMessages(event.Message),
			UserNickName:  event.Sender.Nickname,
		}

		if event.MessageType == constants.GROUP_MSG {
			msg.GroupInfo = model.GroupInfo{
				IsGroupMsg: true,
				GroupId:    event.GroupID,
				UserCard:   event.Sender.Card,
			}
		} else {
			msg.GroupInfo = model.GroupInfo{
				IsGroupMsg: false,
				GroupId:    event.Sender.GroupID,
			}
		}
		go func() {
			reply := handler.MsgInHandler(msg)
			if reply != nil {
				action.ActionManager.SendMsg(reply)
			}
		}()
	}
}

func (c *WebSocketClient) SendMessage(messageType int, message []byte) error {
	c.lock.Lock()
	defer c.lock.Unlock()
	return c.conn.WriteMessage(messageType, message)
}

func (c *WebSocketClient) Close() error {
	c.closed = true
	err := c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
	if err != nil {
		return err
	}
	<-c.Done
	return c.conn.Close()
}
