from __future__ import annotations

from typing import Any

from pydantic import BaseModel, ConfigDict, Field, model_validator


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


class RipsTransaccion(RipsDomainModel):
    numDocumentoIdObligado: str
    numFactura: str
    tipoNota: str | None = None
    numNota: str | None = None

    @model_validator(mode="after")
    def validate_note_fields(self) -> RipsTransaccion:
        if bool(self.tipoNota) != bool(self.numNota):
            raise ValueError("tipoNota y numNota deben informarse juntos.")
        return self


class RipsProcedimiento(RipsDomainModel):
    codPrestador: str
    fechaInicioAtencion: str
    idMIPRES: str | None = None
    numAutorizacion: str | None = None
    codProcedimiento: str
    viaIngresoServicioSalud: str
    modalidadGrupoServicioTecSal: str
    grupoServicios: str
    codServicio: int
    finalidadTecnologiaSalud: str
    tipoDocumentoIdentificacion: str
    numDocumentoIdentificacion: str
    codDiagnosticoPrincipal: str
    codDiagnosticoRelacionado: str | None = None
    codComplicacion: str | None = None
    vrServicio: int = Field(ge=0)
    conceptoRecaudo: str
    valorPagoModerador: int = Field(ge=0)
    numFEVPagoModerador: str | None = None
    consecutivo: int = Field(ge=1)


class RipsMedicamento(RipsDomainModel):
    codPrestador: str
    numAutorizacion: str | None = None
    idMIPRES: str | None = None
    fechaDispensAdmon: str
    codDiagnosticoPrincipal: str
    codDiagnosticoRelacionado: str | None = None
    tipoMedicamento: str
    codTecnologiaSalud: str
    nomTecnologiaSalud: str | None = None
    concentracionMedicamento: float = Field(ge=0)
    unidadMedida: int = Field(ge=0)
    formaFarmaceutica: str | None = None
    unidadMinDispensa: int = Field(ge=1)
    cantidadMedicamento: float = Field(gt=0)
    diasTratamiento: int = Field(ge=0)
    tipoDocumentoIdentificacion: str
    numDocumentoIdentificacion: str
    vrUnitMedicamento: int = Field(ge=0)
    vrServicio: int = Field(ge=0)
    conceptoRecaudo: str
    valorPagoModerador: int = Field(ge=0)
    numFEVPagoModerador: str | None = None
    consecutivo: int = Field(ge=1)


class RipsHospitalizacion(RipsDomainModel):
    codPrestador: str
    viaIngresoServicioSalud: str
    fechaInicioAtencion: str
    numAutorizacion: str | None = None
    causaMotivoAtencion: str
    codDiagnosticoPrincipal: str
    codDiagnosticoPrincipalE: str
    codDiagnosticoRelacionadoE1: str | None = None
    codDiagnosticoRelacionadoE2: str | None = None
    codDiagnosticoRelacionadoE3: str | None = None
    codComplicacion: str | None = None
    condicionDestinoUsuarioEgreso: str
    codDiagnosticoCausaMuerte: str | None = None
    fechaEgreso: str
    consecutivo: int = Field(ge=1)


class RipsOtroServicio(RipsDomainModel):
    codPrestador: str
    numAutorizacion: str | None = None
    idMIPRES: str | None = None
    fechaSuministroTecnologia: str
    tipoOS: str
    codTecnologiaSalud: str
    nomTecnologiaSalud: str
    cantidadOS: float = Field(gt=0)
    tipoDocumentoIdentificacion: str | None = None
    numDocumentoIdentificacion: str | None = None
    vrUnitOS: int = Field(ge=0)
    vrServicio: int = Field(ge=0)
    conceptoRecaudo: str
    valorPagoModerador: int = Field(ge=0)
    numFEVPagoModerador: str | None = None
    consecutivo: int = Field(ge=1)

    @model_validator(mode="after")
    def validate_document_pair(self) -> RipsOtroServicio:
        if bool(self.tipoDocumentoIdentificacion) != bool(self.numDocumentoIdentificacion):
            raise ValueError(
                "tipoDocumentoIdentificacion y numDocumentoIdentificacion deben informarse juntos en otrosServicios."
            )
        return self


class RipsServiciosUsuario(RipsDomainModel):
    consultas: list[dict[str, Any]] = Field(default_factory=list)
    procedimientos: list[RipsProcedimiento] = Field(default_factory=list)
    urgencias: list[dict[str, Any]] = Field(default_factory=list)
    hospitalizacion: list[RipsHospitalizacion] = Field(default_factory=list)
    recienNacidos: list[dict[str, Any]] = Field(default_factory=list)
    medicamentos: list[RipsMedicamento] = Field(default_factory=list)
    otrosServicios: list[RipsOtroServicio] = Field(default_factory=list)

    @model_validator(mode="after")
    def validate_any_service_present(self) -> RipsServiciosUsuario:
        service_groups = (
            self.consultas,
            self.procedimientos,
            self.urgencias,
            self.hospitalizacion,
            self.recienNacidos,
            self.medicamentos,
            self.otrosServicios,
        )
        if not any(service_groups):
            raise ValueError("El usuario debe tener al menos un servicio RIPS.")
        return self


class RipsUsuario(RipsDomainModel):
    tipoDocumentoIdentificacion: str
    numDocumentoIdentificacion: str
    tipoUsuario: str
    fechaNacimiento: str
    codSexo: str
    codPaisResidencia: str
    codMunicipioResidencia: str
    codZonaTerritorialResidencia: str
    incapacidad: str
    consecutivo: int = Field(ge=1)
    codPaisOrigen: str | None = None
    servicios: RipsServiciosUsuario


class RipsPayload(RipsDomainModel):
    numDocumentoIdObligado: str
    numFactura: str
    tipoNota: str | None = None
    numNota: str | None = None
    usuarios: list[RipsUsuario] = Field(min_length=1)

    @model_validator(mode="after")
    def validate_transaction_note(self) -> RipsPayload:
        if bool(self.tipoNota) != bool(self.numNota):
            raise ValueError("tipoNota y numNota deben informarse juntos en la transacción.")
        return self
