package services

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"net"
	"time"

	"pki-manager/internal/models"
)

type CryptoService struct {
	certsPath string
}

func NewCryptoService(certsPath string) *CryptoService {
	return &CryptoService{certsPath: certsPath}
}

func (s *CryptoService) GenerateRootCA(req models.CreateCARequest) (*models.CA, error) {
	// Generate private key
	priv, err := rsa.GenerateKey(rand.Reader, req.KeySize)
	if err != nil {
		return nil, fmt.Errorf("failed to generate private key: %v", err)
	}

	// Create certificate template
	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %v", err)
	}
	// Préparer le sujet avec email optionnel
	subject := pkix.Name{
		CommonName:         req.CommonName,
		Organization:       []string{req.Organization},
		OrganizationalUnit: []string{req.OrganizationalUnit},
		Country:            []string{req.Country},
		Province:           []string{req.Province},
		Locality:           []string{req.Locality},
		StreetAddress:      []string{req.StreetAddress},
		PostalCode:         []string{req.PostalCode},
	}

	// Ajouter l'email seulement s'il est fourni
	if req.Email != "" {
		// L'email n'est pas un champ standard dans pkix.Name
		// On le stockera dans les champs personnalisés
	}
	template := x509.Certificate{
		SerialNumber:          serialNumber,
		Subject:               subject,
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(req.ValidYears, 0, 0),
		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
		MaxPathLen:            0,
		MaxPathLenZero:        true,
	}
	// Ajouter l'email seulement s'il est fourni
	if req.Email != "" {
		// L'email n'est pas un champ standard dans pkix.Name
		// On le stockera dans les champs personnalisés
	}
	// Self-sign the certificate
	certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return nil, fmt.Errorf("failed to create certificate: %v", err)
	}

	// Encode private key
	privBytes := x509.MarshalPKCS1PrivateKey(priv)
	privPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: privBytes,
	})

	// Encode certificate
	certPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certBytes,
	})

	ca := &models.CA{
		ID:                 fmt.Sprintf("ca_%d", time.Now().UnixNano()),
		Name:               req.Name,
		CommonName:         req.CommonName,
		Organization:       req.Organization,
		OrganizationalUnit: req.OrganizationalUnit,
		Country:            req.Country,
		Province:           req.Province,
		Locality:           req.Locality,
		StreetAddress:      req.StreetAddress,
		PostalCode:         req.PostalCode,
		Email:              req.Email,
		PrivateKey:         string(privPEM),
		Certificate:        string(certPEM),
		SerialNumber:       serialNumber.String(),
		ValidFrom:          template.NotBefore,
		ValidTo:            template.NotAfter,
		IsRoot:             req.IsRoot,
	}

	return ca, nil
}

func (s *CryptoService) GenerateSubCA(req models.CreateSubCARequest, parentCA *models.CA) (*models.SubCA, error) {
	// Parse parent CA certificate and private key
	parentCertBlock, _ := pem.Decode([]byte(parentCA.Certificate))
	if parentCertBlock == nil {
		return nil, fmt.Errorf("failed to parse parent CA certificate")
	}
	parentCert, err := x509.ParseCertificate(parentCertBlock.Bytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse parent CA certificate: %v", err)
	}

	parentKeyBlock, _ := pem.Decode([]byte(parentCA.PrivateKey))
	if parentKeyBlock == nil {
		return nil, fmt.Errorf("failed to parse parent CA private key")
	}
	parentKey, err := x509.ParsePKCS1PrivateKey(parentKeyBlock.Bytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse parent CA private key: %v", err)
	}

	// Generate subCA private key
	priv, err := rsa.GenerateKey(rand.Reader, req.KeySize)
	if err != nil {
		return nil, fmt.Errorf("failed to generate private key: %v", err)
	}

	// Create certificate template
	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %v", err)
	}
	subject := pkix.Name{
		CommonName:         req.CommonName,
		Organization:       []string{req.Organization},
		OrganizationalUnit: []string{req.OrganizationalUnit},
		Country:            []string{req.Country},
		Province:           []string{req.Province},
		Locality:           []string{req.Locality},
	}
	template := x509.Certificate{
		SerialNumber:          serialNumber,
		Subject:               subject,
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(req.ValidYears, 0, 0),
		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
		MaxPathLen:            0,
		MaxPathLenZero:        false,
	}

	// Sign the certificate with parent CA
	certBytes, err := x509.CreateCertificate(rand.Reader, &template, parentCert, &priv.PublicKey, parentKey)
	if err != nil {
		return nil, fmt.Errorf("failed to create certificate: %v", err)
	}

	// Encode private key
	privBytes := x509.MarshalPKCS1PrivateKey(priv)
	privPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: privBytes,
	})

	// Encode certificate
	certPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certBytes,
	})

	subca := &models.SubCA{
		ID:                 fmt.Sprintf("subca_%d", time.Now().UnixNano()),
		Name:               req.Name,
		CommonName:         req.CommonName,
		Organization:       req.Organization,
		OrganizationalUnit: req.OrganizationalUnit,
		Country:            req.Country,
		Province:           req.Province,
		Locality:           req.Locality,
		Email:              req.Email,
		PrivateKey:         string(privPEM),
		Certificate:        string(certPEM),
		SerialNumber:       serialNumber.String(),
		ValidFrom:          template.NotBefore,
		ValidTo:            template.NotAfter,
		ParentCAID:         req.ParentCAID,
	}

	return subca, nil
}

func (s *CryptoService) GenerateCertificate(req models.CreateCertificateRequest, issuer interface{}) (*models.Certificate, error) {
	var issuerCert *x509.Certificate
	var issuerKey *rsa.PrivateKey

	// Parse issuer based on type
	switch iss := issuer.(type) {
	case *models.CA:
		certBlock, _ := pem.Decode([]byte(iss.Certificate))
		if certBlock == nil {
			return nil, fmt.Errorf("failed to parse issuer certificate")
		}
		var err error
		issuerCert, err = x509.ParseCertificate(certBlock.Bytes)
		if err != nil {
			return nil, fmt.Errorf("failed to parse issuer certificate: %v", err)
		}

		keyBlock, _ := pem.Decode([]byte(iss.PrivateKey))
		if keyBlock == nil {
			return nil, fmt.Errorf("failed to parse issuer private key")
		}
		issuerKey, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
		if err != nil {
			return nil, fmt.Errorf("failed to parse issuer private key: %v", err)
		}

	case *models.SubCA:
		certBlock, _ := pem.Decode([]byte(iss.Certificate))
		if certBlock == nil {
			return nil, fmt.Errorf("failed to parse issuer certificate")
		}
		var err error
		issuerCert, err = x509.ParseCertificate(certBlock.Bytes)
		if err != nil {
			return nil, fmt.Errorf("failed to parse issuer certificate: %v", err)
		}

		keyBlock, _ := pem.Decode([]byte(iss.PrivateKey))
		if keyBlock == nil {
			return nil, fmt.Errorf("failed to parse issuer private key")
		}
		issuerKey, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
		if err != nil {
			return nil, fmt.Errorf("failed to parse issuer private key: %v", err)
		}

	default:
		return nil, fmt.Errorf("unsupported issuer type")
	}
	// Generate certificate private key
	priv, err := rsa.GenerateKey(rand.Reader, req.KeySize)
	if err != nil {
		return nil, fmt.Errorf("failed to generate private key: %v", err)
	}

	// Create certificate template
	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %v", err)
	}

	subject := pkix.Name{
		CommonName:         req.CommonName,
		Organization:       []string{issuerCert.Subject.Organization[0]},
		OrganizationalUnit: []string{issuerCert.Subject.OrganizationalUnit[0]},
		Country:            []string{issuerCert.Subject.Country[0]},
		Province:           []string{issuerCert.Subject.Province[0]},
		Locality:           []string{issuerCert.Subject.Locality[0]},
		StreetAddress:      []string{issuerCert.Subject.StreetAddress[0]},
		PostalCode:         []string{issuerCert.Subject.PostalCode[0]},
	}
	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject:      subject,
		NotBefore:    time.Now(),
		NotAfter:     time.Now().AddDate(0, 0, req.ValidDays),
		KeyUsage:     x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage:  s.getExtKeyUsage(req.Type),
		DNSNames:     req.DNSNames,
		IPAddresses:  s.parseIPs(req.IPAddresses),
	}

	// Sign the certificate
	certBytes, err := x509.CreateCertificate(rand.Reader, &template, issuerCert, &priv.PublicKey, issuerKey)
	if err != nil {
		return nil, fmt.Errorf("failed to create certificate: %v", err)
	}

	// Encode private key
	privBytes := x509.MarshalPKCS1PrivateKey(priv)
	privPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: privBytes,
	})

	// Encode certificate
	certPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certBytes,
	})

	cert := &models.Certificate{
		ID:           fmt.Sprintf("cert_%d", time.Now().UnixNano()),
		CommonName:   req.CommonName,
		DNSNames:     req.DNSNames,
		IPAddresses:  req.IPAddresses,
		Type:         req.Type,
		PrivateKey:   string(privPEM),
		Certificate:  string(certPEM),
		SerialNumber: serialNumber.String(),
		ValidFrom:    template.NotBefore,
		ValidTo:      template.NotAfter,
		IssuerCAID:   req.IssuerCAID,
		Revoked:      false,
	}

	return cert, nil
}

func (s *CryptoService) getExtKeyUsage(certType string) []x509.ExtKeyUsage {
	switch certType {
	case "server":
		return []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	case "client":
		return []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
	default:
		return []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	}
}

func (s *CryptoService) parseIPs(ips []string) []net.IP {
	var parsedIPs []net.IP
	for _, ipStr := range ips {
		if ip := net.ParseIP(ipStr); ip != nil {
			parsedIPs = append(parsedIPs, ip)
		}
	}
	return parsedIPs
}

func (s *CryptoService) ParseCertificate(data []byte) (*x509.Certificate, error) {
	block, _ := pem.Decode(data)
	if block == nil {
		return nil, fmt.Errorf("format PEM invalide")
	}
	return x509.ParseCertificate(block.Bytes)
}

func (s *CryptoService) ParsePrivateKey(data []byte) (interface{}, error) {
	block, _ := pem.Decode(data)
	if block == nil {
		return nil, fmt.Errorf("format PEM invalide")
	}

	if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
		return key, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
		return key, nil
	}
	if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
		return key, nil
	}

	return nil, fmt.Errorf("format de clé non supporté")
}
