package cert_manager_wdd

import (
	"agent-wdd/cloudflare"
	"agent-wdd/log"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

const (
	// DefaultCertDir 默认证书保存目录
	DefaultCertDir = "certs"
	// DefaultDaysBeforeRenewal 默认更新阈值天数
	DefaultDaysBeforeRenewal = 30
	// DefaultKeyType 默认密钥类型
	DefaultKeyType = "ec-256"
)

// CACertInfo CA证书服务器信息
type CACertInfo struct {
	Name      string // 证书颁发机构名称
	Directory string // ACME目录URL
}

// 预定义的CA服务器
var (
	ZeroSSLCA = CACertInfo{
		Name:      "ZeroSSL",
		Directory: "https://acme.zerossl.com/v2/DV90",
	}

	LetsEncryptCA = CACertInfo{
		Name:      "Let's Encrypt",
		Directory: "https://acme-v02.api.letsencrypt.org/directory",
	}
)

// CertInfo 存储证书信息
type CertInfo struct {
	Domain        string    // 证书域名
	RegisteredAt  time.Time // 注册时间
	ExpiresAt     time.Time // 过期时间
	CertPath      string    // 证书路径
	KeyPath       string    // 密钥路径
	CAName        string    // CA名称
	NeedsRenewal  bool      // 是否需要更新
	WildcardCert  bool      // 是否为通配符证书
	DaysRemaining int       // 剩余有效天数
}

// CertManager 证书管理类
type CertManager struct {
	CertDir           string                       // 证书保存目录
	CloudflareAPI     *cloudflare.CloudflareClient // Cloudflare API客户端
	CAServer          CACertInfo                   // 使用的CA服务器
	DaysBeforeRenewal int                          // 证书更新阈值天数
	EmailAddr         string                       // 申请证书使用的邮箱
}

// NewCertManager 创建一个新的证书管理器
//
// 参数:
//   - certDir: 证书保存目录，若为空则使用默认目录
//   - cfToken: Cloudflare API令牌
//   - emailAddr: 申请证书使用的邮箱
//
// 返回:
//   - *CertManager: 初始化的证书管理器
func NewCertManager(certDir string, cfToken string, emailAddr string) *CertManager {
	if certDir == "" {
		homeDir, err := os.UserHomeDir()
		if err != nil {
			log.Warning("无法获取用户主目录: %v，使用当前目录", err)
			homeDir = "."
		}
		certDir = filepath.Join(homeDir, DefaultCertDir)
	}

	// 确保证书目录存在
	if err := os.MkdirAll(certDir, 0755); err != nil {
		log.Error("创建证书目录失败: %v", err)
	}

	return &CertManager{
		CertDir:           certDir,
		CloudflareAPI:     cloudflare.GetInstance(cfToken),
		CAServer:          LetsEncryptCA, // 默认使用Let's Encrypt
		DaysBeforeRenewal: DefaultDaysBeforeRenewal,
		EmailAddr:         emailAddr,
	}
}

// SetCAServer 设置CA服务器
//
// 参数:
//   - caServer: CA服务器信息
func (cm *CertManager) SetCAServer(caServer CACertInfo) {
	cm.CAServer = caServer
	log.Info("已将CA服务器设置为 %s", caServer.Name)
}

// ApplyCertificate 申请证书
//
// 参数:
//   - domain: 需要申请证书的域名
//
// 返回:
//   - *CertInfo: 证书信息
//   - error: 错误信息
func (cm *CertManager) ApplyCertificate(domain string) (*CertInfo, error) {
	log.Info("开始为域名 %s 申请证书", domain)

	// 处理通配符域名文件夹命名
	folderName := domain
	if strings.HasPrefix(domain, "*.") {
		folderName = "x" + domain[1:]
		log.Debug("通配符域名文件夹命名为: %s", folderName)
	}

	// 创建域名对应的证书目录
	certFolder := filepath.Join(cm.CertDir, folderName)
	if err := os.MkdirAll(certFolder, 0755); err != nil {
		log.Error("创建域名证书目录失败: %v", err)
		return nil, fmt.Errorf("创建域名证书目录失败: %w", err)
	}

	// 生成私钥
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		log.Error("生成私钥失败: %v", err)
		return nil, fmt.Errorf("生成私钥失败: %w", err)
	}

	keyPath := filepath.Join(certFolder, "private.key")
	if err := savePrivateKey(privateKey, keyPath); err != nil {
		log.Error("保存私钥失败: %v", err)
		return nil, fmt.Errorf("保存私钥失败: %w", err)
	}

	// 使用自定义的DNS验证方法
	dnsSolver := &DNSSolver{
		CloudflareAPI: cm.CloudflareAPI,
	}

	// 使用自定义方法申请证书
	issuedCert, err := IssueCertificate(domain, cm.EmailAddr, cm.CAServer.Directory, privateKey, certFolder, dnsSolver)
	if err != nil {
		log.Error("证书申请失败: %v", err)
		return nil, fmt.Errorf("证书申请失败: %w", err)
	}

	log.Info("证书申请成功: %s", domain)
	return issuedCert, nil
}

// GetAllCerts 获取所有证书信息
//
// 返回:
//   - []CertInfo: 证书信息列表
//   - error: 错误信息
func (cm *CertManager) GetAllCerts() ([]CertInfo, error) {
	log.Info("获取所有证书信息")
	var certs []CertInfo

	// 遍历证书目录
	err := filepath.Walk(cm.CertDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 只处理目录
		if !info.IsDir() || path == cm.CertDir {
			return nil
		}

		// 尝试解析证书
		certPath := filepath.Join(path, "cert.pem")
		keyPath := filepath.Join(path, "private.key")

		if !fileExists(certPath) || !fileExists(keyPath) {
			log.Debug("目录 %s 中未找到完整的证书文件", path)
			return nil
		}

		cert, err := parseCertificate(certPath)
		if err != nil {
			log.Warning("解析证书 %s 失败: %v", certPath, err)
			return nil
		}

		// 获取域名
		folderName := filepath.Base(path)
		domain := folderName
		if strings.HasPrefix(folderName, "x") {
			domain = "*" + domain[1:]
		}

		// 计算剩余天数
		daysRemaining := int(cert.NotAfter.Sub(time.Now()).Hours() / 24)
		needsRenewal := daysRemaining <= cm.DaysBeforeRenewal

		certInfo := CertInfo{
			Domain:        domain,
			RegisteredAt:  cert.NotBefore,
			ExpiresAt:     cert.NotAfter,
			CertPath:      certPath,
			KeyPath:       keyPath,
			CAName:        getCertCAName(cert),
			NeedsRenewal:  needsRenewal,
			WildcardCert:  strings.HasPrefix(domain, "*."),
			DaysRemaining: daysRemaining,
		}

		certs = append(certs, certInfo)
		return nil
	})

	if err != nil {
		log.Error("遍历证书目录失败: %v", err)
		return nil, fmt.Errorf("遍历证书目录失败: %w", err)
	}

	log.Info("找到 %d 个证书", len(certs))
	return certs, nil
}

// RenewCertificate 更新证书
//
// 参数:
//   - domain: 需要更新证书的域名
//
// 返回:
//   - *CertInfo: 更新后的证书信息
//   - error: 错误信息
func (cm *CertManager) RenewCertificate(domain string) (*CertInfo, error) {
	log.Info("开始更新域名 %s 的证书", domain)

	// 获取当前证书信息
	allCerts, err := cm.GetAllCerts()
	if err != nil {
		return nil, err
	}

	var certToRenew *CertInfo
	for _, cert := range allCerts {
		if cert.Domain == domain {
			certToRenew = &cert
			break
		}
	}

	if certToRenew == nil {
		log.Error("未找到域名 %s 的证书", domain)
		return nil, fmt.Errorf("未找到域名 %s 的证书", domain)
	}

	// 调用申请证书的方法重新申请
	return cm.ApplyCertificate(domain)
}

// 工具函数

// savePrivateKey 保存私钥到文件
func savePrivateKey(privateKey *ecdsa.PrivateKey, filePath string) error {
	// 将私钥编码为PKCS8格式
	privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
	if err != nil {
		return err
	}

	// 将私钥编码为PEM格式
	privateKeyPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: privateKeyBytes,
	})

	// 写入文件
	return os.WriteFile(filePath, privateKeyPEM, 0600)
}

// parseCertificate 解析证书文件
func parseCertificate(certPath string) (*x509.Certificate, error) {
	certPEM, err := os.ReadFile(certPath)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(certPEM)
	if block == nil || block.Type != "CERTIFICATE" {
		return nil, fmt.Errorf("无法解码证书PEM数据")
	}

	return x509.ParseCertificate(block.Bytes)
}

// getCertCAName 获取证书颁发机构名称
func getCertCAName(cert *x509.Certificate) string {
	// 尝试从证书中提取颁发者信息
	if cert.Issuer.CommonName != "" {
		if strings.Contains(cert.Issuer.CommonName, "Let's Encrypt") {
			return "Let's Encrypt"
		} else if strings.Contains(cert.Issuer.CommonName, "ZeroSSL") {
			return "ZeroSSL"
		}
	}
	return cert.Issuer.CommonName
}

// fileExists 检查文件是否存在
func fileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}
