"""Client RAR AUTOPASS — portare din rar_autopass.prg / rar-forms.prg.

Sursa de adevar pentru contract: docs/api-rar-contract.md (verificat live 2026-06-15).
Reguli care guverneaza acest client:
  - TOATE apelurile trimit header User-Agent (altfel WAF da 403).
  - login -> JWT (TTL 30h); token-ul se ataseaza ca `Authorization: Bearer`.
  - postPrezentare: status mereu "FINALIZATA"; NU se trimite tipPrestatie.
  - nomenclator: GET /nomenclator/getNomenclatorPrestatii (NU getPrestatiiNom -> 403).
  - eroare validare RAR: HTTP 400, data = listă [{field, message}] (NU data.message).
"""

from __future__ import annotations

from typing import Any

import httpx

from .config import Settings, get_settings


class RarError(Exception):
    """Eroare la apel RAR. `status_code` = HTTP RAR; `field_errors` = lista [{field,message}] la 400.

    `rar_message` = mesajul din envelope-ul de eroare al RAR (`{statusCode, message, data}`),
    cand exista. Prezenta lui pe un 5xx inseamna ca RAR A RASPUNS definitiv „am esuat"
    (nu o pierdere de raspuns) -> worker-ul il trateaza ca permanent, nu reconciliaza.
    """

    def __init__(
        self,
        message: str,
        *,
        status_code: int | None = None,
        field_errors: list[dict] | None = None,
        rar_message: str | None = None,
    ):
        super().__init__(message)
        self.status_code = status_code
        self.field_errors = field_errors or []
        self.rar_message = rar_message


class RarAuthError(RarError):
    """Login esuat (401 / credentiale invalide). NU se face retry."""


def base_url_pentru_env(settings: "Settings", env: str) -> str:
    """URL de baza al mediului RAR: 'prod' -> rar_base_url_prod, altfel rar_base_url_test."""
    return settings.rar_base_url_prod if env == "prod" else settings.rar_base_url_test


class RarClient:
    """Client sincron httpx. Folosit din worker (proces separat).

    Utilizare:
        with RarClient() as rar:
            token = rar.login(email, password)
            data = rar.post_prezentare(token, payload)
    """

    def __init__(self, settings: Settings | None = None, *, base_url: str | None = None):
        self.settings = settings or get_settings()
        self._client = httpx.Client(
            base_url=base_url if base_url is not None else self.settings.rar_base_url,
            timeout=self.settings.http_timeout_s,
            headers={"User-Agent": self.settings.http_user_agent},  # fix WAF 403
        )

    def __enter__(self) -> "RarClient":
        return self

    def __exit__(self, *exc: object) -> None:
        self.close()

    def close(self) -> None:
        self._client.close()

    # --- Autentificare ---

    def login(self, email: str, password: str) -> str:
        """POST /public/login -> JWT (str). Ridica RarAuthError la 401."""
        resp = self._client.post("/public/login", json={"email": email, "password": password})
        if resp.status_code == 401:
            raise RarAuthError("Credentiale RAR invalide", status_code=401)
        if resp.status_code != 200:
            raise RarError(f"Login esuat (HTTP {resp.status_code})", status_code=resp.status_code)
        token = resp.json().get("token")
        if not token:
            raise RarError("Login fara token in raspuns", status_code=resp.status_code)
        return token

    # --- Nomenclator ---

    def get_nomenclator(self, token: str) -> list[dict]:
        """GET /nomenclator/getNomenclatorPrestatii -> listă coduri prestatii."""
        resp = self._client.get(
            "/nomenclator/getNomenclatorPrestatii",
            headers={"Authorization": f"Bearer {token}"},
        )
        if resp.status_code != 200:
            raise RarError(f"Nomenclator esuat (HTTP {resp.status_code})", status_code=resp.status_code)
        data = resp.json()
        # Raspunsul poate fi listă directa sau {data: [...]}; normalizam.
        return data.get("data", data) if isinstance(data, dict) else data

    # --- Prezentari ---

    def post_prezentare(self, token: str, payload: dict[str, Any]) -> dict:
        """POST /prezentari/postPrezentare. Intoarce `data` (obiect) la succes.

        La 400 (validare) ridica RarError cu field_errors din `data` (listă).
        Apelantul NU trebuie sa includa tipPrestatie; status trebuie "FINALIZATA".
        """
        resp = self._client.post(
            "/prezentari/postPrezentare",
            json=payload,
            headers={"Authorization": f"Bearer {token}"},
        )
        body = _safe_json(resp)
        if resp.status_code == 200:
            return body.get("data", {}) if isinstance(body, dict) else {}
        if resp.status_code == 400 and isinstance(body, dict):
            errors = body.get("data") if isinstance(body.get("data"), list) else []
            msg = body.get("message", "Validare esuata la RAR")
            raise RarError(msg, status_code=400, field_errors=errors)
        # Non-200/non-400: pastram mesajul din envelope-ul RAR daca exista (ex. 500 cu
        # `{"statusCode":500,"message":"Eroare la adaugarea prezentarii : ORA-..."}`).
        rar_message = body.get("message") if isinstance(body, dict) else None
        raise RarError(
            f"postPrezentare esuat (HTTP {resp.status_code})",
            status_code=resp.status_code,
            rar_message=rar_message,
        )

    def get_finalizate(self, token: str) -> list[dict]:
        """Lista prezentarilor finalizate (pentru reconciliere — T2).

        GET /prezentari/getAllPrezentariFinalizate -> data.content (listă).
        Verificat live: filtrele/paginarea NU functioneaza pe test (vezi contract),
        deci interogam fara parametri si filtram client-side. Pe test `prestatii`
        vine null in fiecare item — match-ul se face pe vin+dataPrestatie+odometruFinal.
        """
        resp = self._client.get(
            "/prezentari/getAllPrezentariFinalizate",
            headers={"Authorization": f"Bearer {token}"},
        )
        if resp.status_code != 200:
            raise RarError(f"getFinalizate esuat (HTTP {resp.status_code})", status_code=resp.status_code)
        body = _safe_json(resp)
        if isinstance(body, dict):
            data = body.get("data")
            if isinstance(data, dict) and isinstance(data.get("content"), list):
                return data["content"]
            if isinstance(data, list):
                return data
        return []


def _safe_json(resp: httpx.Response) -> Any:
    try:
        return resp.json()
    except ValueError:
        return {"message": resp.text}
