package redeem

import (
	"fmt"
	"math/rand"
	"regexp"
	"strings"

	"go-helper/internal/chatgpt"
	"go-helper/internal/database"
	"go-helper/internal/model"
)

var (
	emailRegex = regexp.MustCompile(`^[^\s@]+@[^\s@]+\.[^\s@]+$`)
	codeRegex  = regexp.MustCompile(`^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$`)
	codeChars  = []byte("ABCDEFGHJKLMNPQRSTUVWXYZ23456789") // exclude confusable chars
)

// RedeemResult contains information about a successful redemption.
type RedeemResult struct {
	AccountEmail string
	InviteOK     bool
	Message      string
}

// Redeem validates the code, finds an available account, and sends an invite.
func Redeem(db *database.DB, client *chatgpt.Client, code, email string, capacity int) (*RedeemResult, error) {
	email = strings.TrimSpace(strings.ToLower(email))
	code = strings.TrimSpace(strings.ToUpper(code))

	if email == "" {
		return nil, fmt.Errorf("请输入邮箱地址")
	}
	if !emailRegex.MatchString(email) {
		return nil, fmt.Errorf("邮箱格式不正确")
	}
	if code == "" {
		return nil, fmt.Errorf("请输入兑换码")
	}
	if !codeRegex.MatchString(code) {
		return nil, fmt.Errorf("兑换码格式不正确（格式：XXXX-XXXX-XXXX）")
	}

	// 1. Look up the code.
	rc, err := db.GetCodeByCode(code)
	if err != nil {
		return nil, fmt.Errorf("兑换码不存在或已失效")
	}
	if rc.IsRedeemed {
		return nil, fmt.Errorf("该兑换码已被使用")
	}

	// 2. Find a usable account.
	var account *model.GptAccount

	if rc.AccountEmail != "" {
		// Code is bound to a specific account.
		accounts, err := db.GetOpenAccounts(capacity + 100) // get all open
		if err != nil {
			return nil, fmt.Errorf("查找账号失败: %v", err)
		}
		for i := range accounts {
			if strings.EqualFold(accounts[i].Email, rc.AccountEmail) {
				if accounts[i].UserCount+accounts[i].InviteCount < capacity {
					account = &accounts[i]
				}
				break
			}
		}
		if account == nil {
			return nil, fmt.Errorf("该兑换码绑定的账号不可用或已满")
		}
	} else {
		// Find any open account with capacity.
		accounts, err := db.GetOpenAccounts(capacity)
		if err != nil || len(accounts) == 0 {
			return nil, fmt.Errorf("暂无可用账号，请稍后再试")
		}
		account = &accounts[0]
	}

	// 3. Send invite.
	inviteErr := client.InviteUser(email, account)

	// 4. Mark code as redeemed regardless of invite outcome.
	if err := db.RedeemCode(rc.ID, email); err != nil {
		return nil, fmt.Errorf("更新兑换码状态失败: %v", err)
	}

	// 5. Sync counts.
	syncCounts(db, client, account)

	result := &RedeemResult{AccountEmail: account.Email}
	if inviteErr != nil {
		result.InviteOK = false
		result.Message = fmt.Sprintf("兑换成功，但邀请发送失败: %v\n请联系管理员手动添加", inviteErr)
	} else {
		result.InviteOK = true
		result.Message = "兑换成功！邀请邮件已发送到您的邮箱，请查收。"
	}
	return result, nil
}

// GenerateCode creates a random code in XXXX-XXXX-XXXX format.
func GenerateCode() string {
	parts := make([]byte, 12)
	for i := range parts {
		parts[i] = codeChars[rand.Intn(len(codeChars))]
	}
	return fmt.Sprintf("%s-%s-%s", string(parts[0:4]), string(parts[4:8]), string(parts[8:12]))
}

// GenerateCodes creates n unique codes.
func GenerateCodes(n int) []string {
	seen := make(map[string]bool)
	var codes []string
	for len(codes) < n {
		c := GenerateCode()
		if !seen[c] {
			seen[c] = true
			codes = append(codes, c)
		}
	}
	return codes
}

// syncCounts updates user_count and invite_count from the ChatGPT API.
func syncCounts(db *database.DB, client *chatgpt.Client, account *model.GptAccount) {
	userTotal, _, err := client.GetUsers(account)
	if err != nil {
		return
	}
	inviteTotal, _, err := client.GetInvites(account)
	if err != nil {
		return
	}
	_ = db.UpdateAccountCounts(account.ID, userTotal, inviteTotal)
}
