// Package ssl 提供 SSL Session Tickets 支持。
//
// 该文件包含 TLS Session Tickets 密钥管理和轮换逻辑，包括：
//   - Session Ticket 密钥生成和加载
//   - 自动密钥轮换机制
//   - 多密钥保留策略（支持旧票据解密）
//   - 与 TLS 配置的集成
//
// Session Tickets 允许 TLS 1.3 会话恢复，避免完整握手，显著提升性能。
// 密钥定期轮换增强安全性，同时保留旧密钥确保已发放的票据仍可解密。
//
// 作者：xfy
package ssl

import (
	"crypto/rand"
	"crypto/tls"
	"errors"
	"fmt"
	"os"
	"sync"
	"time"

	"rua.plus/lolly/internal/config"
	"rua.plus/lolly/internal/logging"
)

const (
	// ticketKeySize Session Ticket 密钥大小（字节）
	// TLS 1.3 使用 32 字节的 AES-256-GCM 密钥
	ticketKeySize = 32

	// defaultRotateInterval 默认密钥轮换间隔
	defaultRotateInterval = time.Hour

	// defaultRetainKeys 默认保留的密钥数量
	// 至少保留 2 个密钥（当前 + 上一个）
	defaultRetainKeys = 3

	// minRetainKeys 最小保留密钥数量
	minRetainKeys = 2
)

// SessionTicketManager Session Ticket 密钥管理器。
//
// 管理 Session Ticket 密钥的生命周期，包括生成、轮换、存储和加载。
// 密钥按时间顺序排列，最新的密钥用于加密，所有密钥都可用于解密。
type SessionTicketManager struct {
	rotateTimer *time.Timer
	stopCh      chan struct{}
	keys        [][]byte
	config      config.SessionTicketsConfig
	mu          sync.RWMutex
	started     bool
}

// NewSessionTicketManager 创建新的 Session Ticket 管理器。
//
// 根据配置创建管理器，如 key_file 存在则加载现有密钥，
// 否则自动生成新密钥。
//
// 参数：
//   - cfg: Session Tickets 配置
//
// 返回值：
//   - *SessionTicketManager: 配置好的管理器
//   - error: 密钥加载或生成失败时返回错误
func NewSessionTicketManager(cfg config.SessionTicketsConfig) (*SessionTicketManager, error) {
	if !cfg.Enabled {
		return nil, errors.New("session tickets are disabled")
	}

	// 使用默认值
	rotateInterval := cfg.RotateInterval
	if rotateInterval <= 0 {
		rotateInterval = defaultRotateInterval
	}

	retainKeys := cfg.RetainKeys
	if retainKeys < minRetainKeys {
		retainKeys = defaultRetainKeys
	}

	manager := &SessionTicketManager{
		config: config.SessionTicketsConfig{
			Enabled:        cfg.Enabled,
			KeyFile:        cfg.KeyFile,
			RotateInterval: rotateInterval,
			RetainKeys:     retainKeys,
		},
		keys:   make([][]byte, 0, retainKeys),
		stopCh: make(chan struct{}),
	}

	// 尝试加载或生成初始密钥
	if cfg.KeyFile != "" {
		if err := manager.loadOrGenerateKey(); err != nil {
			return nil, fmt.Errorf("failed to initialize session ticket key: %w", err)
		}
	} else {
		// 没有指定密钥文件，生成内存中的密钥
		// 警告：服务重启后旧票据将失效，影响前向保密性
		logging.Warn().Msg("SessionTickets enabled without KeyFile: session tickets will be invalid after restart, consider configuring KeyFile for persistence")
		key, err := generateTicketKey()
		if err != nil {
			return nil, fmt.Errorf("failed to generate session ticket key: %w", err)
		}
		manager.keys = append(manager.keys, key)
	}

	return manager, nil
}

// Start 启动密钥轮换定时器。
//
// 按照配置的 rotate_interval 定期生成新密钥。
// 必须在调用 GetKeys 之前启动。
func (m *SessionTicketManager) Start() {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.started {
		return
	}
	m.started = true

	// 重建 stopCh 以支持 Start-Stop-Start 周期
	select {
	case <-m.stopCh:
		m.stopCh = make(chan struct{})
	default:
	}

	// 启动轮换定时器
	m.scheduleRotationLocked()
}

// Stop 停止密钥轮换定时器。
//
// 停止后不再进行自动密钥轮换，但现有密钥仍然有效。
func (m *SessionTicketManager) Stop() {
	m.mu.Lock()
	if !m.started {
		m.mu.Unlock()
		return
	}
	m.started = false
	if m.rotateTimer != nil {
		m.rotateTimer.Stop()
		m.rotateTimer = nil
	}
	m.mu.Unlock()

	select {
	case <-m.stopCh:
		// 已经关闭
	default:
		close(m.stopCh)
	}
}

// GetKeys 返回当前所有有效的 Session Ticket 密钥。
//
// 返回的密钥按时间顺序排列，最新的在最后。
// TLS 配置应该使用最新的密钥加密，所有密钥都可以解密。
//
// 返回值：
//   - [][]byte: 密钥列表，每个密钥 32 字节
func (m *SessionTicketManager) GetKeys() [][]byte {
	m.mu.RLock()
	defer m.mu.RUnlock()

	// 返回副本以防止外部修改
	result := make([][]byte, len(m.keys))
	for i, key := range m.keys {
		result[i] = make([]byte, len(key))
		copy(result[i], key)
	}
	return result
}

// RotateKey 手动轮换 Session Ticket 密钥。
//
// 生成新密钥并添加到密钥列表，如果超过 retain_keys 数量则移除最旧的密钥。
// 新密钥用于加密新票据，旧密钥仍可用于解密已发放的票据。
//
// 返回值：
//   - error: 密钥生成失败时返回错误
func (m *SessionTicketManager) RotateKey() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	// 生成新密钥
	newKey, err := generateTicketKey()
	if err != nil {
		return fmt.Errorf("failed to generate new ticket key: %w", err)
	}

	// 添加新密钥
	m.keys = append(m.keys, newKey)

	// 如果超过保留数量，移除最旧的密钥
	if len(m.keys) > m.config.RetainKeys {
		m.keys = m.keys[len(m.keys)-m.config.RetainKeys:]
	}

	// 如果有密钥文件，保存所有密钥
	if m.config.KeyFile != "" {
		if err := m.saveKeys(); err != nil {
			logging.Warn().Err(err).Msg("Session Ticket 密钥保存失败")
		}
	}

	return nil
}

// ApplyToTLSConfig 将 Session Ticket 密钥应用到 TLS 配置。
//
// 设置 tls.Config 的 SetSessionTicketKeys 回调，用于动态提供密钥。
//
// 参数：
//   - tlsCfg: TLS 配置对象
func (m *SessionTicketManager) ApplyToTLSConfig(tlsCfg *tls.Config) {
	if tlsCfg == nil {
		return
	}

	// 设置会话票据密钥
	// Go 的 crypto/tls 使用 SetSessionTicketKeys 方法设置密钥
	// 需要转换为 [][32]byte 类型
	keys := m.GetKeys()
	ticketKeys := make([][32]byte, len(keys))
	for i, key := range keys {
		if len(key) >= 32 {
			copy(ticketKeys[i][:], key)
		}
	}
	tlsCfg.SetSessionTicketKeys(ticketKeys)
}

// scheduleRotation 调度密钥轮换。
//
// 使用定时器在指定间隔后执行密钥轮换。
func (m *SessionTicketManager) scheduleRotation() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.scheduleRotationLocked()
}

func (m *SessionTicketManager) scheduleRotationLocked() {
	if !m.started {
		return
	}

	m.rotateTimer = time.AfterFunc(m.config.RotateInterval, func() {
		select {
		case <-m.stopCh:
			return
		default:
			_ = m.RotateKey()
			m.scheduleRotation()
		}
	})
}

// loadOrGenerateKey 从文件加载密钥或生成新密钥。
//
// 如果密钥文件存在，加载所有密钥；否则生成新密钥并保存。
//
// 返回值：
//   - error: 加载或生成失败时返回错误
func (m *SessionTicketManager) loadOrGenerateKey() error {
	// 尝试加载现有密钥
	if _, err := os.Stat(m.config.KeyFile); err == nil {
		// 文件存在，加载密钥
		if err := m.loadKeys(); err != nil {
			// 加载失败，生成新密钥
			return m.generateAndSaveKey()
		}
		return nil
	}

	// 文件不存在，生成新密钥
	return m.generateAndSaveKey()
}

// loadKeys 从文件加载所有密钥。
//
// 密钥文件格式：每个密钥 32 字节，连续存储
//
// 返回值：
//   - error: 读取或解析失败时返回错误
func (m *SessionTicketManager) loadKeys() error {
	data, err := os.ReadFile(m.config.KeyFile)
	if err != nil {
		return fmt.Errorf("failed to read key file: %w", err)
	}

	// 解析密钥（每个 32 字节）
	if len(data) < ticketKeySize {
		return errors.New("key file too small")
	}

	m.keys = make([][]byte, 0, m.config.RetainKeys)
	for i := 0; i+ticketKeySize <= len(data); i += ticketKeySize {
		key := make([]byte, ticketKeySize)
		copy(key, data[i:i+ticketKeySize])
		m.keys = append(m.keys, key)
	}

	// 如果加载的密钥超过保留数量，只保留最新的
	if len(m.keys) > m.config.RetainKeys {
		m.keys = m.keys[len(m.keys)-m.config.RetainKeys:]
	}

	// 确保至少有一个密钥
	if len(m.keys) == 0 {
		return errors.New("no valid keys found in file")
	}

	return nil
}

// saveKeys 将所有密钥保存到文件。
//
// 密钥文件格式：每个密钥 32 字节，连续存储
// 文件权限设置为 0600（仅所有者可读写）
//
// 返回值：
//   - error: 写入失败时返回错误
func (m *SessionTicketManager) saveKeys() error {
	// 计算总大小
	totalSize := len(m.keys) * ticketKeySize
	data := make([]byte, 0, totalSize)

	for _, key := range m.keys {
		data = append(data, key...)
	}

	// 使用 0600 权限写入文件（敏感数据，限制访问）
	if err := os.WriteFile(m.config.KeyFile, data, 0o600); err != nil {
		return fmt.Errorf("failed to write key file: %w", err)
	}

	return nil
}

// generateAndSaveKey 生成新密钥并保存。
//
// 返回值：
//   - error: 生成或保存失败时返回错误
func (m *SessionTicketManager) generateAndSaveKey() error {
	key, err := generateTicketKey()
	if err != nil {
		return err
	}

	m.keys = [][]byte{key}

	if m.config.KeyFile != "" {
		if err := m.saveKeys(); err != nil {
			return err
		}
	}

	return nil
}

// generateTicketKey 生成新的随机 Session Ticket 密钥。
//
// 使用 crypto/rand 生成加密安全的随机密钥。
//
// 返回值：
//   - []byte: 32 字节的随机密钥
//   - error: 随机数生成失败时返回错误
func generateTicketKey() ([]byte, error) {
	key := make([]byte, ticketKeySize)
	if _, err := rand.Read(key); err != nil {
		return nil, fmt.Errorf("failed to generate random key: %w", err)
	}
	return key, nil
}

// SessionTicketStatus Session Ticket 密钥状态信息。
//
// 用于监控和调试，显示当前密钥数量和轮换状态。
type SessionTicketStatus struct {
	// KeyCount 当前密钥数量
	KeyCount int

	// RetainKeys 配置的最大保留密钥数
	RetainKeys int

	// RotateInterval 配置的轮换间隔
	RotateInterval time.Duration

	// Started 管理器是否已启动
	Started bool
}
