// Package tcpserver
// @Link  https://github.com/bufanyun/hotgo
// @Copyright  Copyright (c) 2023 HotGo CLI
// @Author  Ms <133814250@qq.com>
// @License  https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcpserver

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"hotgo/internal/consts"
	"hotgo/internal/dao"
	"hotgo/internal/library/network/tcp"
	"hotgo/internal/model/entity"
	"hotgo/utility/convert"
	"hotgo/utility/encrypt"
)

// onServerLogin 处理客户端登录
func (s *sTCPServer) onServerLogin(ctx context.Context, req *tcp.ServerLoginReq) {
	var (
		conn   = tcp.ConnFromCtx(ctx)
		models *entity.SysServeLicense
		res    = new(tcp.ServerLoginRes)
		cols   = dao.SysServeLicense.Columns()
	)

	if conn == nil {
		g.Log().Warningf(ctx, "conn is nil.")
		return
	}

	if err := dao.SysServeLicense.Ctx(ctx).Where(cols.Appid, req.AppId).Scan(&models); err != nil {
		return
	}
	if models == nil {
		res.SetError(gerror.New("授权信息不存在"))
		_ = conn.Send(ctx, res)
		return
	}

	// 验证签名
	sign := encrypt.Md5ToString(fmt.Sprintf("%v%v%v", models.Appid, req.Timestamp, models.SecretKey))
	if sign != req.Sign {
		res.SetError(gerror.New("签名错误，请检查！"))
		_ = conn.Send(ctx, res)
		return
	}

	if models.Status != consts.StatusEnabled {
		res.SetError(gerror.New("授权已禁用，请联系管理员"))
		_ = conn.Send(ctx, res)
		return
	}

	if models.Group != req.Group {
		res.SetError(gerror.New("你登录的授权分组未得到授权，请联系管理员"))
		_ = conn.Send(ctx, res)
		return
	}

	if models.EndAt.Before(gtime.Now()) {
		res.SetError(gerror.New("授权已过期，请联系管理员"))
		_ = conn.Send(ctx, res)
		return
	}

	ip := gstr.StrTillEx(conn.RemoteAddr().String(), ":")
	if !convert.MatchIpStrategy(models.AllowedIps, ip) {
		res.SetError(gerror.New("IP(" + ip + ")未授权，请联系管理员"))
		_ = conn.Send(ctx, res)
		return
	}

	var routes []string
	if err := models.Routes.Scan(&routes); err != nil {
		res.SetError(gerror.New("授权路由解析失败，请联系管理员"))
		_ = conn.Send(ctx, res)
		return
	}

	// 拿出当前登录应用的所有客户端
	clients := s.serv.GetAppIdClients(models.Appid)

	// 检查多地登录，如果连接超过上限，则断开当前许可证下的所有连接
	if len(clients)+1 > models.OnlineLimit {
		for _, client := range clients {
			client.Close()
		}
		res.SetError(gerror.New("授权登录端超出上限，请勿多地登录"))
		_ = conn.Send(ctx, res)
		return
	}

	for _, client := range clients {
		if client.Auth.Name == req.Name {
			res.SetError(gerror.Newf("应用名称[%v]已存在登录用户，当前连接已被拒绝。", req.Name))
			_ = conn.Send(ctx, res)
			return
		}
	}

	auth := &tcp.AuthMeta{
		Name:      req.Name,
		Extra:     req.Extra,
		Group:     models.Group,
		AppId:     models.Appid,
		SecretKey: models.SecretKey,
		EndAt:     models.EndAt,
		Routes:    routes,
	}
	s.serv.AuthClient(conn, auth)

	update := g.Map{
		cols.LoginTimes:   models.LoginTimes + 1,
		cols.LastLoginAt:  gtime.Now(),
		cols.LastActiveAt: gtime.Now(),
		cols.RemoteAddr:   conn.RemoteAddr().String(),
	}
	if _, err := dao.SysServeLicense.Ctx(ctx).Where(cols.Id, models.Id).Data(update).Update(); err != nil {
		res.SetError(err)
		_ = conn.Send(ctx, res)
		return
	}

	g.Log().Debugf(ctx, "onServerLogin succeed. appid:%v, group:%v, name:%v", auth.AppId, auth.Group, auth.Name)
	_ = conn.Send(ctx, res)
}

// onServerHeartbeat 处理客户端心跳
func (s *sTCPServer) onServerHeartbeat(ctx context.Context, req *tcp.ServerHeartbeatReq) {
	var (
		conn = tcp.ConnFromCtx(ctx)
		res  = new(tcp.ServerHeartbeatRes)
	)

	if conn == nil {
		g.Log().Warningf(ctx, "conn is nil.")
		return
	}

	client := s.serv.GetClient(conn.Conn)
	if client == nil {
		res.SetError(gerror.New("登录异常，请重新登录"))
		_ = conn.Send(ctx, res)
		return
	}

	// 更新心跳
	client.Heartbeat = gtime.Timestamp()

	// 更新活跃时间
	update := g.Map{
		dao.SysServeLicense.Columns().LastActiveAt: gtime.Now(),
	}
	if _, err := dao.SysServeLicense.Ctx(ctx).Where(dao.SysServeLicense.Columns().Appid, client.Auth.AppId).Data(update).Update(); err != nil {
		res.SetError(err)
		_ = conn.Send(ctx, res)
		return
	}

	_ = conn.Send(ctx, res)
}
