package verifactu

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"VerifactuMidAPI/internal/cert"
)

type Client struct {
	BaseURL     string
	HTTPClient  *http.Client
	Certificate *tls.Certificate
}

type ClientConfig struct {
	BaseURL             string
	Timeout             time.Duration
	CertificatePath     string
	CertificatePassword string
}

func NewClient(cfg ClientConfig) (*Client, error) {
	httpClient := &http.Client{
		Timeout: cfg.Timeout,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: false,
			},
		},
	}

	var tlsCert *tls.Certificate
	if cfg.CertificatePath != "" {
		c, err := LoadCertificate(cfg.CertificatePath, cfg.CertificatePassword)
		if err != nil {
			return nil, fmt.Errorf("loading certificate: %w", err)
		}
		tlsCert = c
	}

	return &Client{
		BaseURL:     cfg.BaseURL,
		HTTPClient:  httpClient,
		Certificate: tlsCert,
	}, nil
}

func LoadCertificate(certPath, password string) (*tls.Certificate, error) {
	der, err := os.ReadFile(certPath)
	if err != nil {
		return nil, fmt.Errorf("reading cert file: %w", err)
	}

	return LoadCertificateFromBytes(der, password)
}

func LoadCertificateFromBytes(der []byte, password string) (*tls.Certificate, error) {
	return parseP12(der, password)
}

func parseP12(der []byte, password string) (*tls.Certificate, error) {
	key, x509Cert, chain, err := cert.DecodeLeafAndKey(der, password)
	if err != nil {
		return nil, fmt.Errorf("decoding PKCS#12: %w", err)
	}

	if x509Cert == nil {
		return nil, fmt.Errorf("no certificate found in PKCS#12")
	}

	tlsCert := &tls.Certificate{
		Certificate: [][]byte{x509Cert.Raw},
		PrivateKey:  key,
		Leaf:        x509Cert,
	}
	for _, c := range chain {
		tlsCert.Certificate = append(tlsCert.Certificate, c.Raw)
	}

	log.Printf("cert loaded: subject=%s, expires=%s, chain=%d", x509Cert.Subject.CommonName, x509Cert.NotAfter.Format("2006-01-02"), len(chain))

	return tlsCert, nil
}

func (c *Client) SetCertificate(certPath, password string) error {
	tlsCert, err := LoadCertificate(certPath, password)
	if err != nil {
		return err
	}
	c.Certificate = tlsCert
	return nil
}

func (c *Client) SendAlta(data AltaData) (*Response, error) {
	env, err := BuildAltaSOAPRequest(data)
	if err != nil {
		return nil, fmt.Errorf("building Alta request: %w", err)
	}

	if xmlBytes, err2 := env.ToBytes(); err2 == nil {
		log.Printf("[SOAP-Alta] XML generado (%d bytes):\n%s", len(xmlBytes), string(xmlBytes))
	}

	return c.SendRequest(env)
}

func (c *Client) SendAnulacion(data AltaData) (*Response, error) {
	env, err := BuildAnulacionSOAPRequest(data)
	if err != nil {
		return nil, fmt.Errorf("building Anulacion request: %w", err)
	}

	return c.SendRequest(env)
}

func (c *Client) SendRequest(env *SOAPEnvelope) (*Response, error) {
	body, err := env.ToBytes()
	if err != nil {
		return nil, fmt.Errorf("marshaling request: %w", err)
	}

	req, err := http.NewRequest("POST", c.BaseURL, bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("creating request: %w", err)
	}

	req.Header.Set("Content-Type", "text/xml; charset=utf-8")
	req.Header.Set("SOAPAction", "")

	if c.Certificate != nil {
		log.Printf("Using client certificate with TLS")
		tlsConfig := &tls.Config{
			Certificates:       []tls.Certificate{*c.Certificate},
			InsecureSkipVerify: true,
			MinVersion:         tls.VersionTLS12,
			MaxVersion:         tls.VersionTLS12,
		}
		c.HTTPClient.Transport = &http.Transport{
			TLSClientConfig: tlsConfig,
		}
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("sending request: %w", err)
	}
	defer resp.Body.Close()

	log.Printf("AEAT response status: %d", resp.StatusCode)

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading response: %w", err)
	}

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP error: %d - %s", resp.StatusCode, string(respBody))
	}

	response, err := ParseResponse(respBody)
	if err != nil {
		log.Printf("[AEAT] Respuesta no es SOAP:\n%s", string(respBody))
		return nil, fmt.Errorf("parsing response: %w", err)
	}

	return response, nil
}

// CertSubject extrae el NIF y nombre del emisor del certificado cargado.
func (c *Client) CertSubject() (nif, nombre string) {
	if c.Certificate == nil || c.Certificate.Leaf == nil {
		return "", ""
	}
	subj := c.Certificate.Leaf.Subject

	nif = normalizeNIF(subj.SerialNumber)

	// Fallback: buscar NIF: en el CommonName (ej. ACCV: "NOMBRE - NIF:12345678A")
	if nif == "" {
		nif = extractNIFFromCN(subj.CommonName)
	}

	if len(subj.Organization) > 0 {
		nombre = subj.Organization[0]
	} else {
		// Usar la parte del CN antes del separador, sin el NIF
		nombre = cleanNombre(subj.CommonName)
	}
	return
}

func normalizeNIF(serial string) string {
	s := strings.ToUpper(strings.TrimSpace(serial))
	for _, prefix := range []string{"IDCES-", "VATES-", "ES"} {
		if strings.HasPrefix(s, prefix) {
			return s[len(prefix):]
		}
	}
	return s
}

// extractNIFFromCN busca patrones como "NIF:12345678A" o "- 12345678A" en el CN.
func extractNIFFromCN(cn string) string {
	upper := strings.ToUpper(cn)
	// Patrón "NIF:XXXXXXXXX"
	if idx := strings.Index(upper, "NIF:"); idx != -1 {
		rest := strings.TrimSpace(cn[idx+4:])
		end := strings.IndexAny(rest, " ,;)")
		if end == -1 {
			end = len(rest)
		}
		return strings.ToUpper(rest[:end])
	}
	return ""
}

// cleanNombre devuelve el CN sin el fragmento del NIF.
func cleanNombre(cn string) string {
	if idx := strings.Index(strings.ToUpper(cn), " - NIF:"); idx != -1 {
		return strings.TrimSpace(cn[:idx])
	}
	return cn
}

func (c *Client) SetRootCA(caPath string) error {
	caData, err := os.ReadFile(caPath)
	if err != nil {
		return fmt.Errorf("reading CA file: %w", err)
	}

	pool := x509.NewCertPool()
	pool.AppendCertsFromPEM(caData)

	if transport, ok := c.HTTPClient.Transport.(*http.Transport); ok {
		if transport.TLSClientConfig == nil {
			transport.TLSClientConfig = &tls.Config{}
		}
		transport.TLSClientConfig.RootCAs = pool
	}

	return nil
}

func GetTestURL() string {
	return "https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"
}

func GetProdURL() string {
	return "https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"
}
