"""Modele Pydantic pentru suprafata API.

Aici sunt doar formele de baza + normalizare strip/upper. Validarea completa de
continut (regex VIN, interval data, R-ODO/I-ODO -> odometruInitial, ordine
odometru) este in app.validation.
"""

from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, Field, field_validator, model_validator


class RarCredentials(BaseModel):
    """Credentiale RAR per-cerere (vin de la ROAAUTO din Oracle). NU se stocheaza."""

    email: str
    # repr=False: str(creds) / loguri care fac repr pe model NU expun parola.
    password: str = Field(..., repr=False)


class PrestatieItem(BaseModel):
    """O operatie de declarat. Contract hibrid:

    ROAAUTO poate trimite FIE `cod_prestatie` (cod RAR direct, ex. OE-1), FIE
    `cod_op_service` (cod intern ROAAUTO) + `denumire` — pe care gateway-ul le
    mapeaza in cod RAR prin operations_mapping. Cel putin unul dintre
    cod_prestatie / cod_op_service e obligatoriu (shape -> 422 daca lipsesc ambele).
    """

    cod_prestatie: str | None = Field(None, description="cod din nomenclator RAR, ex. OE-1")
    cod_op_service: str | None = Field(None, description="cod intern operatie ROAAUTO (mapat -> cod RAR)")
    denumire: str | None = Field(None, description="denumirea operatiei ROAAUTO (pentru fuzzy lookup la mapare)")

    @field_validator("cod_prestatie")
    @classmethod
    def _norm_cod(cls, v: str | None) -> str | None:
        return v.strip().upper() if v else None

    @field_validator("cod_op_service", "denumire")
    @classmethod
    def _norm_op(cls, v: str | None) -> str | None:
        return v.strip() if v else None

    @model_validator(mode="after")
    def _require_one(self) -> "PrestatieItem":
        if not self.cod_prestatie and not self.cod_op_service:
            raise ValueError("fiecare prestatie are nevoie de cod_prestatie sau cod_op_service")
        return self


class PrezentareIn(BaseModel):
    """O prezentare de declarat la RAR.

    Pydantic doar NORMALIZEAZA aici (strip/upper pe vin/nrInm). Validarea de
    continut (regex VIN, interval data, R-ODO/I-ODO, odometru) e in
    app.validation.validate_prezentare si NU resping cererea — marcheaza
    `needs_data`.
    """

    vin: str
    nr_inmatriculare: str
    data_prestatie: str  # YYYY-MM-DD
    odometru_final: str  # string per contract
    odometru_initial: str | None = None
    prestatii: list[PrestatieItem]
    sistem_reparat: str = "null"
    obs: str | None = None
    b64_image: str | None = None

    @field_validator("vin", "nr_inmatriculare")
    @classmethod
    def _norm_upper(cls, v: str) -> str:
        return v.strip().upper()

    @field_validator("data_prestatie", "odometru_final")
    @classmethod
    def _norm_strip(cls, v: str) -> str:
        return v.strip()


class PrezentareRequest(BaseModel):
    """Body pentru POST /v1/prezentari — una sau mai multe prezentari + creds RAR.

    `rar_credentials` e OPTIONAL: daca lipseste, worker-ul foloseste creds-urile RAR
    durabile salvate pe cont (per-env: `accounts.rar_creds_{env}_enc`, via POST /v1/conturi/rar-creds).
    Trimite-le explicit doar cand vrei sa suprascrii creds-urile contului pe acea cerere.
    """

    rar_credentials: RarCredentials | None = None
    prezentari: list[PrezentareIn] = Field(..., min_length=1)
    # Optional: override per-cerere al comportamentului la cod necunoscut/nemapat.
    #   True  -> respinge cererea fara enqueue (status 'error');
    #   False -> submission 'needs_mapping' (intra in editorul de mapare);
    #   None  -> se foloseste accounts.on_unmapped_error_default (implicit False).
    on_unmapped_error: bool | None = None
    # Mediul RAR tinta: 'test' | 'prod'. Absent -> default-ul contului (REQ-DEFAULT).
    rar_env: Literal["test", "prod"] | None = None


class SubmissionResult(BaseModel):
    # submission_id e None cand cererea a fost RESPINSA fara enqueue (on_unmapped_error=True).
    submission_id: int | None = None
    status: str
    id_prezentare: int | None = None
    deduped: bool = False  # True daca idempotency a intors un submission existent
    # Camp aditiv. True cand un rand `error` cu aceeasi cheie de continut a fost
    # RE-ACTIVAT (re-clasificat + creds actualizate) la resubmit. `deduped` pastreaza
    # semantica actuala (clientii vechi care testeaza `deduped` nu se sparg).
    reactivated: bool = False
    # Ecou PUR de coada (PRD 5.19 US-010): True cand randul e `queued AND held=1`
    # (tinut manual, cont Auto OFF) -> NU a plecat la RAR, asteapta eliberarea umana.
    # NU influenteaza payload/idempotenta; onestitate fata de integratorul API (invariant 5.7).
    held: bool = False
    # Mediul RAR tinta efectiv (ecou din DB / rezolvat la ingestie).
    rar_env: str = "test"
    # Raspuns ONEST pentru randuri blocate: orice status != 'queued' isi expune
    # motivul, ca integratorul sa nu trateze un needs_data/needs_mapping drept succes.
    #   erori    = validare de continut (needs_data), 3 niveluri [{field, cod, problema, cauza, fix, message}].
    #              Pe ramura on_unmapped_error='error' pastreaza COD_NEMAPAT (compat).
    #   nemapate = coduri fara mapare RAR (needs_mapping / respins), 3 niveluri + cod_op_service/denumire.
    #   motiv    = rezumat uman pe o linie (None cand status='queued').
    erori: list[dict] = []
    nemapate: list[dict] = []
    motiv: str | None = None


class PrezentariResponse(BaseModel):
    results: list[SubmissionResult]


class ValidarePrezentariRequest(BaseModel):
    """Body pentru POST /v1/prezentari/valideaza — dry-run fara enqueue."""

    rar_credentials: RarCredentials | None = None
    prezentari: list[PrezentareIn] = Field(..., min_length=1)
    on_unmapped_error: bool | None = None
    # Mediul RAR tinta: 'test' | 'prod'. Absent -> default-ul contului.
    rar_env: Literal["test", "prod"] | None = None


class ValidareResult(BaseModel):
    """Verdictul dry-run per prezentare."""

    index: int
    valid: bool
    status_estimat: str  # "queued" | "needs_data" | "needs_mapping"
    rar_env: str = "test"  # mediul RAR tinta efectiv (ecou din rezolvare)
    erori: list[dict] = []
    nemapate: list[dict] = []
    prestatii_rezolvate: list[dict] = []


class ValidareResponse(BaseModel):
    results: list[ValidareResult]
