package service

import (
	"crypto/ecdsa"
	"crypto/ed25519"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"

	"github.com/shankar0123/certctl/internal/domain"
)

// CSRValidationResult contains metadata extracted from a validated CSR.
type CSRValidationResult struct {
	KeyAlgorithm string
	KeySize      int
}

// ValidateCSRAgainstProfile parses a CSR PEM and validates that its key algorithm
// and size comply with the profile's allowed_key_algorithms rules.
// Returns extracted key metadata on success for storage in certificate_versions.
func ValidateCSRAgainstProfile(csrPEM string, profile *domain.CertificateProfile) (*CSRValidationResult, error) {
	if profile == nil {
		// No profile assigned — skip validation, extract metadata only
		return extractCSRKeyInfo(csrPEM)
	}

	result, err := extractCSRKeyInfo(csrPEM)
	if err != nil {
		return nil, err
	}

	// Check that the CSR's key algorithm + size matches at least one allowed rule
	if len(profile.AllowedKeyAlgorithms) == 0 {
		// No restrictions defined — allow anything
		return result, nil
	}

	for _, rule := range profile.AllowedKeyAlgorithms {
		if rule.Algorithm == result.KeyAlgorithm && result.KeySize >= rule.MinSize {
			return result, nil
		}
	}

	return nil, fmt.Errorf("CSR key (%s %d-bit) does not match any allowed algorithm in profile %q: %v",
		result.KeyAlgorithm, result.KeySize, profile.Name, profile.AllowedKeyAlgorithms)
}

// extractCSRKeyInfo parses a CSR PEM and extracts the key algorithm and size.
func extractCSRKeyInfo(csrPEM string) (*CSRValidationResult, error) {
	block, _ := pem.Decode([]byte(csrPEM))
	if block == nil {
		return nil, fmt.Errorf("failed to decode CSR PEM")
	}

	csr, err := x509.ParseCertificateRequest(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse CSR: %w", err)
	}

	if err := csr.CheckSignature(); err != nil {
		return nil, fmt.Errorf("CSR signature verification failed: %w", err)
	}

	switch key := csr.PublicKey.(type) {
	case *rsa.PublicKey:
		return &CSRValidationResult{
			KeyAlgorithm: domain.KeyAlgorithmRSA,
			KeySize:      key.N.BitLen(),
		}, nil
	case *ecdsa.PublicKey:
		return &CSRValidationResult{
			KeyAlgorithm: domain.KeyAlgorithmECDSA,
			KeySize:      key.Curve.Params().BitSize,
		}, nil
	case ed25519.PublicKey:
		return &CSRValidationResult{
			KeyAlgorithm: domain.KeyAlgorithmEd25519,
			KeySize:      256, // Ed25519 is fixed 256-bit
		}, nil
	default:
		return nil, fmt.Errorf("unsupported key type in CSR: %T", csr.PublicKey)
	}
}
