package service

import (
	"bytes"
	"context"
	crand "crypto/rand"
	"encoding/hex"
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"math/big"
	"math/rand"
	"strings"
	"time"

	"github.com/user-management-system/internal/cache"
)

const (
	captchaWidth  = 120
	captchaHeight = 40
	captchaLength = 4 // 验证码位数
	captchaTTL    = 5 * time.Minute
)

// captchaChars 验证码字符集（去掉容易混淆的字符 0/O/1/I/l）
const captchaChars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz"

// CaptchaService 图形验证码服务
type CaptchaService struct {
	cache *cache.CacheManager
}

// NewCaptchaService 创建验证码服务
func NewCaptchaService(cache *cache.CacheManager) *CaptchaService {
	return &CaptchaService{cache: cache}
}

// CaptchaResult 验证码生成结果
type CaptchaResult struct {
	CaptchaID string // 验证码ID（UUID）
	ImageData []byte // PNG图片字节
}

// Generate 生成图形验证码
func (s *CaptchaService) Generate(ctx context.Context) (*CaptchaResult, error) {
	// 生成随机验证码文字
	text, err := s.randomText(captchaLength)
	if err != nil {
		return nil, fmt.Errorf("生成验证码文本失败: %w", err)
	}

	// 生成验证码ID
	captchaID, err := s.generateID()
	if err != nil {
		return nil, fmt.Errorf("生成验证码ID失败: %w", err)
	}

	// 生成图片
	imgData, err := s.renderImage(text)
	if err != nil {
		return nil, fmt.Errorf("生成验证码图片失败: %w", err)
	}

	// 存入缓存（不区分大小写，存小写）
	cacheKey := "captcha:" + captchaID
	s.cache.Set(ctx, cacheKey, strings.ToLower(text), captchaTTL, captchaTTL)

	return &CaptchaResult{
		CaptchaID: captchaID,
		ImageData: imgData,
	}, nil
}

// Verify 验证验证码（验证后立即删除，防止重放）
func (s *CaptchaService) Verify(ctx context.Context, captchaID, answer string) bool {
	if captchaID == "" || answer == "" {
		return false
	}

	cacheKey := "captcha:" + captchaID
	val, ok := s.cache.Get(ctx, cacheKey)
	if !ok {
		return false
	}

	// 删除验证码（一次性使用）
	s.cache.Delete(ctx, cacheKey)

	expected, ok := val.(string)
	if !ok {
		return false
	}

	return strings.ToLower(answer) == expected
}

// VerifyWithoutDelete 验证验证码但不删除（用于测试）
func (s *CaptchaService) VerifyWithoutDelete(ctx context.Context, captchaID, answer string) bool {
	if captchaID == "" || answer == "" {
		return false
	}

	cacheKey := "captcha:" + captchaID
	val, ok := s.cache.Get(ctx, cacheKey)
	if !ok {
		return false
	}

	expected, ok := val.(string)
	if !ok {
		return false
	}

	return strings.ToLower(answer) == expected
}

// ValidateCaptcha 验证验证码（对外暴露，验证后删除）
func (s *CaptchaService) ValidateCaptcha(ctx context.Context, captchaID, answer string) error {
	if captchaID == "" {
		return errors.New("验证码ID不能为空")
	}
	if answer == "" {
		return errors.New("验证码答案不能为空")
	}
	if !s.Verify(ctx, captchaID, answer) {
		return errors.New("验证码错误或已过期")
	}
	return nil
}

// randomText 生成随机验证码文字
func (s *CaptchaService) randomText(length int) (string, error) {
	chars := []byte(captchaChars)
	result := make([]byte, length)
	for i := range result {
		n, err := crand.Int(crand.Reader, big.NewInt(int64(len(chars))))
		if err != nil {
			return "", err
		}
		result[i] = chars[n.Int64()]
	}
	return string(result), nil
}

// generateID 生成验证码ID（crypto/rand 保证全局唯一，无碰撞）
func (s *CaptchaService) generateID() (string, error) {
	b := make([]byte, 16)
	if _, err := crand.Read(b); err != nil {
		return "", err
	}
	return fmt.Sprintf("%d-%s", time.Now().UnixNano(), hex.EncodeToString(b)), nil
}

// renderImage 将文字渲染为PNG验证码图片（纯Go实现，无外部字体依赖）
func (s *CaptchaService) renderImage(text string) ([]byte, error) {
	// 创建 RGBA 图像
	img := image.NewRGBA(image.Rect(0, 0, captchaWidth, captchaHeight))

	// 随机背景色（浅色）
	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
	// #nosec G115 - rng.Intn(35) returns 0-34, so 220+34=254 fits in uint8
	bgColor := color.RGBA{
		R: uint8(220 + rng.Intn(35)),
		G: uint8(220 + rng.Intn(35)), // #nosec G115
		B: uint8(220 + rng.Intn(35)), // #nosec G115
		A: 255,
	}
	draw.Draw(img, img.Bounds(), &image.Uniform{bgColor}, image.Point{}, draw.Src)

	// 绘制干扰线
	for i := 0; i < 5; i++ {
		lineColor := color.RGBA{
			R: uint8(rng.Intn(200)), // #nosec G115
			G: uint8(rng.Intn(200)), // #nosec G115
			B: uint8(rng.Intn(200)), // #nosec G115
			A: 255,
		}
		x1 := rng.Intn(captchaWidth)
		y1 := rng.Intn(captchaHeight)
		x2 := rng.Intn(captchaWidth)
		y2 := rng.Intn(captchaHeight)
		drawLine(img, x1, y1, x2, y2, lineColor)
	}

	// 绘制文字（使用像素字体）
	for i, ch := range text {
		// #nosec G115 - rng.Intn(150) returns 0-149, fits in uint8
		charColor := color.RGBA{
			R: uint8(rng.Intn(150)), // #nosec G115
			G: uint8(rng.Intn(150)), // #nosec G115
			B: uint8(rng.Intn(150)), // #nosec G115
			A: 255,
		}
		x := 10 + i*25 + rng.Intn(5)
		y := 8 + rng.Intn(12)
		// #nosec G115 - ch is ASCII text character, byte() truncation is intentional
		drawChar(img, x, y, byte(ch), charColor)
	}

	// 绘制干扰点
	for i := 0; i < 80; i++ {
	// #nosec G115 - Intn(255) returns 0-254, Intn(100) returns 0-99, both fit in uint8
	dotColor := color.RGBA{
		R: uint8(rng.Intn(255)), // #nosec G115
		G: uint8(rng.Intn(255)), // #nosec G115
		B: uint8(rng.Intn(255)), // #nosec G115
		A: uint8(100 + rng.Intn(100)), // #nosec G115
	}
		img.Set(rng.Intn(captchaWidth), rng.Intn(captchaHeight), dotColor)
	}

	// 编码为 PNG
	var buf bytes.Buffer
	if err := png.Encode(&buf, img); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// drawLine 画直线（Bresenham算法）
func drawLine(img *image.RGBA, x1, y1, x2, y2 int, c color.RGBA) {
	dx := abs(x2 - x1)
	dy := abs(y2 - y1)
	sx, sy := 1, 1
	if x1 > x2 {
		sx = -1
	}
	if y1 > y2 {
		sy = -1
	}
	err := dx - dy
	for {
		img.Set(x1, y1, c)
		if x1 == x2 && y1 == y2 {
			break
		}
		e2 := 2 * err
		if e2 > -dy {
			err -= dy
			x1 += sx
		}
		if e2 < dx {
			err += dx
			y1 += sy
		}
	}
}

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

// pixelFont 5x7 像素字体位图（ASCII 32-127）
// 每个字符用5个uint8表示（5列），每个uint8的低7位是每行是否亮起
var pixelFont = map[byte][5]uint8{
	'0': {0x3E, 0x51, 0x49, 0x45, 0x3E},
	'1': {0x00, 0x42, 0x7F, 0x40, 0x00},
	'2': {0x42, 0x61, 0x51, 0x49, 0x46},
	'3': {0x21, 0x41, 0x45, 0x4B, 0x31},
	'4': {0x18, 0x14, 0x12, 0x7F, 0x10},
	'5': {0x27, 0x45, 0x45, 0x45, 0x39},
	'6': {0x3C, 0x4A, 0x49, 0x49, 0x30},
	'7': {0x01, 0x71, 0x09, 0x05, 0x03},
	'8': {0x36, 0x49, 0x49, 0x49, 0x36},
	'9': {0x06, 0x49, 0x49, 0x29, 0x1E},
	'A': {0x7E, 0x11, 0x11, 0x11, 0x7E},
	'B': {0x7F, 0x49, 0x49, 0x49, 0x36},
	'C': {0x3E, 0x41, 0x41, 0x41, 0x22},
	'D': {0x7F, 0x41, 0x41, 0x22, 0x1C},
	'E': {0x7F, 0x49, 0x49, 0x49, 0x41},
	'F': {0x7F, 0x09, 0x09, 0x09, 0x01},
	'G': {0x3E, 0x41, 0x49, 0x49, 0x7A},
	'H': {0x7F, 0x08, 0x08, 0x08, 0x7F},
	'J': {0x20, 0x40, 0x41, 0x3F, 0x01},
	'K': {0x7F, 0x08, 0x14, 0x22, 0x41},
	'L': {0x7F, 0x40, 0x40, 0x40, 0x40},
	'M': {0x7F, 0x02, 0x0C, 0x02, 0x7F},
	'N': {0x7F, 0x04, 0x08, 0x10, 0x7F},
	'P': {0x7F, 0x09, 0x09, 0x09, 0x06},
	'Q': {0x3E, 0x41, 0x51, 0x21, 0x5E},
	'R': {0x7F, 0x09, 0x19, 0x29, 0x46},
	'S': {0x46, 0x49, 0x49, 0x49, 0x31},
	'T': {0x01, 0x01, 0x7F, 0x01, 0x01},
	'U': {0x3F, 0x40, 0x40, 0x40, 0x3F},
	'V': {0x1F, 0x20, 0x40, 0x20, 0x1F},
	'W': {0x3F, 0x40, 0x38, 0x40, 0x3F},
	'X': {0x63, 0x14, 0x08, 0x14, 0x63},
	'Y': {0x07, 0x08, 0x70, 0x08, 0x07},
	'Z': {0x61, 0x51, 0x49, 0x45, 0x43},
	'a': {0x20, 0x54, 0x54, 0x54, 0x78},
	'b': {0x7F, 0x48, 0x44, 0x44, 0x38},
	'c': {0x38, 0x44, 0x44, 0x44, 0x20},
	'd': {0x38, 0x44, 0x44, 0x48, 0x7F},
	'e': {0x38, 0x54, 0x54, 0x54, 0x18},
	'f': {0x08, 0x7E, 0x09, 0x01, 0x02},
	'g': {0x0C, 0x52, 0x52, 0x52, 0x3E},
	'h': {0x7F, 0x08, 0x04, 0x04, 0x78},
	'j': {0x20, 0x40, 0x44, 0x3D, 0x00},
	'k': {0x7F, 0x10, 0x28, 0x44, 0x00},
	'm': {0x7C, 0x04, 0x18, 0x04, 0x78},
	'n': {0x7C, 0x08, 0x04, 0x04, 0x78},
	'p': {0x7C, 0x14, 0x14, 0x14, 0x08},
	'q': {0x08, 0x14, 0x14, 0x18, 0x7C},
	'r': {0x7C, 0x08, 0x04, 0x04, 0x08},
	's': {0x48, 0x54, 0x54, 0x54, 0x20},
	't': {0x04, 0x3F, 0x44, 0x40, 0x20},
	'u': {0x3C, 0x40, 0x40, 0x20, 0x7C},
	'v': {0x1C, 0x20, 0x40, 0x20, 0x1C},
	'w': {0x3C, 0x40, 0x30, 0x40, 0x3C},
	'x': {0x44, 0x28, 0x10, 0x28, 0x44},
	'y': {0x0C, 0x50, 0x50, 0x50, 0x3C},
	'z': {0x44, 0x64, 0x54, 0x4C, 0x44},
}

// drawChar 在图像上绘制单个字符
func drawChar(img *image.RGBA, x, y int, ch byte, c color.RGBA) {
	glyph, ok := pixelFont[ch]
	if !ok {
		// 未知字符画个方块
		for dy := 0; dy < 7; dy++ {
			for dx := 0; dx < 5; dx++ {
				img.Set(x+dx*2, y+dy*2, c)
			}
		}
		return
	}

	for col, colData := range glyph {
		for row := 0; row < 7; row++ {
			if colData&(1<<uint(row)) != 0 {
				// 放大2倍绘制
				img.Set(x+col*2, y+row*2, c)
				img.Set(x+col*2+1, y+row*2, c)
				img.Set(x+col*2, y+row*2+1, c)
				img.Set(x+col*2+1, y+row*2+1, c)
			}
		}
	}
}
