package api

import (
	"bytes"
	"crypto/x509"
	"fmt"
	"net/http"
	"pki-manager/internal/models"
	"pki-manager/internal/repository"
	"pki-manager/internal/services"

	"github.com/gin-gonic/gin"
	"software.sslmate.com/src/go-pkcs12"
)

type Handlers struct {
	repo          repository.Repository
	cryptoService *services.CryptoService
}

func NewHandlers(repo repository.Repository, cryptoService *services.CryptoService) *Handlers {
	return &Handlers{
		repo:          repo,
		cryptoService: cryptoService,
	}
}

// CA Handlers
func (h *Handlers) CreateCA(c *gin.Context) {
	var req models.CreateCARequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	ca, err := h.cryptoService.GenerateRootCA(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if err := h.repo.CreateCA(c.Request.Context(), ca); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, ca)
}

func (h *Handlers) GetCA(c *gin.Context) {
	id := c.Param("id")
	ca, err := h.repo.GetCA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "CA not found"})
		return
	}

	// Don't expose private key in GET requests
	ca.PrivateKey = ""
	c.JSON(http.StatusOK, ca)
}

// GetAllCAs - Retourne toujours un tableau, même vide
func (h *Handlers) GetAllCAs(c *gin.Context) {
	cas, err := h.repo.GetAllCAs(c.Request.Context())
	if err != nil {
		// IMPORTANT: Retourner un tableau vide, pas une erreur
		c.JSON(http.StatusOK, []interface{}{})
		return
	}

	// Remove private keys from response
	for _, ca := range cas {
		ca.PrivateKey = ""
	}

	// S'assurer qu'on retourne toujours un tableau
	if cas == nil {
		c.JSON(http.StatusOK, []interface{}{})
	} else {
		c.JSON(http.StatusOK, cas)
	}
}

func (h *Handlers) UpdateCA(c *gin.Context) {
	id := c.Param("id")
	var req models.UpdateCARequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	updates := make(map[string]interface{})
	if req.Name != "" {
		updates["name"] = req.Name
	}
	if req.Organization != "" {
		updates["organization"] = req.Organization
	}
	if req.Email != "" {
		updates["email"] = req.Email
	}

	if err := h.repo.UpdateCA(c.Request.Context(), id, updates); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "CA updated successfully"})
}

func (h *Handlers) DeleteCA(c *gin.Context) {
	id := c.Param("id")
	if err := h.repo.DeleteCA(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "CA deleted successfully"})
}

// SubCA Handlers
func (h *Handlers) CreateSubCA(c *gin.Context) {
	var req models.CreateSubCARequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Get parent CA
	parentCA, err := h.repo.GetCA(c.Request.Context(), req.ParentCAID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Parent CA not found"})
		return
	}

	subca, err := h.cryptoService.GenerateSubCA(req, parentCA)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if err := h.repo.CreateSubCA(c.Request.Context(), subca); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, subca)
}

func (h *Handlers) GetSubCA(c *gin.Context) {
	id := c.Param("id")
	subca, err := h.repo.GetSubCA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "SubCA not found"})
		return
	}

	subca.PrivateKey = ""
	c.JSON(http.StatusOK, subca)
}

// GetAllSubCAs - Retourne toujours un tableau, même vide
func (h *Handlers) GetAllSubCAs(c *gin.Context) {
	subcas, err := h.repo.GetAllSubCAs(c.Request.Context())
	if err != nil {
		// IMPORTANT: Retourner un tableau vide, pas une erreur
		c.JSON(http.StatusOK, []interface{}{})
		return
	}

	// Remove private keys from response
	for _, subca := range subcas {
		subca.PrivateKey = ""
	}

	// S'assurer qu'on retourne toujours un tableau
	if subcas == nil {
		c.JSON(http.StatusOK, []interface{}{})
	} else {
		c.JSON(http.StatusOK, subcas)
	}
}

func (h *Handlers) UpdateSubCA(c *gin.Context) {
	id := c.Param("id")
	var req models.UpdateSubCARequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	updates := make(map[string]interface{})
	if req.Name != "" {
		updates["name"] = req.Name
	}
	if req.Organization != "" {
		updates["organization"] = req.Organization
	}
	if req.Email != "" {
		updates["email"] = req.Email
	}

	if err := h.repo.UpdateSubCA(c.Request.Context(), id, updates); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "SubCA updated successfully"})
}

func (h *Handlers) DeleteSubCA(c *gin.Context) {
	id := c.Param("id")
	if err := h.repo.DeleteSubCA(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "SubCA deleted successfully"})
}

// Certificate Handlers
func (h *Handlers) CreateCertificate(c *gin.Context) {
	var req models.CreateCertificateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Try to get issuer as CA first
	issuer, err := h.repo.GetCA(c.Request.Context(), req.IssuerCAID)
	if err != nil {
		// If not found as CA, try as SubCA
		issuer, err := h.repo.GetSubCA(c.Request.Context(), req.IssuerCAID)
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": "Issuer not found"})
			return
		}
		cert, err := h.cryptoService.GenerateCertificate(req, issuer)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		if err := h.repo.CreateCertificate(c.Request.Context(), cert); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusCreated, cert)
		return
	}

	cert, err := h.cryptoService.GenerateCertificate(req, issuer)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if err := h.repo.CreateCertificate(c.Request.Context(), cert); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, cert)
}

func (h *Handlers) GetCertificate(c *gin.Context) {
	id := c.Param("id")
	cert, err := h.repo.GetCertificate(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
		return
	}

	cert.PrivateKey = ""
	c.JSON(http.StatusOK, cert)
}

// GetAllCertificates - Retourne toujours un tableau, même vide
func (h *Handlers) GetAllCertificates(c *gin.Context) {
	certs, err := h.repo.GetAllCertificates(c.Request.Context())
	if err != nil {
		// IMPORTANT: Retourner un tableau vide, pas une erreur
		c.JSON(http.StatusOK, []interface{}{})
		return
	}

	// Remove private keys from response
	for _, cert := range certs {
		cert.PrivateKey = ""
	}

	// S'assurer qu'on retourne toujours un tableau
	if certs == nil {
		c.JSON(http.StatusOK, []interface{}{})
	} else {
		c.JSON(http.StatusOK, certs)
	}
}

func (h *Handlers) DeleteCertificate(c *gin.Context) {
	id := c.Param("id")
	if err := h.repo.DeleteCertificate(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Certificate deleted successfully"})
}

func (h *Handlers) RevokeCertificate(c *gin.Context) {
	id := c.Param("id")
	var req models.RevokeCertificateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if err := h.repo.RevokeCertificate(c.Request.Context(), id, req.Reason); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Certificate revoked successfully"})
}

// Download handlers for CA and SubCA
func (h *Handlers) DownloadCACertificate(c *gin.Context) {
	id := c.Param("id")
	ca, err := h.repo.GetCA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "CA not found"})
		return
	}

	c.Header("Content-Type", "application/x-pem-file")
	c.Header("Content-Disposition", "attachment; filename="+ca.CommonName+".crt")
	c.String(http.StatusOK, ca.Certificate)
}

func (h *Handlers) DownloadCAPrivateKey(c *gin.Context) {
	id := c.Param("id")
	cert, err := h.repo.GetCA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
		return
	}

	c.Header("Content-Type", "application/x-pem-file")
	c.Header("Content-Disposition", "attachment; filename="+cert.CommonName+".key")
	c.String(http.StatusOK, cert.PrivateKey)
}

func (h *Handlers) DownloadSubCACertificate(c *gin.Context) {
	id := c.Param("id")
	subca, err := h.repo.GetSubCA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "SubCA not found"})
		return
	}

	c.Header("Content-Type", "application/x-pem-file")
	c.Header("Content-Disposition", "attachment; filename="+subca.CommonName+".crt")
	c.String(http.StatusOK, subca.Certificate)
}

func (h *Handlers) DownloadSubCAPrivateKey(c *gin.Context) {
	id := c.Param("id")
	cert, err := h.repo.GetSubCA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
		return
	}

	c.Header("Content-Type", "application/x-pem-file")
	c.Header("Content-Disposition", "attachment; filename="+cert.CommonName+".key")
	c.String(http.StatusOK, cert.PrivateKey)
}

// Download handlers
func (h *Handlers) DownloadCertificate(c *gin.Context) {
	id := c.Param("id")
	cert, err := h.repo.GetCertificate(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
		return
	}

	c.Header("Content-Type", "application/x-pem-file")
	c.Header("Content-Disposition", "attachment; filename="+cert.CommonName+".crt")
	c.String(http.StatusOK, cert.Certificate)
}

func (h *Handlers) DownloadPrivateKey(c *gin.Context) {
	id := c.Param("id")
	cert, err := h.repo.GetCertificate(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
		return
	}

	c.Header("Content-Type", "application/x-pem-file")
	c.Header("Content-Disposition", "attachment; filename="+cert.CommonName+".key")
	c.String(http.StatusOK, cert.PrivateKey)
}

func (h *Handlers) DownloadP12(c *gin.Context) {
	id := c.Param("id")
	var p12Data []byte

	cert, err := h.repo.GetCertificate(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
		return
	}

	ca, err := h.repo.GetCA(c.Request.Context(), cert.IssuerCAID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Certificate CA not found"})
		return
	}

	// Choisir la méthode d'encodage selon la configuration

	x509cert, err := h.cryptoService.ParseCertificate([]byte(cert.Certificate))
	if err != nil {
		message := fmt.Sprintf("Certificateformat PEM invalide %s", err)
		c.JSON(http.StatusNotFound, gin.H{"error": message})
		return
	}
	x509key, err := h.cryptoService.ParsePrivateKey([]byte(cert.PrivateKey))
	if err != nil {
		message := fmt.Sprintf("Keyformat PEM invalide %s", err)
		c.JSON(http.StatusNotFound, gin.H{"error": message})
		return
	}

	x509cacert, err := h.cryptoService.ParseCertificate([]byte(ca.Certificate))
	if err != nil {
		message := fmt.Sprintf("CA Certificateformat PEM invalide %s", err)
		c.JSON(http.StatusNotFound, gin.H{"error": message})
		return
	}

	caCerts := []*x509.Certificate{x509cacert}

	p12Data, err = pkcs12.Modern.Encode(x509key, x509cert, caCerts, "password")
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "échec encodage P12"})
		return
	}
	buffer := bytes.NewBuffer(p12Data)
	securityLevel := c.DefaultQuery("security", "modern")
	filename := cert.CommonName + ".p12"
	c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
	c.Header("Content-Type", "application/x-pkcs12")
	c.Header("Content-Length", fmt.Sprintf("%d", buffer.Len()))
	c.Header("Content-Transfer-Encoding", "binary")
	c.Header("X-Security-Level", securityLevel)

	// Retourner les données
	c.DataFromReader(
		http.StatusOK,
		int64(buffer.Len()),
		"application/x-pkcs12",
		buffer,
		map[string]string{"Content-Disposition": fmt.Sprintf(`attachment; filename="%s"`, filename)},
	)
}

// Web Interface
func (h *Handlers) ServeWebInterface(c *gin.Context) {
	c.HTML(http.StatusOK, "index.html", nil)
}
