package sysconfig

import (
	"bindbox-game/configs"
	"bindbox-game/internal/pkg/cryptoaes"
	"bindbox-game/internal/pkg/logger"
	"bindbox-game/internal/repository/mysql"
	"context"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"go.uber.org/zap"
	"gorm.io/gorm"
)

// 敏感配置 Key 后缀列表，这些 Key 的值需要加密存储
var sensitiveKeySuffixes = []string{
	"secret_key",
	"secret_id",
	"app_secret",
	"access_key_secret",
	"api_v3_key",
	"private_key",
	"public_key",
}

// 配置 Key 常量
const (
	// COS 配置
	KeyCOSBucket    = "cos.bucket"
	KeyCOSRegion    = "cos.region"
	KeyCOSSecretID  = "cos.secret_id"
	KeyCOSSecretKey = "cos.secret_key"
	KeyCOSBaseURL   = "cos.base_url"

	// 微信小程序配置
	KeyWechatAppID                   = "wechat.app_id"
	KeyWechatAppSecret               = "wechat.app_secret"
	KeyWechatLotteryResultTemplateID = "wechat.lottery_result_template_id"

	// 微信支付配置
	KeyWechatPayMchID       = "wechatpay.mchid"
	KeyWechatPaySerialNo    = "wechatpay.serial_no"
	KeyWechatPayPrivateKey  = "wechatpay.private_key"
	KeyWechatPayApiV3Key    = "wechatpay.api_v3_key"
	KeyWechatPayNotifyURL   = "wechatpay.notify_url"
	KeyWechatPayPublicKeyID = "wechatpay.public_key_id"
	KeyWechatPayPublicKey   = "wechatpay.public_key"

	// 阿里云短信配置
	KeyAliyunSMSAccessKeyID     = "aliyun_sms.access_key_id"
	KeyAliyunSMSAccessKeySecret = "aliyun_sms.access_key_secret"
	KeyAliyunSMSSignName        = "aliyun_sms.sign_name"
	KeyAliyunSMSTemplateCode    = "aliyun_sms.template_code"

	// 抖音小程序配置
	KeyDouyinAppID     = "douyin.app_id"
	KeyDouyinAppSecret = "douyin.app_secret"
	KeyDouyinNotifyURL = "douyin.notify_url"
	KeyDouyinPayAppID  = "douyin.pay_app_id"
	KeyDouyinPaySecret = "douyin.pay_secret"
	KeyDouyinPaySalt   = "douyin.pay_salt"
)

// SystemConfig local model for raw GORM access
type SystemConfig struct {
	ID          int64  `gorm:"primaryKey"`
	ConfigKey   string `gorm:"uniqueIndex"`
	ConfigValue string
	Remark      string
	DeletedAt   gorm.DeletedAt
}

func (SystemConfig) TableName() string {
	return "system_configs"
}

// COSConfig COS 配置结构
type COSConfig struct {
	Bucket    string
	Region    string
	SecretID  string
	SecretKey string
	BaseURL   string
}

// WechatConfig 微信小程序配置结构
type WechatConfig struct {
	AppID                   string
	AppSecret               string
	LotteryResultTemplateID string
}

// WechatPayConfig 微信支付配置结构
type WechatPayConfig struct {
	MchID       string
	SerialNo    string
	PrivateKey  string // Base64 编码的私钥内容
	ApiV3Key    string
	NotifyURL   string
	PublicKeyID string
	PublicKey   string // Base64 编码的公钥内容
}

// AliyunSMSConfig 阿里云短信配置结构
type AliyunSMSConfig struct {
	AccessKeyID     string
	AccessKeySecret string
	SignName        string
	TemplateCode    string
}

// DouyinConfig 抖音小程序配置结构
type DouyinConfig struct {
	AppID     string
	AppSecret string
	NotifyURL string
	PayAppID  string // 支付应用ID (担保支付)
	PaySecret string // 支付密钥
	PaySalt   string // 支付盐
}

// DynamicConfig 动态配置服务
type DynamicConfig struct {
	cache    sync.Map // key -> string value
	db       *gorm.DB
	logger   logger.CustomLogger
	encKey   string        // 加密密钥 (32字节用于AES-256)
	ttl      time.Duration // 缓存过期时间
	loadedAt time.Time
	mu       sync.RWMutex
}

// NewDynamicConfig 创建动态配置服务实例
func NewDynamicConfig(l logger.CustomLogger, db mysql.Repo) *DynamicConfig {
	// 使用 CommitMasterKey 的前32字节作为 AES-256 密钥
	masterKey := configs.Get().Random.CommitMasterKey
	encKey := masterKey
	if len(encKey) > 32 {
		encKey = encKey[:32]
	} else if len(encKey) < 32 {
		// 不足32字节则填充
		encKey = encKey + strings.Repeat("0", 32-len(encKey))
	}

	return &DynamicConfig{
		db:     db.GetDbR(),
		logger: l,
		encKey: encKey,
		ttl:    5 * time.Minute,
	}
}

// IsSensitiveKey 判断是否为敏感配置 Key
func IsSensitiveKey(key string) bool {
	for _, suffix := range sensitiveKeySuffixes {
		if strings.HasSuffix(key, suffix) {
			return true
		}
	}
	return false
}

// encryptValue 加密配置值
func (d *DynamicConfig) encryptValue(value string) (string, error) {
	return cryptoaes.Encrypt(d.encKey, value)
}

// decryptValue 解密配置值
func (d *DynamicConfig) decryptValue(value string) (string, error) {
	return cryptoaes.Decrypt(d.encKey, value)
}

// LoadAll 启动时预加载所有配置到缓存
func (d *DynamicConfig) LoadAll(ctx context.Context) error {
	var items []SystemConfig
	// Raw GORM query
	if err := d.db.WithContext(ctx).Find(&items).Error; err != nil {
		return err
	}

	for _, item := range items {
		value := item.ConfigValue
		// 敏感配置需要解密
		if IsSensitiveKey(item.ConfigKey) && value != "" {
			if decrypted, err := d.decryptValue(value); err == nil {
				value = decrypted
			} else {
				// 解密失败，尝试使用原始值
			}
		}
		d.cache.Store(item.ConfigKey, value)
		// DEBUG: Print keys being cached
		if item.ConfigKey == "wechat.app_id" || item.ConfigKey == "wechat.app_secret" {
			fmt.Printf("DEBUG LoadAll: Caching key=%s value=%s\n", item.ConfigKey, value)
		}
	}

	d.mu.Lock()
	d.loadedAt = time.Now()
	d.mu.Unlock()

	d.logger.Info("动态配置加载完成", zap.Int("count", len(items)))
	return nil
}

// Refresh 刷新缓存
func (d *DynamicConfig) Refresh(ctx context.Context) error {
	return d.LoadAll(ctx)
}

// NeedsRefresh 检查是否需要刷新缓存
func (d *DynamicConfig) NeedsRefresh() bool {
	d.mu.RLock()
	defer d.mu.RUnlock()
	return time.Since(d.loadedAt) > d.ttl
}

// Get 获取配置值（带缓存）
// Get 获取配置值（带缓存）
func (d *DynamicConfig) Get(ctx context.Context, key string) string {
	// 1. 从缓存读取
	if v, ok := d.cache.Load(key); ok {
		return v.(string)
	}

	// 2. 从数据库读取 (Raw GORM)
	var cfg SystemConfig
	// Use WithContext and Where
	err := d.db.WithContext(ctx).Where("config_key = ?", key).First(&cfg).Error
	if err == nil {
		value := cfg.ConfigValue
		// 敏感配置需要解密
		if IsSensitiveKey(key) && value != "" {
			if decrypted, err := d.decryptValue(value); err == nil {
				value = decrypted
			} else {
				d.logger.Warn("Failed to decrypt sensitive key", zap.String("key", key), zap.Error(err))
			}
		}
		d.cache.Store(key, value)
		return value
	} else {
		// Only log warning if not found, don't return error (return empty string)
		// d.logger.Warn("Config NOT found in DB", zap.String("key", key), zap.Error(err))
	}

	// 3. 返回空字符串（调用方需要处理 fallback）
	return ""
}

// GetWithFallback 获取配置值，如果不存在则返回 fallback 值
func (d *DynamicConfig) GetWithFallback(ctx context.Context, key, fallback string) string {
	if v := d.Get(ctx, key); v != "" {
		return v
	}
	return fallback
}

// UpdateCache 手动更新缓存（用于 Admin API 调用后同步）
func (d *DynamicConfig) UpdateCache(key, value string) {
	// 敏感配置需要解密后再存入缓存？
	// 这里假设 Admin API 传入的是明文 value，数据库存的是密文。
	// 但是 LoadAll 里是从 DB 读出(可能是密文)解密后存缓存。
	// cache 里存的是 明文。
	// Admin API UpsertByKey 传入的是明文。
	d.cache.Store(key, value)
}

// DeleteCache 删除缓存
func (d *DynamicConfig) DeleteCache(key string) {
	d.cache.Delete(key)
}

// Set 设置配置值（自动处理加密）
func (d *DynamicConfig) Set(ctx context.Context, key, value, remark string) error {
	storeValue := value
	// 敏感配置需要加密
	if IsSensitiveKey(key) && value != "" {
		encrypted, err := d.encryptValue(value)
		if err != nil {
			return err
		}
		storeValue = encrypted
	}

	// Upsert logic
	var existing SystemConfig
	err := d.db.WithContext(ctx).Where("config_key = ?", key).First(&existing).Error
	if err == nil {
		// Update
		existing.ConfigValue = storeValue
		existing.Remark = remark
		return d.db.WithContext(ctx).Save(&existing).Error
	}

	// Create
	if errors.Is(err, gorm.ErrRecordNotFound) {
		newItem := SystemConfig{
			ConfigKey:   key,
			ConfigValue: storeValue,
			Remark:      remark,
		}
		return d.db.WithContext(ctx).Create(&newItem).Error
	}

	return err
}

// GetCOS 获取 COS 配置
func (d *DynamicConfig) GetCOS(ctx context.Context) COSConfig {
	staticCfg := configs.Get().COS
	return COSConfig{
		Bucket:    d.GetWithFallback(ctx, KeyCOSBucket, staticCfg.Bucket),
		Region:    d.GetWithFallback(ctx, KeyCOSRegion, staticCfg.Region),
		SecretID:  d.GetWithFallback(ctx, KeyCOSSecretID, staticCfg.SecretID),
		SecretKey: d.GetWithFallback(ctx, KeyCOSSecretKey, staticCfg.SecretKey),
		BaseURL:   d.GetWithFallback(ctx, KeyCOSBaseURL, staticCfg.BaseURL),
	}
}

// GetWechat 获取微信小程序配置
func (d *DynamicConfig) GetWechat(ctx context.Context) WechatConfig {
	staticCfg := configs.Get().Wechat
	return WechatConfig{
		AppID:                   d.GetWithFallback(ctx, KeyWechatAppID, staticCfg.AppID),
		AppSecret:               d.GetWithFallback(ctx, KeyWechatAppSecret, staticCfg.AppSecret),
		LotteryResultTemplateID: d.GetWithFallback(ctx, KeyWechatLotteryResultTemplateID, staticCfg.LotteryResultTemplateID),
	}
}

// GetWechatPay 获取微信支付配置
func (d *DynamicConfig) GetWechatPay(ctx context.Context) WechatPayConfig {
	staticCfg := configs.Get().WechatPay
	return WechatPayConfig{
		MchID:       d.GetWithFallback(ctx, KeyWechatPayMchID, staticCfg.MchID),
		SerialNo:    d.GetWithFallback(ctx, KeyWechatPaySerialNo, staticCfg.SerialNo),
		PrivateKey:  d.Get(ctx, KeyWechatPayPrivateKey), // Key content only, no fallback to file path
		ApiV3Key:    d.GetWithFallback(ctx, KeyWechatPayApiV3Key, staticCfg.ApiV3Key),
		NotifyURL:   d.GetWithFallback(ctx, KeyWechatPayNotifyURL, staticCfg.NotifyURL),
		PublicKeyID: d.GetWithFallback(ctx, KeyWechatPayPublicKeyID, staticCfg.PublicKeyID),
		PublicKey:   d.Get(ctx, KeyWechatPayPublicKey), // Key content only
	}
}

// GetAliyunSMS 获取阿里云短信配置
func (d *DynamicConfig) GetAliyunSMS(ctx context.Context) AliyunSMSConfig {
	staticCfg := configs.Get().AliyunSMS
	return AliyunSMSConfig{
		AccessKeyID:     d.GetWithFallback(ctx, KeyAliyunSMSAccessKeyID, staticCfg.AccessKeyID),
		AccessKeySecret: d.GetWithFallback(ctx, KeyAliyunSMSAccessKeySecret, staticCfg.AccessKeySecret),
		SignName:        d.GetWithFallback(ctx, KeyAliyunSMSSignName, staticCfg.SignName),
		TemplateCode:    d.GetWithFallback(ctx, KeyAliyunSMSTemplateCode, staticCfg.TemplateCode),
	}
}

// GetDouyin 获取抖音小程序配置
func (d *DynamicConfig) GetDouyin(ctx context.Context) DouyinConfig {
	return DouyinConfig{
		AppID:     d.Get(ctx, KeyDouyinAppID),
		AppSecret: d.Get(ctx, KeyDouyinAppSecret),
		NotifyURL: d.Get(ctx, KeyDouyinNotifyURL),
		PayAppID:  d.Get(ctx, KeyDouyinPayAppID),
		PaySecret: d.Get(ctx, KeyDouyinPaySecret),
	}
}

// innerSubstr 截取字符串，避免越界
func innerSubstr(s string, start, length int) string {
	if start >= len(s) {
		return ""
	}
	end := start + length
	if end > len(s) {
		end = len(s)
	}
	return s[start:end]
}
