package flexCdn

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
	"github.com/go-nunu/nunu-layout-advanced/internal/repository/api/waf"
	"github.com/go-nunu/nunu-layout-advanced/internal/service"
)

type SslCertService interface {
	ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error)
	AddSSLCert(ctx context.Context, req v1.SSL) (int64, error)
	EditSSLCert(ctx context.Context, req v1.SSL) error
	AddSslPolicy(ctx context.Context, CertIds []int64) (sslPolicyId int64, err error)
	EditSslPolicy(ctx context.Context, sslPolicyId int64, CertIds []int64, action string) error
}
func NewSslCertService(
    service *service.Service,
   	webForwardingRep waf.WebForwardingRepository,
	cdn CdnService,
) SslCertService {
	return &sslCertService{
		Service:        service,
		webForwardingRep: webForwardingRep,
		cdn: cdn,
	}
}

type sslCertService struct {
	*service.Service
	webForwardingRep waf.WebForwardingRepository
	cdn              CdnService
}

func (s *sslCertService) ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error) {
	cert, err := tls.X509KeyPair([]byte(httpsCert), []byte(httpKey))
	if err != nil {
		return "", nil, nil, 0, 0, false, fmt.Errorf("无法从字符串加载密钥对: %v", err)
	}

	if len(cert.Certificate) == 0 {
		return "", nil, nil, 0, 0, false, fmt.Errorf("提供的证书数据中没有找到证书。")
	}

	// 解析第一个证书（通常是叶子证书）
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return "", nil, nil, 0, 0, false, fmt.Errorf("无法解析证书: %v", err)
	}

	// 1. 获取 Common Name (通用名称)
	// Common Name 位于 Subject 字段内. [1]
	serverName = x509Cert.Subject.CommonName

	// 2. 获取 DNS Names (备用主题名称中的DNS条目)
	// DNS Names 直接是证书结构体的一个字段. [1]
	DNSNames = x509Cert.DNSNames

	// 检查证书是否为自签名
	// 判断条件：颁发者(Issuer)和主题(Subject)相同，并且证书的签名可以由其自身的公钥验证
	if err := x509Cert.CheckSignatureFrom(x509Cert); err == nil {
		isSelfSigned = true
	}

	// 将CommonName放入一个切片，以匹配[]string的类型要求
	var commonNames []string
	if x509Cert.Subject.CommonName != "" {
		commonNames = []string{x509Cert.Subject.CommonName}
	}

	return serverName, commonNames, DNSNames, x509Cert.NotBefore.Unix(), x509Cert.NotAfter.Unix(), isSelfSigned, nil

}

func (s *sslCertService) AddSslPolicy(ctx context.Context, CertIds []int64) (sslPolicyId int64, err error) {
	// 构造策略中引用的证书列表
	type sslCerts struct {
		IsOn   bool  `json:"isOn" form:"isOn"`
		CertId int64 `json:"certId" form:"certId"`
	}
	var sslCertsSlice []sslCerts
	for _, certId := range CertIds {
		sslCertsSlice = append(sslCertsSlice, sslCerts{
			IsOn:   true,
			CertId: certId,
		})
	}

	sslCertsJson, err := json.Marshal(sslCertsSlice)
	if err != nil {
		return 0, fmt.Errorf("序列化SSL证书引用失败: %w", err)
	}

	// 调用CDN服务创建策略
	newSslPolicyId, err := s.cdn.AddSSLPolicy(ctx, v1.AddSSLPolicy{
		Http2Enabled: true,
		SslCertsJSON: sslCertsJson,
		MinVersion:   "TLS 1.1", // 可根据安全要求调整
	})
	if err != nil {
		// 如果策略创建失败，需要考虑回滚或记录错误，这里直接返回错误
		return 0, fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
	}
	return newSslPolicyId, nil
}

func (s *sslCertService) EditSslPolicy(ctx context.Context, sslPolicyId int64, CertIds []int64, action string) error {
	type sslCerts struct {
		IsOn   bool  `json:"isOn" form:"isOn"`
		CertId int64 `json:"certId" form:"certId"`
	}
	oldCertIds, err := s.webForwardingRep.GetSslCertId(ctx, sslPolicyId)
	if err != nil {
		return fmt.Errorf("获取SSL证书失败: %w", err)
	}

	var sslCertsSlice []sslCerts

	newCertIdSet := make(map[int64]struct{}, len(CertIds))
	for _, certId := range CertIds {
		newCertIdSet[certId] = struct{}{}
	}

	oldCertIdSet := make(map[int64]struct{}, len(oldCertIds))
	for _, oldCert := range oldCertIds {
		oldCertIdSet[oldCert.CertId] = struct{}{}
	}

	switch action {
	case "add":
		for _, certId := range CertIds {
			// 使用 oldCertIdSet 进行 O(1) 复杂度的查找。
			if _, found := oldCertIdSet[certId]; !found {
				// 如果在旧的集合中没找到，说明是新增的。
				sslCertsSlice = append(sslCertsSlice, sslCerts{
					IsOn:   true,
					CertId: certId,
				})
			}
		}
	case "del":
		for _, oldCert := range oldCertIds {
			// 使用 newCertIdSet 进行 O(1) 复杂度的查找。
			if _, found := newCertIdSet[oldCert.CertId]; !found {
				// 如果在新的集合中没找到，说明被删除了。
				sslCertsSlice = append(sslCertsSlice, sslCerts{
					IsOn:   false,
					CertId: oldCert.CertId,
				})
			}
		}

	}


	sslCertsJson, err := json.Marshal(sslCertsSlice)
	if err != nil {
		return  fmt.Errorf("序列化SSL证书引用失败: %w", err)
	}

	// 调用CDN服务创建策略
	 err = s.cdn.EditSSLPolicy(ctx, v1.SSLPolicy{
		SslPolicyId: sslPolicyId,
		Http2Enabled: true,
		SslCertsJSON: sslCertsJson,
		MinVersion:   "TLS 1.1", // 可根据安全要求调整
	})
	if err != nil {
		// 如果策略创建失败，需要考虑回滚或记录错误，这里直接返回错误
		return fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
	}
	return nil
}


func (s *sslCertService) AddSSLCert(ctx context.Context, req v1.SSL) (int64, error) {
	// 1. 解析证书文件，提取元数据
	serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
	if err != nil {
		return  0, fmt.Errorf("解析证书失败: %w", err)
	}

	// 2. 将证书添加到CDN提供商
	// 这是获取可以在策略中引用的 `sslCertId` 的前提
	newSslCertId, err := s.cdn.AddSSLCert(ctx, v1.SSlCert{
		IsOn:         true,
		UserId:       int64(req.CdnUserId),
		Name:         req.Domain, // 使用域名作为证书名称
		ServerName:   serverName,
		Description:  req.Description,
		CertData:     []byte(req.CertData),
		KeyData:      []byte(req.KeyData),
		TimeBeginAt:  before,
		TimeEndAt:    after,
		DnsNames:     DNSNames,
		CommonNames:  commonNames,
		IsSelfSigned: isSelfSigned,
	})
	if err != nil {
		return  0, fmt.Errorf("添加SSL证书到CDN失败: %w", err)
	}
	return newSslCertId, nil
}


func (s *sslCertService) EditSSLCert(ctx context.Context, req v1.SSL) error {
	if req.CertData == "" && req.KeyData == "" {
		return nil
	}
	// 1. 解析证书文件，提取元数据
	serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
	if err != nil {
		return fmt.Errorf("解析证书失败: %w", err)
	}

	// 2. 将证书添加到CDN提供商
	err = s.cdn.EditSSLCert(ctx, v1.SSlCert{
		SslCertId: int64(req.CertId),
		IsOn:        true,
		UserId:      int64(req.CdnUserId),
		Name:        req.Domain, // 使用域名作为证书名称
		ServerName:  serverName,
		Description: req.Description,
		CertData:    []byte(req.CertData),
		KeyData:     []byte(req.KeyData),
		TimeBeginAt: before,
		TimeEndAt:   after,
		DnsNames:    DNSNames,
		CommonNames:  commonNames,
		IsSelfSigned: isSelfSigned,
	})
	if err != nil {
		return fmt.Errorf("添加SSL证书到CDN失败: %w", err)
	}
	return nil
}

