package auth

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"sundynix-micro-go/app/auth/api/internal/svc"
	"sundynix-micro-go/app/auth/api/internal/types"
	sysPb "sundynix-micro-go/app/system/rpc/system"
	"sundynix-micro-go/common/utils/captcha"
	jwtUtil "sundynix-micro-go/common/utils/jwt"

	jwtv5 "github.com/golang-jwt/jwt/v5"
	"github.com/zeromicro/go-zero/core/logx"
)

type LoginLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	r      *http.Request
}

func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
	return &LoginLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
}

// SetRequest 注入 http.Request 以便从请求头取 clientId
func (l *LoginLogic) SetRequest(r *http.Request) {
	l.r = r
}

func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
	// 1. 校验验证码
	if !captcha.Verify(req.CaptchaId, req.Captcha) {
		return nil, fmt.Errorf("验证码错误")
	}

	// 2. 从请求头获取 clientId
	clientId := ""
	if l.r != nil {
		clientId = l.r.Header.Get("X-Client-Id")
	}
	if clientId == "" {
		return nil, fmt.Errorf("缺少客户端标识")
	}

	// 3. 查询 Client 信息，获取 token 过期时间
	clientResp, err := l.svcCtx.SystemRpc.GetClientById(l.ctx, &sysPb.GetClientByIdReq{ClientId: clientId})
	if err != nil {
		l.Errorf("[Login] ❌ 查询客户端失败 | clientId=%s | err=%v", clientId, err)
		return nil, fmt.Errorf("无效的客户端标识，请确认 X-Client-Id 请求头是否正确")
	}
	activeTimeout := clientResp.Client.ActiveTimeout
	if activeTimeout <= 0 {
		activeTimeout = 7200
	}

	// 4. 调用 system-rpc 验证账号密码
	rpcResp, err := l.svcCtx.SystemRpc.LoginByAccount(l.ctx, &sysPb.LoginByAccountReq{
		Account:  req.Account,
		Password: req.Password,
	})
	if err != nil {
		l.Errorf("[Login] ❌ 账号验证失败 | account=%s | err=%v", req.Account, err)
		return nil, err
	}

	// 5. 签发 JWT Token
	token, err := generateToken(l.svcCtx.Config.Auth.AccessSecret, activeTimeout, rpcResp.User)
	if err != nil {
		l.Errorf("[Login] ❌ 生成 Token 失败 | userId=%s | err=%v", rpcResp.User.Id, err)
		return nil, err
	}

	l.Infof("[Login] ✅ 登录成功 | account=%s | userId=%s | clientId=%s | tokenExpire=%ds",
		req.Account, rpcResp.User.Id, clientId, activeTimeout)

	return &types.LoginResp{
		Token: token,
		UserInfo: map[string]interface{}{
			"id":       rpcResp.User.Id,
			"name":     rpcResp.User.Name,
			"account":  rpcResp.User.Account,
			"nickName": rpcResp.User.NickName,
			"phone":    rpcResp.User.Phone,
			"avatarId": rpcResp.User.AvatarId,
			"gender":   rpcResp.User.Gender,
		},
	}, nil
}

func generateToken(accessSecret string, activeTimeout int64, userInfo *sysPb.UserInfo) (string, error) {
	j := jwtUtil.NewJWT(accessSecret)
	claims := jwtUtil.CustomClaims{
		BaseClaims: jwtUtil.BaseClaims{
			ID:      userInfo.Id,
			Account: userInfo.Account,
		},
		BufferTime: 3600,
		RegisteredClaims: jwtv5.RegisteredClaims{
			Audience:  jwtv5.ClaimStrings{"sundynix"},
			NotBefore: jwtv5.NewNumericDate(time.Now().Add(-1000)),
			ExpiresAt: jwtv5.NewNumericDate(time.Now().Add(time.Duration(activeTimeout) * time.Second)),
			Issuer:    "sundynix",
		},
	}
	token, err := j.CreateToken(claims)
	if err != nil {
		return "", fmt.Errorf("生成Token失败: %w", err)
	}
	return token, nil
}
