from __future__ import annotations

from datetime import datetime

from pydantic import AliasChoices, BaseModel, ConfigDict, EmailStr, Field, field_validator


def _validate_iso_datetime(value: str) -> str:
    candidate = value.strip()
    if not candidate:
        raise ValueError("Data de abertura cannot be empty.")
    if candidate.count("T") == 0 and len(candidate) == 10:
        try:
            datetime.strptime(candidate, "%Y-%m-%d")
            return candidate
        except ValueError as exc:
            raise ValueError("Data de abertura must follow YYYY-MM-DD or ISO 8601 format.") from exc
    try:
        datetime.fromisoformat(candidate.replace("Z", "+00:00"))
        return candidate
    except ValueError as exc:
        raise ValueError("Data de abertura must follow YYYY-MM-DD or ISO 8601 format.") from exc


class EmpresaBase(BaseModel):
    model_config = ConfigDict(populate_by_name=True, extra="forbid")

    cnpj: str | None = Field(default=None, alias="cnpj", max_length=50)
    razao_social: str | None = Field(default=None, alias="razao-social", max_length=255)
    nome_fantasia: str | None = Field(default=None, alias="nome-fantasia", max_length=255)
    data_abertura: str | None = Field(default=None, alias="data-abertura")
    situacao: str | None = Field(default=None, alias="situacao", max_length=255)
    natureza_juridica: str | None = Field(default=None, alias="natureza-juridica", max_length=255)
    porte: str | None = Field(default=None, alias="porte", max_length=255)
    capital_social: float | None = Field(default=None, alias="capital-social")
    telefone: str | None = Field(default=None, alias="telefone", max_length=11)
    email: EmailStr | None = Field(default=None, alias="email")
    atividade_principal_codigo: str | None = Field(
        default=None, alias="atividade-principal-codigo", max_length=255
    )
    atividade_principal_desc: str | None = Field(
        default=None, alias="atividade-principal-desc", max_length=255
    )
    latitude: float | None = Field(default=None, alias="latitude")
    longitude: float | None = Field(default=None, alias="longitude")
    tipo_frete: str | None = Field(
        default=None,
        alias="tipoFrete",
        validation_alias=AliasChoices("tipoFrete", "tipo-frete"),
        serialization_alias="tipoFrete",
    )
    taxa_entrega: float | None = Field(
        default=None,
        alias="taxaEntrega",
        validation_alias=AliasChoices("taxaEntrega", "taxa-entrega"),
        serialization_alias="taxaEntrega",
        ge=0,
    )
    valor_frete_km: float | None = Field(
        default=None,
        alias="valorFreteKm",
        validation_alias=AliasChoices("valorFreteKm", "valor-frete-km"),
        serialization_alias="valorFreteKm",
        ge=0,
    )
    raio_entrega_km: float | None = Field(
        default=None,
        alias="raioEntregaKm",
        validation_alias=AliasChoices("raioEntregaKm", "raio-entrega-km"),
        serialization_alias="raioEntregaKm",
        ge=0,
    )
    enderecos: list[str] | None = Field(
        default=None,
        alias="enderecos",
        validation_alias=AliasChoices("enderecos", "enderecoID"),
    )

    @field_validator("cnpj", mode="before")
    @classmethod
    def _validate_cnpj(cls, value: str | None) -> str | None:
        if value is None:
            return value
        if not isinstance(value, str):
            raise TypeError("CNPJ must be provided as a string.")
        digits_only = "".join(ch for ch in value if ch.isdigit())
        if len(digits_only) != 14:
            raise ValueError("CNPJ must contain exactly 14 numeric characters.")
        return digits_only

    @field_validator("telefone", mode="before")
    @classmethod
    def _validate_telefone(cls, value: str | None) -> str | None:
        if value is None:
            return value
        if not isinstance(value, str):
            raise TypeError("Telefone must be provided as a string.")
        digits = "".join(ch for ch in value if ch.isdigit())
        if len(digits) not in {10, 11}:
            raise ValueError("Telefone must contain 10 or 11 digits including DDD.")
        return digits

    @field_validator("enderecos", mode="before")
    @classmethod
    def _validate_enderecos(cls, value: list[str] | None) -> list[str] | None:
        if value is None:
            return value
        if isinstance(value, str):
            value = [value]
        if not isinstance(value, list):
            raise TypeError("Enderecos must be provided as a list of document identifiers.")
        cleaned: list[str] = []
        for item in value:
            if not isinstance(item, str) or not item.strip():
                raise ValueError("Each endereco identifier must be a non-empty string.")
            cleaned.append(item.strip())
        return cleaned

    @field_validator("data_abertura", mode="before")
    @classmethod
    def _validate_data_abertura(cls, value: str | None) -> str | None:
        if value is None:
            return value
        if not isinstance(value, str):
            raise TypeError("Data de abertura must be provided as a string.")
        return _validate_iso_datetime(value)


class EmpresaCreate(EmpresaBase):
    cnpj: str = Field(..., alias="cnpj", max_length=50)


class EmpresaUpdate(EmpresaBase):
    pass


class EmpresaOut(EmpresaBase):
    id: str = Field(alias="id")
    cnpj: str = Field(alias="cnpj")
    created_at: str = Field(alias="createdAt")
    updated_at: str = Field(alias="updatedAt")

    model_config = ConfigDict(from_attributes=True, populate_by_name=True)


class EmpresaRawOut(BaseModel):
    """Representacao crua do documento de empresas (empresas-dados) no Appwrite."""

    model_config = ConfigDict(populate_by_name=True, extra="ignore")

    cnpj: str | None = Field(default=None, alias="cnpj")
    razao_social: str | None = Field(default=None, alias="razao-social")
    nome_fantasia: str | None = Field(default=None, alias="nome-fantasia")
    situacao: str | None = Field(default=None, alias="situacao")
    natureza_juridica: str | None = Field(default=None, alias="natureza-juridica")
    porte: str | None = Field(default=None, alias="porte")
    telefone: str | None = Field(default=None, alias="telefone")
    atividade_principal_codigo: str | None = Field(
        default=None, alias="atividade-principal-codigo"
    )
    atividade_principal_desc: str | None = Field(
        default=None, alias="atividade-principal-desc"
    )
    endereco_id: str | None = Field(default=None, alias="enderecoID")
    email: str | None = Field(default=None, alias="email")
    capital_social: float | int | str | None = Field(default=None, alias="capital-social")
    data_abertura: str | None = Field(default=None, alias="data-abertura")
    latitude: float | None = Field(default=None, alias="latitude")
    longitude: float | None = Field(default=None, alias="longitude")
    tipo_frete: str | None = Field(default=None, alias="tipoFrete")
    raio_entrega_km: float | None = Field(default=None, alias="raioEntregaKm")
    taxa_entrega: float | None = Field(default=None, alias="taxaEntrega")
    valor_frete_km: float | None = Field(default=None, alias="valorFreteKm")
    sequence: int | None = Field(default=None, alias="$sequence")
    id: str = Field(alias="$id")
    created_at: str = Field(alias="$createdAt")
    updated_at: str = Field(alias="$updatedAt")
    permissions: list[str] | None = Field(default=None, alias="$permissions")
    database_id: str | None = Field(default=None, alias="$databaseId")
    collection_id: str | None = Field(default=None, alias="$collectionId")
