package service

import (
	"context"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"math"
	"math/big"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	"juhe-factory/api/internal/security"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type AdminData struct {
	DB                   *gorm.DB
	Passwords            security.PasswordHasher
	Encryptor            *security.Encryptor
	HTTPClient           *http.Client
	EncryptionKeyVersion string
	balanceSyncing       atomic.Bool
}

type Page struct {
	Items    []map[string]any `json:"items"`
	Total    int64            `json:"total"`
	Page     int              `json:"page"`
	PageSize int              `json:"page_size"`
}

type ResourceSpec struct {
	Table         string
	Select        string
	SearchColumns []string
	Fields        map[string]string
	Filters       map[string]string
	Order         string
	SoftDelete    bool
}

var resourceSpecs = map[string]ResourceSpec{
	"styles":   {Table: "project_styles", Select: "id,name,image_key,image_url,image_mime,image_size,image_width,image_height,sort_order", Fields: map[string]string{"name": "name", "image_key": "image_key", "image_url": "image_url", "image_mime": "image_mime", "image_size": "image_size", "image_width": "image_width", "image_height": "image_height"}, Order: "sort_order,name", SoftDelete: true},
	"channels": {Table: "channels", Select: "c.id,c.name,c.channel_type,c.base_url,c.api_key_ciphertext,c.api_key_last4,c.warning_threshold,c.channel_points_per_cny,c.max_concurrency,c.max_user_concurrency,c.enabled,c.created_at,c.updated_at,b.balance,b.currency,b.synced_at", SearchColumns: []string{"c.name", "c.base_url"}, Fields: map[string]string{"name": "name", "channel_type": "channel_type", "base_url": "base_url", "warning_threshold": "warning_threshold", "channel_points_per_cny": "channel_points_per_cny", "max_concurrency": "max_concurrency", "max_user_concurrency": "max_user_concurrency", "enabled": "enabled"}, Filters: map[string]string{"channel_type": "channel_type", "enabled": "enabled"}, Order: "c.created_at DESC", SoftDelete: true},
	"models":   {Table: "models", Select: "id", Fields: map[string]string{}, Order: "created_at DESC", SoftDelete: true},
	"prompts":  {Table: "prompts", Select: "id", Fields: map[string]string{}, Order: "updated_at DESC", SoftDelete: true},
}

func pageArgs(pageRaw, sizeRaw string) (int, int) {
	page, _ := strconv.Atoi(pageRaw)
	if page < 1 {
		page = 1
	}
	size, _ := strconv.Atoi(sizeRaw)
	if size < 1 {
		size = 20
	}
	if size > 100 {
		size = 100
	}
	return page, size
}

func (s *AdminData) ListResource(ctx context.Context, resource, keyword string, filters map[string]string, pageRaw, sizeRaw string) (Page, error) {
	spec, ok := resourceSpecs[resource]
	if !ok {
		return Page{}, errors.New("不支持的资源类型")
	}
	page, size := pageArgs(pageRaw, sizeRaw)
	if resource == "channels" {
		s.triggerChannelBalanceSync()
	}
	query := s.DB.Table(spec.Table)
	if resource == "channels" {
		query = s.DB.Table("channels c").Joins("LEFT JOIN LATERAL (SELECT balance,currency,synced_at FROM channel_balance_snapshots WHERE channel_id=c.id ORDER BY synced_at DESC LIMIT 1) b ON true")
	}
	if spec.SoftDelete {
		prefix := strings.Split(spec.Table, " ")[0]
		if resource == "channels" {
			prefix = "c"
		}
		query = query.Where(prefix + ".deleted_at IS NULL")
	}
	if keyword = strings.TrimSpace(keyword); keyword != "" && len(spec.SearchColumns) > 0 {
		parts := make([]string, len(spec.SearchColumns))
		args := make([]any, len(spec.SearchColumns))
		for i, column := range spec.SearchColumns {
			parts[i] = column + "::text ILIKE ?"
			args[i] = "%" + keyword + "%"
		}
		query = query.Where("("+strings.Join(parts, " OR ")+")", args...)
	}
	for key, value := range filters {
		if column, exists := spec.Filters[key]; exists && value != "" {
			query = query.Where(column+" = ?", value)
		}
	}
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return Page{}, err
	}
	items := make([]map[string]any, 0)
	if err := query.Select(spec.Select).Order(spec.Order).Offset((page - 1) * size).Limit(size).Find(&items).Error; err != nil {
		return Page{}, err
	}
	if resource == "channels" {
		s.maskChannelAPIKeys(items)
	}
	return Page{Items: items, Total: total, Page: page, PageSize: size}, nil
}

func (s *AdminData) maskChannelAPIKeys(items []map[string]any) {
	for _, item := range items {
		ciphertext := strings.TrimSpace(fmt.Sprint(item["api_key_ciphertext"]))
		last4 := strings.TrimSpace(fmt.Sprint(item["api_key_last4"]))
		delete(item, "api_key_ciphertext")
		delete(item, "api_key_last4")
		item["api_key_masked"] = ""
		if ciphertext != "" && ciphertext != "<nil>" && s.Encryptor != nil {
			if plain, err := s.Encryptor.Decrypt(ciphertext); err == nil {
				item["api_key_masked"] = maskAPIKey(plain)
				continue
			}
		}
		if last4 != "" && last4 != "<nil>" {
			item["api_key_masked"] = "********" + last4
		}
	}
}

func maskAPIKey(value string) string {
	characters := []rune(value)
	if len(characters) <= 10 {
		return strings.Repeat("*", max(len(characters), 8))
	}
	return string(characters[:5]) + "********" + string(characters[len(characters)-5:])
}

func (s *AdminData) SaveResource(resource, id string, values map[string]any) (string, error) {
	spec, ok := resourceSpecs[resource]
	if !ok {
		return "", errors.New("不支持的资源类型")
	}
	data := map[string]any{}
	for input, column := range spec.Fields {
		if value, exists := values[input]; exists {
			data[column] = value
		}
	}
	if resource == "channels" {
		if err := s.prepareChannel(values, data); err != nil {
			return "", err
		}
	}
	if resource == "channels" && id == "" {
		apiKey, _ := values["api_key"].(string)
		if strings.TrimSpace(apiKey) == "" {
			return "", errors.New("API Key 不能为空")
		}
		data["channel_type"] = "relay"
	}
	if id == "" {
		newID := uuid.NewString()
		data["id"] = newID
		if resource == "styles" {
			var next int
			if err := s.DB.Table("project_styles").Select("coalesce(max(sort_order),-1)+1").Scan(&next).Error; err != nil {
				return "", err
			}
			data["sort_order"] = next
		}
		if err := s.DB.Table(spec.Table).Create(data).Error; err != nil {
			return "", err
		}
		return newID, nil
	}
	if len(data) == 0 {
		return id, nil
	}
	result := s.DB.Table(spec.Table).Where("id = ? AND deleted_at IS NULL", id).Updates(data)
	if result.Error != nil {
		return "", result.Error
	}
	if result.RowsAffected == 0 {
		return "", gorm.ErrRecordNotFound
	}
	return id, nil
}

func (s *AdminData) ReorderStyles(ids []string) error {
	if len(ids) == 0 {
		return errors.New("风格排序不能为空")
	}
	seen := make(map[string]struct{}, len(ids))
	for _, id := range ids {
		if _, err := uuid.Parse(id); err != nil {
			return errors.New("风格 ID 无效")
		}
		if _, exists := seen[id]; exists {
			return errors.New("风格排序包含重复项")
		}
		seen[id] = struct{}{}
	}
	return s.DB.Transaction(func(tx *gorm.DB) error {
		var count int64
		if err := tx.Table("project_styles").Where("deleted_at IS NULL").Count(&count).Error; err != nil {
			return err
		}
		if int64(len(ids)) != count {
			return errors.New("风格列表已变化，请刷新后重试")
		}
		for position, id := range ids {
			result := tx.Table("project_styles").Where("id=? AND deleted_at IS NULL", id).Update("sort_order", position)
			if result.Error != nil {
				return result.Error
			}
			if result.RowsAffected == 0 {
				return errors.New("风格列表已变化，请刷新后重试")
			}
		}
		return nil
	})
}

func (s *AdminData) prepareChannel(values, data map[string]any) error {
	rateText := strings.TrimSpace(fmt.Sprint(values["channel_points_per_cny"]))
	rate, err := strconv.ParseFloat(rateText, 64)
	if err != nil || math.IsNaN(rate) || math.IsInf(rate, 0) || rate <= 0 || rate > 1000000000000 {
		return errors.New("渠道汇率必须大于 0")
	}
	data["channel_points_per_cny"] = rateText
	maxConcurrency, err := positiveInt(values["max_concurrency"], 500)
	if err != nil || maxConcurrency > 5000 {
		return errors.New("最大并发数必须为 1 至 5000")
	}
	maxUserConcurrency, err := positiveInt(values["max_user_concurrency"], 10)
	if err != nil || maxUserConcurrency > 500 || maxUserConcurrency > maxConcurrency {
		return errors.New("单用户最高并发数必须为 1 至 500，且不能超过最大并发数")
	}
	data["max_concurrency"] = maxConcurrency
	data["max_user_concurrency"] = maxUserConcurrency
	plain, _ := values["api_key"].(string)
	plain = strings.TrimSpace(plain)
	if plain == "" {
		return nil
	}
	if s.Encryptor == nil {
		return errors.New("敏感配置加密未配置")
	}
	ciphertext, err := s.Encryptor.Encrypt(plain)
	if err != nil {
		return err
	}
	last4 := plain
	if len(last4) > 4 {
		last4 = last4[len(last4)-4:]
	}
	data["api_key_ciphertext"] = ciphertext
	data["api_key_last4"] = last4
	data["encryption_key_version"] = s.EncryptionKeyVersion
	return nil
}

func positiveInt(value any, fallback int) (int, error) {
	if value == nil || fmt.Sprint(value) == "" {
		return fallback, nil
	}
	parsed, err := strconv.Atoi(fmt.Sprint(value))
	if err != nil || parsed < 1 {
		return 0, errors.New("必须为正整数")
	}
	return parsed, nil
}

func (s *AdminData) ToggleResource(resource, id string, enabled bool) error {
	spec, ok := resourceSpecs[resource]
	if !ok {
		return errors.New("不支持的资源类型")
	}
	if resource == "models" && enabled {
		var channelEnabled bool
		if err := s.DB.Raw(`SELECT c.enabled FROM models m JOIN channels c ON c.id=m.channel_id
			WHERE m.id=? AND m.deleted_at IS NULL AND c.deleted_at IS NULL`, id).Scan(&channelEnabled).Error; err != nil {
			return err
		}
		if !channelEnabled {
			return errors.New("所属渠道已禁用，无法启用该模型")
		}
	}
	if resource == "channels" {
		return s.DB.Transaction(func(tx *gorm.DB) error {
			result := tx.Table("channels").Where("id = ? AND deleted_at IS NULL", id).Update("enabled", enabled)
			if result.Error != nil {
				return result.Error
			}
			if result.RowsAffected == 0 {
				return gorm.ErrRecordNotFound
			}
			return tx.Table("models").Where("channel_id = ? AND deleted_at IS NULL", id).Update("enabled", enabled).Error
		})
	}
	result := s.DB.Table(spec.Table).Where("id = ? AND deleted_at IS NULL", id).Update("enabled", enabled)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}

func (s *AdminData) DeleteResource(resource, id string) error {
	spec, ok := resourceSpecs[resource]
	if !ok || !spec.SoftDelete {
		return errors.New("不支持删除该资源")
	}
	result := s.DB.Table(spec.Table).Where("id = ? AND deleted_at IS NULL", id).Update("deleted_at", time.Now())
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}

func (s *AdminData) ListUsers(keyword, enabled, pageRaw, sizeRaw string) (Page, error) {
	page, size := pageArgs(pageRaw, sizeRaw)
	query := s.DB.Table("web_users u").Where("u.deleted_at IS NULL")
	if keyword = strings.TrimSpace(keyword); keyword != "" {
		query = query.Where("u.account::text ILIKE ? OR u.username::text ILIKE ? OR u.uid = ?", "%"+keyword+"%", "%"+keyword+"%", keyword)
	}
	if enabled != "" {
		query = query.Where("u.enabled = ?", enabled)
	}
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return Page{}, err
	}
	items := make([]map[string]any, 0)
	err := query.Select(`u.id,u.account,u.username,u.uid,u.point_balance,u.daily_limit,u.enabled,u.last_online_at,u.created_at,
		coalesce((SELECT sum(-l.change_amount) FROM point_ledger l WHERE l.user_id=u.id AND l.change_amount<0 AND l.created_at>=CURRENT_DATE AND l.created_at<CURRENT_DATE+INTERVAL '1 day'),0) AS today_consumption`).
		Order("u.created_at DESC").Offset((page - 1) * size).Limit(size).Find(&items).Error
	return Page{Items: items, Total: total, Page: page, PageSize: size}, err
}

func (s *AdminData) CreateUser(account, password string, dailyLimit any) (map[string]any, error) {
	account = strings.TrimSpace(account)
	if account == "" {
		return nil, errors.New("账号不能为空")
	}
	items, err := s.createUsers([]string{account}, password, dailyLimit)
	if err != nil {
		return nil, err
	}
	return items[0], nil
}

func (s *AdminData) BatchCreateUsers(prefix, startSequence, endSequence, password string, dailyLimit any) ([]map[string]any, error) {
	accounts, err := buildBatchAccounts(prefix, startSequence, endSequence)
	if err != nil {
		return nil, err
	}
	return s.createUsers(accounts, password, dailyLimit)
}

func (s *AdminData) createUsers(accounts []string, password string, dailyLimit any) ([]map[string]any, error) {
	hash, err := s.Passwords.Hash(password)
	if err != nil {
		return nil, err
	}
	created := make([]map[string]any, 0, len(accounts))
	err = s.DB.Transaction(func(tx *gorm.DB) error {
		var existing string
		if err := tx.Table("web_users").Select("account").Where("account IN ?", accounts).Limit(1).Scan(&existing).Error; err != nil {
			return err
		}
		if existing != "" {
			return fmt.Errorf("账号 %s 已存在", existing)
		}
		for _, account := range accounts {
			user, err := createUserWithAccount(tx, account, hash, dailyLimit)
			if err != nil {
				return err
			}
			created = append(created, user)
		}
		return nil
	})
	return created, err
}

const usernameAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789"

var sequencePattern = regexp.MustCompile(`^[0-9]+$`)

func buildBatchAccounts(prefix, startSequence, endSequence string) ([]string, error) {
	prefix = strings.TrimSpace(prefix)
	startSequence = strings.TrimSpace(startSequence)
	endSequence = strings.TrimSpace(endSequence)
	if prefix == "" {
		return nil, errors.New("账号前缀不能为空")
	}
	if !sequencePattern.MatchString(startSequence) || !sequencePattern.MatchString(endSequence) {
		return nil, errors.New("起始序号和终止序号必须为数字")
	}
	start, err := strconv.Atoi(startSequence)
	if err != nil {
		return nil, errors.New("起始序号无效")
	}
	end, err := strconv.Atoi(endSequence)
	if err != nil {
		return nil, errors.New("终止序号无效")
	}
	if start > end {
		return nil, errors.New("起始序号不能大于终止序号")
	}
	count := end - start + 1
	if count > 50 {
		return nil, errors.New("每次最多批量创建 50 个用户")
	}
	width := max(len(startSequence), len(endSequence))
	accounts := make([]string, 0, count)
	for sequence := start; sequence <= end; sequence++ {
		accounts = append(accounts, fmt.Sprintf("%s-%0*d", prefix, width, sequence))
	}
	return accounts, nil
}

func createUserWithAccount(tx *gorm.DB, account, passwordHash string, dailyLimit any) (map[string]any, error) {
	for range 20 {
		username, err := randomUsername()
		if err != nil {
			return nil, err
		}
		uid, err := newUID(tx)
		if err != nil {
			return nil, err
		}
		id := uuid.NewString()
		result := tx.Exec(`INSERT INTO web_users(id,uid,username,account,password_hash,daily_limit,enabled)
			VALUES(?,?,?,?,?,?,true) ON CONFLICT DO NOTHING`, id, uid, username, account, passwordHash, dailyLimit)
		if result.Error != nil {
			return nil, result.Error
		}
		if result.RowsAffected == 1 {
			return map[string]any{"id": id, "uid": uid, "account": account, "username": username}, nil
		}
		var accountExists int64
		if err := tx.Table("web_users").Where("account = ?", account).Count(&accountExists).Error; err != nil {
			return nil, err
		}
		if accountExists > 0 {
			return nil, fmt.Errorf("账号 %s 已存在", account)
		}
	}
	return nil, errors.New("用户名生成失败，请重试")
}

func randomUsername() (string, error) {
	lengthOffset, err := rand.Int(rand.Reader, big.NewInt(8))
	if err != nil {
		return "", err
	}
	username := make([]byte, 5+lengthOffset.Int64())
	for i := range username {
		index, err := rand.Int(rand.Reader, big.NewInt(int64(len(usernameAlphabet))))
		if err != nil {
			return "", err
		}
		username[i] = usernameAlphabet[index.Int64()]
	}
	return string(username), nil
}

func newUID(tx *gorm.DB) (string, error) {
	for i := 0; i < 20; i++ {
		n, err := rand.Int(rand.Reader, big.NewInt(90000000))
		if err != nil {
			return "", err
		}
		uid := fmt.Sprintf("%08d", n.Int64()+10000000)
		var count int64
		if err := tx.Table("web_users").Where("uid=?", uid).Count(&count).Error; err != nil {
			return "", err
		}
		if count == 0 {
			return uid, nil
		}
	}
	return "", errors.New("UID 生成失败，请重试")
}

func (s *AdminData) UpdateUsers(ids []string, updates map[string]any) error {
	if len(ids) == 0 {
		return errors.New("请选择用户")
	}
	allowed := map[string]any{}
	if value, ok := updates["daily_limit"]; ok {
		allowed["daily_limit"] = value
	}
	if value, ok := updates["enabled"]; ok {
		allowed["enabled"] = value
		allowed["session_version"] = gorm.Expr("session_version + 1")
	}
	if len(allowed) == 0 {
		return errors.New("没有可更新的字段")
	}
	return s.DB.Table("web_users").Where("id IN ? AND deleted_at IS NULL", ids).Updates(allowed).Error
}

func (s *AdminData) DeleteUsers(ids []string) error {
	if len(ids) == 0 {
		return errors.New("请选择用户")
	}
	return s.DB.Table("web_users").Where("id IN ? AND deleted_at IS NULL", ids).Updates(map[string]any{"deleted_at": time.Now(), "enabled": false, "session_version": gorm.Expr("session_version + 1")}).Error
}

var placeholderPattern = regexp.MustCompile(`\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}`)

func ValidatePromptVariables(content string, variables []string) error {
	allowed := map[string]bool{}
	for _, v := range variables {
		allowed[v] = true
	}
	missing := []string{}
	for _, m := range placeholderPattern.FindAllStringSubmatch(content, -1) {
		if !allowed[m[1]] {
			missing = append(missing, m[1])
		}
	}
	sort.Strings(missing)
	if len(missing) > 0 {
		return fmt.Errorf("正文使用了未声明变量：%s", strings.Join(missing, "、"))
	}
	return nil
}

func GenerateRedemptionCode() (plain, hash, mask string, err error) {
	const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	const firstAlphabet = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	raw := make([]byte, 30)
	for i := range raw {
		chars := alphabet
		if i == 0 {
			chars = firstAlphabet
		}
		index, randomErr := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
		if randomErr != nil {
			err = randomErr
			return
		}
		raw[i] = chars[index.Int64()]
	}
	normalized := string(raw)
	groups := make([]string, 0, 5)
	for start := 0; start < len(raw); start += 6 {
		groups = append(groups, string(raw[start:start+6]))
	}
	plain = strings.Join(groups, "-")
	sum := sha256.Sum256([]byte(normalized))
	hash = hex.EncodeToString(sum[:])
	mask = normalized[:3] + "***-******-******-******-" + normalized[len(normalized)-3:]
	return
}
