package dingtalk

import (
	"ai_scheduler/internal/config"
	"ai_scheduler/internal/data/constants"
	"ai_scheduler/internal/data/impl"
	"ai_scheduler/internal/data/model"
	"ai_scheduler/internal/entitys"
	"ai_scheduler/internal/pkg"
	"ai_scheduler/internal/pkg/l_request"
	"ai_scheduler/utils"
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/gofiber/fiber/v2/log"
	"github.com/redis/go-redis/v9"
	"xorm.io/builder"
)

type Auth struct {
	redis         *redis.Client
	cfg           *config.Config
	botConfigImpl *impl.BotConfigImpl
}

func NewAuth(cfg *config.Config, redis *utils.Rdb, botConfigImpl *impl.BotConfigImpl) *Auth {
	return &Auth{
		redis:         redis.Rdb,
		cfg:           cfg,
		botConfigImpl: botConfigImpl,
	}
}

func (a *Auth) GetAccessToken(ctx context.Context, clientId string, clientSecret string) (authInfo *AuthInfo, err error) {
	if clientId == "" {
		return nil, errors.New("clientId  is empty")
	}
	accessToken := a.redis.Get(ctx, a.getKey(clientId)).Val()
	var expire time.Duration
	if accessToken == "" {
		dingTalkAuthRes, _err := a.getNewAccessToken(ctx, clientId, clientSecret)
		if _err != nil {
			return nil, _err
		}
		expire = time.Duration(dingTalkAuthRes.ExpireIn-3600) * time.Second
		err = a.redis.SetEx(ctx, a.getKey(clientId), dingTalkAuthRes.AccessToken, expire).Err()
		if err != nil {
			return
		}
		accessToken = dingTalkAuthRes.AccessToken
	} else {
		expire, _ = a.redis.TTL(ctx, a.getKey(clientId)).Result()
	}
	return &AuthInfo{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		AccessToken:  accessToken,
		Expire:       expire,
	}, nil
}

func (a *Auth) getKey(clientId string) string {
	return a.cfg.Redis.Key + ":" + constants.DingTalkAuthBaseKeyPrefix + ":" + clientId
}

func (a *Auth) getKeyBot(botCode string) string {
	return a.cfg.Redis.Key + ":" + constants.DingTalkAuthBaseKeyBotPrefix + ":" + botCode
}

func (a *Auth) getNewAccessToken(ctx context.Context, clientId string, clientSecret string) (auth DingTalkAuthIRes, err error) {
	if clientId == "" || clientSecret == "" {
		err = errors.New("clientId or clientSecret  is empty")
		return
	}

	req := l_request.Request{
		Method: http.MethodPost,
		Url:    "https://api.dingtalk.com/v1.0/oauth2/accessToken",
		Json: map[string]interface{}{
			"appKey":    clientId,
			"appSecret": clientSecret,
		},
	}
	res, err := req.Send()
	if err != nil {
		return
	}
	err = json.Unmarshal(res.Content, &auth)

	return
}

func (a *Auth) GetTokenFromBotOption(ctx context.Context, botOption ...BotOption) (token *AuthInfo, err error) {
	botInfo := &Bot{}
	for _, option := range botOption {
		option(botInfo)
	}

	if botInfo.Id == 0 && botInfo.BotConfig == nil && botInfo.BotCode == "" {
		err = errors.New("botInfo is nil")
		return
	}

	if botInfo.BotConfig == nil {
		err = a.GetBotConfigFromModel(botInfo)
		if err != nil {
			return
		}
	}

	authInfo := a.redis.Get(ctx, a.getKeyBot(botInfo.BotConfig.RobotCode)).Val()
	if authInfo == "" {
		var botConfig entitys.DingTalkBot
		err = json.Unmarshal([]byte(botInfo.BotConfig.BotConfig), &botConfig)
		if err != nil {
			log.Infof("初始化“%s”失败:%s", botInfo.BotConfig.BotName, err.Error())
			return
		}
		token, err = a.GetAccessToken(ctx, botConfig.ClientId, botConfig.ClientSecret)
		if err != nil {
			return
		}
		err = a.redis.SetEx(ctx, a.getKeyBot(botInfo.BotConfig.RobotCode), pkg.JsonStringIgonErr(token), token.Expire).Err()
		if err != nil {
			return
		}
	} else {
		var tokenData AuthInfo
		err = json.Unmarshal([]byte(authInfo), &tokenData)
		token = &tokenData
	}
	return
}

func (a *Auth) GetBotConfigFromModel(botInfo *Bot) (err error) {
	var (
		botConfigDo model.AiBotConfig
	)
	cond := builder.NewCond()
	if botInfo.Id > 0 {
		cond = cond.And(builder.Eq{"bot_id": botInfo.Id})
	}
	if botInfo.BotCode != "" {
		cond = cond.And(builder.Eq{"robot_code": botInfo.BotCode})
	}
	err = a.botConfigImpl.GetOneBySearchToStrut(&cond, &botConfigDo)
	if err != nil {
		return
	}
	if botConfigDo.BotID == 0 {
		err = errors.New("未找到机器人服务配置")
		return
	}
	botInfo.BotConfig = &botConfigDo
	return nil
}
