package payment

import (
	"context"
	"errors"
	"log"
	"os"
	"strings"
	"sync"
)

// Config 微信支付渠道配置。来源两级：DB 设置（admin 控制面，热生效）优先，env 兜底
// ——与 TokensPerCredit 的「DB 优先 → env → 默认」同一约定。
// APIv3Key 在库里是 AES-GCM 密文（与模型 API Key 同一把 SUNDYNIX_SECRET_KEY），
// 这里拿到的是解密后的明文；PrivateKeyPath 只是服务器磁盘路径，私钥文件本身不进库。
type Config struct {
	MchID          string `json:"mchid"`
	CertSerial     string `json:"cert_serial"`
	PrivateKeyPath string `json:"private_key_path"`
	APIv3Key       string `json:"apiv3_key"`
	AppID          string `json:"appid"`
	NotifyURL      string `json:"notify_url"`
	// 微信支付公钥验签体系（2024 起新注册商户只有这个；本项目商户 2025-09 开户，钉死此模式，
	// 老商户的平台证书模式不做）。
	PublicKeyPath string `json:"public_key_path"` // 微信支付公钥文件路径（服务器磁盘）
	PublicKeyID   string `json:"public_key_id"`   // 公钥 ID（PUB_KEY_ID_ 开头）
}

// Empty 完全未配置（一个字段都没填）。
func (c Config) Empty() bool {
	return c.MchID == "" && c.CertSerial == "" && c.PrivateKeyPath == "" &&
		c.APIv3Key == "" && c.AppID == "" && c.NotifyURL == "" &&
		c.PublicKeyPath == "" && c.PublicKeyID == ""
}

// missing 返回缺失字段名（配置不全时给 admin 一句能看懂的原因）。八项全必填：
// APIv3 密钥用于回调资源解密，公钥两项用于验签。
func (c Config) missing() []string {
	var out []string
	for _, f := range []struct{ k, v string }{
		{"mchid", c.MchID}, {"cert_serial", c.CertSerial}, {"private_key_path", c.PrivateKeyPath},
		{"apiv3_key", c.APIv3Key}, {"appid", c.AppID}, {"notify_url", c.NotifyURL},
		{"public_key_path", c.PublicKeyPath}, {"public_key_id", c.PublicKeyID},
	} {
		if strings.TrimSpace(f.v) == "" {
			out = append(out, f.k)
		}
	}
	return out
}

// ConfigFromEnv 从环境变量读配置（DB 未配置时的兜底，兼容 P5.2 的纯 env 用法）。
func ConfigFromEnv() Config {
	return Config{
		MchID:          os.Getenv("WECHAT_MCHID"),
		CertSerial:     os.Getenv("WECHAT_MCH_CERT_SERIAL"),
		PrivateKeyPath: os.Getenv("WECHAT_MCH_PRIVATE_KEY"),
		APIv3Key:       os.Getenv("WECHAT_APIV3_KEY"),
		AppID:          os.Getenv("WECHAT_APPID"),
		NotifyURL:      os.Getenv("WECHAT_NOTIFY_URL"),
		PublicKeyPath:  os.Getenv("WECHAT_PUBLIC_KEY"),
		PublicKeyID:    os.Getenv("WECHAT_PUBLIC_KEY_ID"),
	}
}

// Manager 持有当前微信渠道实例，支持 admin 改配置后热重载（学 prompt 控制面：改完即生效，
// 不重启 gateway）。所有业务路径经 Current() 取用——nil 即渠道隐藏。
type Manager struct {
	mu     sync.RWMutex
	w      *Wechat
	reason string // 未启用原因（给 admin 状态面看）
}

func NewManager() *Manager {
	return &Manager{reason: "未配置"}
}

// Reload 按给定配置重建渠道实例。失败只降级为隐藏并记录原因，绝不 panic/拖垮服务。
func (m *Manager) Reload(ctx context.Context, c Config) error {
	w, reason, err := build(ctx, c)
	m.mu.Lock()
	m.w = w
	m.reason = reason
	m.mu.Unlock()
	if w != nil {
		log.Printf("[payment] 微信支付 Native 渠道已启用 (mchid=%s)", c.MchID)
	} else if !c.Empty() {
		log.Printf("[payment] 微信支付渠道未启用: %s", reason)
	}
	return err
}

// Current 当前渠道实例；nil = 未配置/配置失败（渠道隐藏）。
func (m *Manager) Current() *Wechat {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.w
}

// Status 给 admin 状态面：是否启用 + 未启用原因。
func (m *Manager) Status() (bool, string) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.w != nil, m.reason
}

// build 装配渠道实例；返回 (实例, 未启用原因, 错误)。配置为空不算错。
func build(ctx context.Context, c Config) (*Wechat, string, error) {
	if c.Empty() {
		return nil, "未配置", nil
	}
	if miss := c.missing(); len(miss) > 0 {
		reason := "配置不全，缺: " + strings.Join(miss, ", ")
		return nil, reason, errors.New(reason)
	}
	w, err := New(ctx, c)
	if err != nil {
		return nil, "初始化失败: " + err.Error(), err
	}
	return w, "", nil
}
