// Package license 实现平台 License 授权校验与账号数管控。
//
// License 是一个 JSON 文件(路径由 LICENSE_FILE 指定),内容为声明字段 +
// HMAC-SHA256 签名(密钥由 CREDENTIAL_MASTER_KEY 派生)。未配置 LICENSE_FILE
// 时按社区版 Free(3 账号)处理;管理员可通过管理端上传 License 热更新。
package license

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"
)

// Edition 是版本枚举;功能矩阵以 features 列表为准,edition 只做展示与
// 默认能力集合。
const (
	EditionFree    = "free"
	EditionPro     = "pro"
	EditionUltra   = "ultra"
	DefaultAccountLimit = 3 // 未配置 License 时按社区版 Free
)

var ErrInvalidLicense = errors.New("license 文件无效或签名不匹配")
var ErrLicenseExpired = errors.New("license 已过期")

// Claims 是 License 的声明部分(不含签名)。
type Claims struct {
	Edition     string   `json:"edition"`
	IssuedTo    string   `json:"issued_to"`
	MaxAccounts int      `json:"max_accounts"` // 0 = 不限
	Features    []string `json:"features"`     // 额外授权特性名(预留)
	NotBefore   string   `json:"not_before"`   // RFC3339
	NotAfter    string   `json:"not_after"`    // RFC3339,空 = 永久
}

// License 是完整的 License 文件内容。
type License struct {
	Claims
	Signature string `json:"signature"` // base64(HMAC-SHA256(claimsCanonicalJSON, key))
}

// Manager 持有当前 License 状态,支持热更新。
type Manager struct {
	mu       sync.RWMutex
	filePath string
	master   []byte
	current  License
}

// NewManager 创建 License 管理器并从 filePath 加载(路径为空表示 Free 版)。
func NewManager(filePath, masterKey string) (*Manager, error) {
	m := &Manager{filePath: filePath, master: deriveKey(masterKey)}
	if strings.TrimSpace(filePath) == "" {
		return m, nil // Free 版,无需文件
	}
	if err := m.Reload(); err != nil {
		return nil, err
	}
	return m, nil
}

// deriveKey 从 master key 派生 License 签名密钥(独立用途域,不与凭据加密混用)。
func deriveKey(masterKey string) []byte {
	sum := sha256.Sum256([]byte("aigateway-license-v1\x00" + masterKey))
	return sum[:]
}

// Reload 重新读取并校验 License 文件。
func (m *Manager) Reload() error {
	if m == nil {
		return nil
	}
	raw, err := os.ReadFile(m.filePath)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return ErrInvalidLicense
		}
		return err
	}
	lic, err := Parse(raw, m.master)
	if err != nil {
		return err
	}
	m.mu.Lock()
	m.current = lic
	m.mu.Unlock()
	return nil
}

// Parse 解析并校验 License 内容(签名 + 有效期)。
func Parse(raw []byte, key []byte) (License, error) {
	var lic License
	if err := json.Unmarshal(raw, &lic); err != nil {
		return License{}, ErrInvalidLicense
	}
	claims, err := json.Marshal(lic.Claims)
	if err != nil {
		return License{}, ErrInvalidLicense
	}
	mac := hmac.New(sha256.New, key)
	_, _ = mac.Write(claims)
	expected := base64.StdEncoding.EncodeToString(mac.Sum(nil))
	if !hmac.Equal([]byte(expected), []byte(strings.TrimSpace(lic.Signature))) {
		return License{}, ErrInvalidLicense
	}
	now := time.Now()
	if lic.NotBefore != "" {
		if start, err := time.Parse(time.RFC3339, lic.NotBefore); err == nil && now.Before(start) {
			return License{}, ErrInvalidLicense
		}
	}
	if lic.NotAfter != "" {
		if end, err := time.Parse(time.RFC3339, lic.NotAfter); err == nil && now.After(end) {
			return License{}, ErrLicenseExpired
		}
	}
	edition := strings.ToLower(strings.TrimSpace(lic.Edition))
	switch edition {
	case EditionFree, EditionPro, EditionUltra, "":
	default:
		return License{}, ErrInvalidLicense
	}
	if lic.Edition == "" {
		lic.Edition = EditionFree
	}
	return lic, nil
}

// Sign 生成 License(供本地签发工具/测试使用)。
func Sign(claims Claims, masterKey string) (License, error) {
	claims.Edition = strings.ToLower(strings.TrimSpace(claims.Edition))
	claims.Features = normalize(claims.Features)
	raw, err := json.Marshal(claims)
	if err != nil {
		return License{}, err
	}
	mac := hmac.New(sha256.New, deriveKey(masterKey))
	_, _ = mac.Write(raw)
	return License{Claims: claims, Signature: base64.StdEncoding.EncodeToString(mac.Sum(nil))}, nil
}

func normalize(values []string) []string {
	seen := map[string]bool{}
	out := make([]string, 0, len(values))
	for _, v := range values {
		v = strings.ToLower(strings.TrimSpace(v))
		if v != "" && !seen[v] {
			seen[v] = true
			out = append(out, v)
		}
	}
	return out
}

// Current 返回当前 License 快照。
func (m *Manager) Current() License {
	if m == nil {
		return License{Claims: Claims{Edition: EditionFree}}
	}
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.current
}

// AccountLimit 返回账号数上限;0 表示不限。
func (m *Manager) AccountLimit() int {
	lic := m.Current()
	// 未配置 License 时 Edition 为空,同样按社区版 Free(3 账号)处理。
	if lic.Edition == "" || lic.Edition == EditionFree {
		if lic.MaxAccounts > 0 {
			return lic.MaxAccounts
		}
		return DefaultAccountLimit
	}
	return lic.MaxAccounts // pro/ultra 由 License 指定;0=不限
}

// EditionName 返回可读版本名。
func (m *Manager) EditionName() string {
	switch m.Current().Edition {
	case EditionPro:
		return "专业版 Pro"
	case EditionUltra:
		return "旗舰版 Ultra"
	default:
		return "社区版 Free"
	}
}

// FilePath 返回 License 文件路径。
func (m *Manager) FilePath() string {
	if m == nil {
		return ""
	}
	return m.filePath
}

// Load 原子替换 License 文件并热更新(管理端上传)。
func (m *Manager) Load(raw []byte) error {
	if m == nil || m.filePath == "" {
		return errors.New("未配置 LICENSE_FILE,无法保存 License")
	}
	lic, err := Parse(raw, m.master)
	if err != nil {
		return err
	}
	tmp := m.filePath + ".tmp"
	if err := os.WriteFile(tmp, raw, 0o600); err != nil {
		return err
	}
	if err := os.Rename(tmp, m.filePath); err != nil {
		return err
	}
	m.mu.Lock()
	m.current = lic
	m.mu.Unlock()
	return nil
}

// Summary 返回给管理端展示的信息。
func (m *Manager) Summary() map[string]any {
	lic := m.Current()
	expires := lic.NotAfter
	if expires == "" {
		expires = "永久"
	}
	return map[string]any{
		"edition":      lic.Edition,
		"edition_name": m.EditionName(),
		"issued_to":    lic.IssuedTo,
		"max_accounts": m.AccountLimit(),
		"features":     lic.Features,
		"expires":      expires,
		"file":         m.FilePath(),
		"licensed":     m.FilePath() != "",
	}
}

// 便捷格式化错误信息。
func FormatError(err error) string {
	switch {
	case errors.Is(err, ErrInvalidLicense):
		return "License 无效或签名不匹配"
	case errors.Is(err, ErrLicenseExpired):
		return "License 已过期"
	case err == nil:
		return ""
	default:
		return fmt.Sprintf("License 加载失败: %v", err)
	}
}
