package api

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"

	"VerifactuMidAPI/internal"
	"VerifactuMidAPI/internal/cert"
	"VerifactuMidAPI/internal/config"
	"VerifactuMidAPI/internal/crypto"
	"VerifactuMidAPI/internal/formats"
)

type RegisterInput struct {
	CertName          string `json:"cert_name"`
	CertNamePascal    string `json:"CertName"`
	CertFile          string `json:"cert_file"`
	CertFilePascal    string `json:"CertFile"`
	PasswordEncrypted string `json:"password_encrypted"`
	PasswordPascal    string `json:"PasswordEncrypted"`
}

func (r RegisterInput) CertNameResolved() string {
	if r.CertName != "" {
		return r.CertName
	}
	return r.CertNamePascal
}

func (r RegisterInput) CertFileResolved() string {
	if r.CertFile != "" {
		return r.CertFile
	}
	return r.CertFilePascal
}

func (r RegisterInput) PasswordResolved() string {
	if r.PasswordEncrypted != "" {
		return r.PasswordEncrypted
	}
	return r.PasswordPascal
}

type Handler struct {
	cfg        *config.Config
	cert       *cert.Storage
	crypto     *crypto.KeyPair
	facturaSvc *internal.FacturaService
}

func New(cfg *config.Config, certStorage *cert.Storage, keyPair *crypto.KeyPair, facturaSvc *internal.FacturaService) *Handler {
	return &Handler{
		cfg:        cfg,
		cert:       certStorage,
		crypto:     keyPair,
		facturaSvc: facturaSvc,
	}
}

func (h *Handler) GetPublicKey(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	pubPEM, err := h.crypto.PublicKeyPEM()
	if err != nil {
		http.Error(w, "failed to get public key", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(fmt.Sprintf(`{"public_key":"%s"}`, base64.StdEncoding.EncodeToString(pubPEM))))
}

func (h *Handler) RegisterCert(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "failed to read body", http.StatusBadRequest)
		return
	}

	var input RegisterInput
	if err := json.Unmarshal(body, &input); err != nil {
		log.Printf("[RegisterCert] ERROR: JSON invalido: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"invalid_json"}`))
		return
	}

	certName := input.CertNameResolved()
	certFileB64 := input.CertFileResolved()
	passwordEnc := input.PasswordResolved()

	log.Printf("[RegisterCert] Inicio: cert_name=%q, cert_file_len=%d, password_len=%d",
		certName, len(certFileB64), len(passwordEnc))

	if certName == "" || certFileB64 == "" || passwordEnc == "" {
		log.Printf("[RegisterCert] ERROR: campos vacios")
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"missing_fields"}`))
		return
	}

	certBytes, err := base64.StdEncoding.DecodeString(certFileB64)
	if err != nil {
		log.Printf("[RegisterCert] ERROR: base64 del cert invalido: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"invalid_cert_file"}`))
		return
	}
	log.Printf("[RegisterCert] Certificado decodificado: %d bytes", len(certBytes))

	decodedPass, err := base64.StdEncoding.DecodeString(passwordEnc)
	if err != nil {
		log.Printf("[RegisterCert] ERROR: base64 contrasena invalido: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"invalid_password_encrypted"}`))
		return
	}

	plainPassBytes, err := h.crypto.Decrypt(decodedPass)
	if err != nil {
		log.Printf("[RegisterCert] ERROR: descifrado RSA fallido: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"decrypt_failed"}`))
		return
	}
	log.Printf("[RegisterCert] Contrasena descifrada correctamente")

	plainPass := string(plainPassBytes)

	log.Printf("[RegisterCert] Validando certificado P12...")
	validation := cert.ValidateP12Bytes(certBytes, plainPass)
	if !validation.Valid {
		log.Printf("[RegisterCert] ERROR validacion: %s", validation.Error)
		resp, _ := json.Marshal(map[string]interface{}{
			"success": false,
			"error":   validation.Error,
			"cert":    validation.CertInfo,
		})
		w.Header().Set("Content-Type", "application/json")
		w.Write(resp)
		return
	}
	log.Printf("[RegisterCert] Certificado valido. Dias hasta expiracion: %d, warnings: %v",
		validation.CertInfo.DaysUntilExpiry, validation.Warnings)

	tempPath, err := h.cert.StoreFromBase64(certName, certFileB64)
	if err != nil {
		log.Printf("[RegisterCert] ERROR StoreFromBase64: %v", err)
		h.cert.DeleteTemp(tempPath)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"temp_storage_failed"}`))
		return
	}
	log.Printf("[RegisterCert] Certificado guardado en temporal: %s", tempPath)

	storedPath, err := h.cert.MoveToPerm(certName, tempPath)
	if err != nil {
		log.Printf("[RegisterCert] ERROR MoveToPerm: %v", err)
		h.cert.DeleteTemp(tempPath)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"storage_failed"}`))
		return
	}
	log.Printf("[RegisterCert] Certificado almacenado definitivamente: %s", storedPath)

	tokenData, err := h.cert.GenerateToken(certName, storedPath, plainPass)
	if err != nil {
		log.Printf("[RegisterCert] ERROR GenerateToken: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"token_generation_failed"}`))
		return
	}
	log.Printf("[RegisterCert] Token generado: %s", tokenData.Token)

	if err := h.facturaSvc.ReloadCertificate(storedPath, plainPass); err != nil {
		log.Printf("[RegisterCert] WARNING: no se pudo recargar el certificado en el cliente VeriFactu: %v", err)
	} else {
		log.Printf("[RegisterCert] Certificado recargado en el cliente VeriFactu")
	}

	if len(validation.Warnings) > 0 {
		log.Printf("[RegisterCert] OK con warnings: %v", validation.Warnings)
		resp, _ := json.Marshal(map[string]interface{}{
			"success":  true,
			"cert":     validation.CertInfo,
			"token":    tokenData.Token,
			"warnings": validation.Warnings,
		})
		w.Header().Set("Content-Type", "application/json")
		w.Write(resp)
		return
	}

	log.Printf("[RegisterCert] OK: certificado %q registrado", certName)
	resp, _ := json.Marshal(map[string]interface{}{
		"success": true,
		"cert":    validation.CertInfo,
		"token":   tokenData.Token,
	})
	w.Header().Set("Content-Type", "application/json")
	w.Write(resp)
}

func (h *Handler) HandleFacturas(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "failed to read body", http.StatusBadRequest)
		return
	}

	log.Printf("facturas request: %s", string(body))

	output, _ := h.facturaSvc.ProcessAlta(body)

	log.Printf("facturas output:%+v", output)

	resp, _ := json.Marshal(output)
	w.Header().Set("Content-Type", "application/json")
	w.Write(resp)
}

func (h *Handler) HandleFacturasAnular(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "failed to read body", http.StatusBadRequest)
		return
	}

	var input internal.AnulacionInput
	if err := json.Unmarshal(body, &input); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"success":false,"error":"invalid_json"}`))
		return
	}

	output, _ := h.facturaSvc.ProcessAnulacion(input)

	resp, _ := json.Marshal(output)
	w.Header().Set("Content-Type", "application/json")
	w.Write(resp)
}

func (h *Handler) ListFormats(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	available := formats.Available()
	resp, _ := json.Marshal(map[string]interface{}{
		"formats": available,
	})
	w.Header().Set("Content-Type", "application/json")
	w.Write(resp)
}
