"""
DTOs (Data Transfer Objects) para módulo de clientes y proveedores
Reemplaza schemas.py siguiendo enfoque DDD y estilo NestJS
"""

from decimal import Decimal
from typing import List, Literal, Optional

from pydantic import BaseModel, ConfigDict, Field, model_validator

from .validators import is_valid_rfc, is_valid_tax_id


# DTOs para dirección
class ClientProviderAddressDTO(BaseModel):
    """DTO para dirección de cliente/proveedor"""

    municipality: Optional[str] = Field(
        None, max_length=150, description="Municipality"
    )
    streets: Optional[str] = Field(None, max_length=100, description="Streets")
    neighborhood: Optional[str] = Field(None, max_length=40, description="Neighborhood")
    interior_number: Optional[str] = Field(
        None, max_length=20, description="Interior number"
    )
    exterior_number: Optional[str] = Field(
        None, max_length=20, description="Exterior number"
    )
    postal_code: Optional[str] = Field(None, max_length=15, description="Postal code")
    city: Optional[str] = Field(None, max_length=30, description="City")
    state: Optional[str] = Field(None, max_length=30, description="State")
    country: Optional[str] = Field(None, max_length=3, description="Country code")
    phone: Optional[str] = Field(None, max_length=30, description="Phone number")
    fax_number: Optional[str] = Field(None, max_length=30, description="Fax number")
    email: Optional[str] = Field(None, max_length=100, description="Email address")
    contact: Optional[str] = Field(None, max_length=50, description="Contact person")
    reference: Optional[str] = Field(None, max_length=250, description="Reference")

    model_config = ConfigDict(from_attributes=True)


# DTOs para programas
class ClientProviderProgramsDTO(BaseModel):
    """DTO para programas de cliente/proveedor"""

    program: Optional[str] = Field(None, max_length=7, description="Program")
    program_number: Optional[str] = Field(
        None, max_length=40, description="Program number"
    )
    prosec: Optional[str] = Field(None, max_length=8, description="PROSEC")
    prosec_authorization: Optional[str] = Field(
        None, max_length=20, description="PROSEC authorization"
    )
    secon_auth_date: Optional[int] = Field(None, description="SECON authorization date")
    manufacturer_id: Optional[str] = Field(
        None, max_length=25, description="Manufacturer ID"
    )
    broker: Optional[str] = Field(None, max_length=6, description="Broker")
    import_broker: Optional[str] = Field(
        None, max_length=6, description="Import broker"
    )
    transfer_key: Optional[str] = Field(None, max_length=8, description="Transfer key")
    secon_authorization: Optional[str] = Field(
        None, max_length=20, description="SECON authorization"
    )
    applied_proportion: Optional[Decimal] = Field(
        None, description="Applied proportion"
    )
    is_certified_company: Optional[str] = Field(
        None, max_length=1, description="Is certified company"
    )
    certified_company_registry: Optional[str] = Field(
        None, max_length=40, description="Certified company registry"
    )
    donation_auth_number: Optional[str] = Field(
        None, max_length=50, description="Donation authorization number"
    )
    ctpat_svi: Optional[str] = Field(None, max_length=100, description="CTPAT SVI")
    tax_registry_number: Optional[str] = Field(
        None, max_length=40, description="Tax registry number"
    )
    subassembly_service: Optional[int] = Field(None, description="Subassembly service")
    autse_dates: Optional[int] = Field(None, description="AUTSE dates")
    autse_number: Optional[str] = Field(
        None, max_length=300, description="AUTSE number"
    )

    model_config = ConfigDict(from_attributes=True)


# DTOs principales
class ClientProviderCreateDTO(BaseModel):
    """DTO para crear cliente/proveedor"""

    type_nat_foreign: Optional[str] = Field(
        None, max_length=1, description="Type national/foreign"
    )
    name: Optional[str] = Field(None, max_length=256, description="Name")
    short_name: Optional[str] = Field(None, max_length=10, description="Short name")
    rfc: Optional[str] = Field(None, max_length=30, description="RFC")
    curp: Optional[str] = Field(None, max_length=19, description="CURP")
    client_or_provider: Optional[Literal["client", "provider", "both"]] = Field(
        None, description="Client or provider"
    )
    linking: Optional[str] = Field(None, max_length=1, description="Linking")
    transform_subassembly: Optional[str] = Field(
        None, max_length=1, description="Transform subassembly"
    )
    extra_information: Optional[str] = Field(
        None, max_length=399, description="Extra information"
    )
    web_key: Optional[str] = Field(None, max_length=40, description="Web key")
    responsible: Optional[str] = Field(
        None, max_length=80, description="Responsible person"
    )
    position: Optional[str] = Field(None, max_length=30, description="Position")
    incoterm: Optional[str] = Field(None, max_length=19, description="Incoterm")
    is_active: Optional[bool] = Field(None, description="Enabled/Disabled status")

    # Nested DTOs
    address: Optional[ClientProviderAddressDTO] = Field(
        None, description="Address information"
    )
    programs: Optional[ClientProviderProgramsDTO] = Field(
        None, description="Programs information"
    )

    @model_validator(mode="after")
    def validate_rfc_or_tax_id_format(self):
        rfc = self.rfc
        if not rfc or not (rfc := (rfc or "").strip()):
            return self
        proc = (self.type_nat_foreign or "N").strip().upper()[:1]
        if proc == "E":
            if not is_valid_tax_id(rfc):
                raise ValueError(
                    "El TAX-ID debe tener formato: 2 dígitos, guión y resto (ej. 12-3456789). Máx 30 caracteres."
                )
        else:
            if not is_valid_rfc(rfc):
                raise ValueError(
                    "El RFC no tiene el formato correcto. Ejemplo: XAXX010101000."
                )
        return self

    model_config = ConfigDict(from_attributes=True)


class ClientProviderUpdateDTO(BaseModel):
    """DTO para actualizar cliente/proveedor"""

    type_nat_foreign: Optional[str] = Field(
        None, max_length=1, description="Type national/foreign"
    )
    name: Optional[str] = Field(None, max_length=256, description="Name")
    short_name: Optional[str] = Field(None, max_length=10, description="Short name")
    rfc: Optional[str] = Field(None, max_length=30, description="RFC")
    curp: Optional[str] = Field(None, max_length=19, description="CURP")
    client_or_provider: Optional[Literal["client", "provider", "both"]] = Field(
        None, description="Client or provider"
    )
    linking: Optional[str] = Field(None, max_length=1, description="Linking")
    transform_subassembly: Optional[str] = Field(
        None, max_length=1, description="Transform subassembly"
    )
    extra_information: Optional[str] = Field(
        None, max_length=399, description="Extra information"
    )
    web_key: Optional[str] = Field(None, max_length=40, description="Web key")
    responsible: Optional[str] = Field(
        None, max_length=80, description="Responsible person"
    )
    position: Optional[str] = Field(None, max_length=30, description="Position")
    incoterm: Optional[str] = Field(None, max_length=19, description="Incoterm")
    is_active: Optional[bool] = Field(None, description="Enabled/Disabled status")

    # Nested DTOs
    address: Optional[ClientProviderAddressDTO] = Field(
        None, description="Address information"
    )
    programs: Optional[ClientProviderProgramsDTO] = Field(
        None, description="Programs information"
    )

    @model_validator(mode="after")
    def validate_rfc_or_tax_id_format(self):
        rfc = self.rfc
        if not rfc or not (rfc := (rfc or "").strip()):
            return self
        proc = (self.type_nat_foreign or "N").strip().upper()[:1]
        if proc == "E":
            if not is_valid_tax_id(rfc):
                raise ValueError(
                    "El TAX-ID debe tener formato: 2 dígitos, guión y resto (ej. 12-3456789). Máx 30 caracteres."
                )
        else:
            if not is_valid_rfc(rfc):
                raise ValueError(
                    "El RFC no tiene el formato correcto. Ejemplo: XAXX010101000."
                )
        return self

    model_config = ConfigDict(from_attributes=True)


class ClientProviderResponseDTO(BaseModel):
    """DTO para respuesta de cliente/proveedor"""

    id: int
    type_nat_foreign: Optional[str] = None
    name: Optional[str] = None
    short_name: Optional[str] = None
    rfc: Optional[str] = None
    curp: Optional[str] = None
    client_or_provider: Optional[str] = None
    linking: Optional[str] = None
    transform_subassembly: Optional[str] = None
    extra_information: Optional[str] = None
    web_key: Optional[str] = None
    responsible: Optional[str] = None
    position: Optional[str] = None
    incoterm: Optional[str] = None
    is_active: Optional[bool] = None
    tenant_id: int
    company_id: int

    # Nested DTOs
    address: Optional[ClientProviderAddressDTO] = None
    programs: Optional[ClientProviderProgramsDTO] = None

    model_config = ConfigDict(from_attributes=True)


# DTOs para respuestas específicas
class ClientProviderBasicDTO(BaseModel):
    """DTO para información básica de cliente/proveedor"""

    client_id: str
    name: Optional[str] = None
    short_name: Optional[str] = None
    rfc: Optional[str] = None
    client_or_provider: Optional[str] = None
    is_active: Optional[bool] = None

    model_config = ConfigDict(from_attributes=True)


class ClientProviderListDTO(BaseModel):
    """DTO para lista de clientes/proveedores"""

    clients: list[ClientProviderBasicDTO]
    total: int
    page: int
    size: int

    model_config = ConfigDict(from_attributes=True)


class ClientProviderPaginatedResponseDTO(BaseModel):
    """DTO para respuesta paginada de clientes/proveedores"""

    items: List[ClientProviderResponseDTO]
    total: int
    page: int
    page_size: int

    model_config = ConfigDict(from_attributes=True)
