"""Esquemas de los datos fiscales del emisor."""

import re
from datetime import datetime

from pydantic import BaseModel, ConfigDict, computed_field, Field, field_validator

from ..catalogs.dto import TaxRegimeResponse

# RFC de persona moral (3 letras) o física (4 letras) + fecha + homoclave.
RFC_PATTERN = re.compile(r"^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$")
ZIP_PATTERN = re.compile(r"^\d{5}$")


class IssuerSettingsInput(BaseModel):
    """Alta o actualización de los datos fiscales del emisor."""

    legal_name: str = Field(..., min_length=1, max_length=255, description="Razón social")
    rfc: str = Field(..., max_length=13, description="RFC del emisor")
    tax_regime_id: int = Field(..., description="Régimen fiscal (c_RegimenFiscal)")
    zip_code: str | None = Field(None, max_length=5, description="CP del lugar de expedición")

    # mode="before": la normalización corre antes que el max_length del campo, para que
    # un RFC con espacios de sobra no se rechace por longitud antes de limpiarlo.
    @field_validator("rfc", mode="before")
    @classmethod
    def _validate_rfc(cls, value: str) -> str:
        """Normaliza a mayúsculas sin espacios y valida el formato oficial del RFC."""
        if not isinstance(value, str):
            raise ValueError("El RFC debe ser texto")
        normalized = value.replace(" ", "").replace("-", "").upper()
        if not RFC_PATTERN.match(normalized):
            raise ValueError("El RFC no tiene un formato válido (ej. XAXX010101000)")
        return normalized

    @field_validator("zip_code")
    @classmethod
    def _validate_zip(cls, value: str | None) -> str | None:
        if value is None or value == "":
            return None
        normalized = value.strip()
        if not ZIP_PATTERN.match(normalized):
            raise ValueError("El código postal debe tener 5 dígitos")
        return normalized


class IssuerSettingsResponse(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    tenant_id: int
    company_id: int
    legal_name: str
    rfc: str
    tax_regime_id: int
    tax_regime: TaxRegimeResponse | None = None
    zip_code: str | None = None
    updated_by: str | None = None
    created_at: datetime
    updated_at: datetime
    # ----- Estado del CSD -----
    # Se expone SI hay certificado cargado y cuál, nunca su contenido ni la contraseña: con el
    # .key se puede firmar a nombre de la empresa ante el SAT.
    csd_cert_number: str | None = None
    csd_uploaded_at: datetime | None = None

    @computed_field
    @property
    def has_csd(self) -> bool:
        """Hay par de archivos y contraseña guardados, o sea que ya se puede timbrar."""
        return bool(self.csd_cert_number and self.csd_uploaded_at)
