package manager

import (
	"Wine-Server/utils"
	"Wine-Server/utils/tables"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

func managerConnected(mid string, conn *websocket.Conn) *tables.ManagerTable {
	table := &tables.ManagerTable{Id: mid}
	err := table.Get()
	if err != nil {
		utils.Logger.Println("manager query failed:", err)
		_ = conn.WriteJSON(utils.WsError("manager query failed"))
		return nil
	}
	table.Last = utils.TimeNow()
	err = table.Update(utils.JsonType{"last": table.Last})
	if err != nil {
		utils.Logger.Println("update last active time failed:", err)
		_ = conn.WriteJSON(utils.WsError("Update last active time failed"))
		return nil
	}
	if _, exist := utils.ManagerWss[mid]; exist {
		_ = conn.WriteJSON(utils.WsEvent("onePageOnly", nil))
		return nil
	}
	utils.ManagerLock.Lock()
	utils.ManagerWss[mid] = conn
	utils.ManagerLock.Unlock()
	utils.Logger.Printf("manager[%s] connected\n", mid)
	return table
}

func managerDisconnect(mid string) {
	utils.ManagerLock.Lock()
	delete(utils.ManagerWss, mid)
	utils.ManagerLock.Unlock()
	utils.Logger.Printf("manager[%s] disconnected\n", mid)
}

func loginSocket(conn *websocket.Conn) (string, error) {
	var mid string
	for {
		var msg utils.WsMsg
		err := conn.ReadJSON(&msg)
		if err != nil {
			_ = conn.Close()
			return "", err
		}
		switch msg.Event {
		case "pin":
			keepAlive(conn)
			break
		case "getUserInfo": // in case: redis token expired
			break
		case "login":
			mid, err = userLogin(conn, msg.Data)
			if err == nil {
				return mid, nil
			}
			break
		default:
			utils.Logger.Println(msg.Event)
			_ = conn.WriteJSON(utils.WsError("loginSocket event only"))
			break
		}
	}
}

func SocketHandler(ctx *gin.Context) {
	conn, err := utils.UpgradeHttp2Ws.Upgrade(ctx.Writer, ctx.Request, nil)
	if err != nil {
		utils.Logger.Println("can't establish manager socket connect")
		ctx.JSON(utils.HttpError, utils.Fail("can't establish socket connect"))
		return
	}
	getSecrets(conn)
	var mid string
	token := ctx.Param("token")
	if token == "undefined" { // no token -> login
		mid, err = loginSocket(conn)
	} else {
		mid, err = utils.Redis.Get(utils.WxPayCli, token).Result()
		if err != nil { // expired -> login
			_ = conn.WriteJSON(utils.WsEvent("tokenExpired", nil))
			mid, err = loginSocket(conn)
		} else { // update expire time
			err = utils.Redis.Expire(utils.WxPayCli, token, utils.Duration(7*24*60*60)).Err()
			if err != nil {
				utils.Logger.Println(err)
			}
		}
	}

	if err != nil {
		return
	}
	manager := managerConnected(mid, conn)
	if manager == nil {
		return
	}
	defer managerDisconnect(mid)

	for {
		var msg utils.WsMsg
		err = conn.ReadJSON(&msg)
		if err != nil {
			return
		}
		switch msg.Event {
		case "pin":
			keepAlive(conn)
			break
		case "getUserInfo":
			getUserInfo(conn, manager)
			break
		case "logout":
			userLogout(conn, msg.Data)
			return
		case "superQueryManager":
			superQueryManager(conn, manager, msg.Data)
			break
		case "superQueryDevice":
			superQueryDevice(conn, manager, msg.Data)
			break
		case "superUpdateDevice":
			superUpdateDevice(conn, manager, msg.Data)
			break
		case "superBatchAssign":
			superBatchAssign(conn, manager, msg.Data)
			break
		case "superQueryWine":
			superQueryWine(conn, manager, msg.Data)
			break
		case "superDeleteWine":
			superDeleteWine(conn, manager, msg.Data)
			break
		default:
			_ = conn.WriteJSON(utils.WsError("unrecognized event"))
			break
		}
	}
}
