package system

import (
	"sync"
	"time"

	"kra/internal/conf"

	"github.com/go-kratos/kratos/v2/transport/http"
	"github.com/mojocn/base64Captcha"
)

// CaptchaService 验证码服务
type CaptchaService struct {
	keyLong            int
	imgWidth           int
	imgHeight          int
	openCaptcha        int // 防爆次数，0表示关闭
	openCaptchaTimeOut int // 缓存超时时间(秒)
	store              base64Captcha.Store
	blackCache         *BlackCache
}

// CaptchaConfig 验证码配置（保留用于兼容）
type CaptchaConfig struct {
	KeyLong            int
	ImgWidth           int
	ImgHeight          int
	OpenCaptcha        int
	OpenCaptchaTimeOut int
}

// BlackCache 黑名单缓存
type BlackCache struct {
	mu    sync.RWMutex
	items map[string]*cacheItem
}

type cacheItem struct {
	value      int
	expiration time.Time
}

// NewBlackCache 创建黑名单缓存
func NewBlackCache() *BlackCache {
	bc := &BlackCache{
		items: make(map[string]*cacheItem),
	}
	go bc.cleanup()
	return bc
}

func (c *BlackCache) Set(key string, value int, duration time.Duration) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.items[key] = &cacheItem{
		value:      value,
		expiration: time.Now().Add(duration),
	}
}

func (c *BlackCache) Get(key string) (int, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	item, ok := c.items[key]
	if !ok || time.Now().After(item.expiration) {
		return 0, false
	}
	return item.value, true
}

func (c *BlackCache) cleanup() {
	ticker := time.NewTicker(time.Minute)
	for range ticker.C {
		c.mu.Lock()
		now := time.Now()
		for k, v := range c.items {
			if now.After(v.expiration) {
				delete(c.items, k)
			}
		}
		c.mu.Unlock()
	}
}

// CaptchaResponse 验证码响应
type CaptchaResponse struct {
	CaptchaId     string `json:"captchaId"`
	PicPath       string `json:"picPath"`
	CaptchaLength int    `json:"captchaLength"`
	OpenCaptcha   bool   `json:"openCaptcha"`
}

// NewCaptchaService 创建验证码服务
func NewCaptchaService(c *conf.Captcha) *CaptchaService {
	return &CaptchaService{
		keyLong:            int(c.KeyLong),
		imgWidth:           int(c.ImgWidth),
		imgHeight:          int(c.ImgHeight),
		openCaptcha:        int(c.OpenCaptcha),
		openCaptchaTimeOut: int(c.OpenCaptchaTimeout),
		store:              base64Captcha.DefaultMemStore,
		blackCache:         NewBlackCache(),
	}
}

// RegisterRoutes 注册路由
func (s *CaptchaService) RegisterRoutes(rg *RouterGroup) {
	// 验证码（公开路由，无需认证）
	rg.Public.POST("/base/captcha", s.handleCaptcha)
}

func (s *CaptchaService) handleCaptcha(ctx http.Context) error {
	// 获取客户端IP
	clientIP := ctx.Request().RemoteAddr

	// 判断验证码是否开启
	openCaptcha := s.openCaptcha
	openCaptchaTimeOut := s.openCaptchaTimeOut

	v, ok := s.blackCache.Get(clientIP)
	if !ok {
		s.blackCache.Set(clientIP, 1, time.Second*time.Duration(openCaptchaTimeOut))
	}

	var oc bool
	if openCaptcha == 0 || openCaptcha < v {
		oc = true
	}

	// 生成验证码
	driver := base64Captcha.NewDriverDigit(
		s.imgHeight,
		s.imgWidth,
		s.keyLong,
		0.7,
		80,
	)
	cp := base64Captcha.NewCaptcha(driver, s.store)
	id, b64s, _, err := cp.Generate()
	if err != nil {
		return ctx.Result(200, map[string]any{
			"code": 7,
			"msg":  "验证码获取失败",
		})
	}

	return ctx.Result(200, map[string]any{
		"code": 0,
		"msg":  "验证码获取成功",
		"data": CaptchaResponse{
			CaptchaId:     id,
			PicPath:       b64s,
			CaptchaLength: s.keyLong,
			OpenCaptcha:   oc,
		},
	})
}

// Verify 验证验证码
func (s *CaptchaService) Verify(id, answer string) bool {
	return s.store.Verify(id, answer, true)
}
