"""Configurare gateway. Env vars (prefix AUTOPASS_) + valori implicite.

NU stocheaza parole RAR. Credentialele RAR vin per-cerere de la ROAAUTO.
Helper-ul `load_test_credentials` citeste blocul <test> din settings.xml DOAR
pentru dev local / probe pe mediul de test.
"""

from __future__ import annotations

import xml.etree.ElementTree as ET
from functools import lru_cache
from pathlib import Path

from pydantic_settings import BaseSettings, SettingsConfigDict

ROOT = Path(__file__).resolve().parent.parent


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="AUTOPASS_", env_file=".env", extra="ignore")

    # --- Bază de date ---
    db_path: Path = ROOT / "data" / "autopass.db"

    # --- Observabilitate / jurnal aplicatie ---
    # Nivel minim al evenimentelor scrise in app_events + log text. Sub el, evenimentul
    # e ignorat (best-effort). DEBUG|INFO|WARNING|ERROR|CRITICAL.
    log_level: str = "INFO"
    log_retention_days: int = 90
    # Director pentru log-ul text rotativ (RotatingFileHandler in aplicatie).
    # Fisier per-proces (app-api.log / app-worker.log) — rotatia nu e multiproces-safe.
    log_dir: Path = ROOT / ".run"
    log_file_max_bytes: int = 5_000_000
    log_file_backup_count: int = 5
    # Retentie randuri blocate (error/needs_data/needs_mapping). Mai scurt decat 90z
    # ale `sent` — un blocat n-are valoare de audit.
    blocked_retention_days: int = 30
    # Prag zile (PRD 5.19 US-007): randurile tinute manual (`queued AND held=1`) mai vechi de
    # atat declanseaza bannerul de avertizare L.142 (declararea are termen legal).
    held_warn_days: int = 7
    # Retentie randuri tinute manual (PRD 5.19 US-008): un `queued AND held=1` mai vechi de atat
    # expira la `error` (`TINUT_EXPIRAT`) + purge_after direct. Aliniat retentiei `sent` (90z).
    held_retention_days: int = 90

    # --- Securitate ---
    # Enforcement auth API-key pe /v1/* protejat. False (dev/test): fara cheie ->
    # cont implicit id=1. True (prod): fara cheie valida -> 401. O cheie PREZENTA
    # dar invalida da 401 indiferent de flag.
    require_api_key: bool = False

    # Cheie Fernet pentru criptarea creds RAR efemere in submissions (zero-storage
    # at rest). Nesetata -> cheie efemera la runtime (creds nu supravietuiesc
    # restartului). In productie seteaz-o persistent. Genereaza:
    #   python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
    creds_key: str | None = None

    # --- Sesiuni web ---
    # Secret semnat cookie sesiune. None -> efemer la fiecare restart (dev ok;
    # in prod seteaza persistent ca si creds_key, altfel cookieurile se invalideaza
    # la restart). Genereaza: python -c "import secrets; print(secrets.token_hex(32))"
    session_secret: str | None = None
    # True (IMPLICIT, sigur pentru prod): rutele web fara sesiune -> redirect /login;
    # CSRF enforce. Pentru dev rapid pe contul implicit id=1,
    # seteaza explicit AUTOPASS_WEB_AUTH_REQUIRED=false.
    web_auth_required: bool = True
    # True (prod, in spatele Cloudflare Tunnel TLS): cookie cu Secure flag.
    # False (dev): cookie fara Secure, functioneaza pe HTTP.
    session_https_only: bool = False

    # --- Contact suport (US-001, PRD 5.12) ---
    # Email/canal de contact afisat in mesaje catre utilizatori (ex. CUI duplicat la signup).
    # Nesetat -> fallback la formularea generica fara canal concret.
    support_email: str | None = None

    # --- Notificare email admin la signup ---
    # Nesetat (smtp_host None) -> notificarea e DEGRADATA (doar log SIGNUP).
    smtp_host: str | None = None
    smtp_port: int = 587
    smtp_user: str | None = None
    smtp_password: str | None = None
    smtp_from: str | None = None

    # --- Rate-limit signup + login ---
    # Max cereri POST /signup per IP in fereastra de timp (in-proces, fara dependinta noua).
    signup_rate_max: int = 5
    signup_rate_window_s: int = 3600
    # Max incercari POST /login per IP (brute-force parole). Fereastra impartita cu signup.
    login_rate_max: int = 10

    # --- RAR ---
    rar_env: str = "test"  # "test" | "prod"
    rar_base_url_test: str = "https://apps.rarom.ro/test-rar-autopass"
    rar_base_url_prod: str = "https://apps.rarom.ro/rar-autopass"

    # WAF-ul RAR da 403 fara User-Agent de browser. Toate apelurile httpx il trimit.
    http_user_agent: str = "Mozilla/5.0"
    http_timeout_s: float = 30.0

    # --- Worker ---
    worker_poll_interval_s: float = 5.0
    worker_heartbeat_stale_s: int = 30  # /healthz considera worker-ul mort peste atat
    # Send DEZACTIVAT implicit (nu trimite la RAR). Activeaza-l explicit pentru
    # proba end-to-end.
    worker_send_enabled: bool = False
    # Dev: foloseste creds <test> din settings.xml pt login worker. In productie
    # creds vin per-cerere de la ROAAUTO — lasa False.
    worker_use_test_creds: bool = False
    # Keepalive RAR: cand coada e goala, worker-ul face un login de proba la fiecare
    # atata timp ca sa pastreze last_rar_login_ok proaspat (sub pragul de 30h al
    # dashboard-ului) — altfel banner-ul "RAR inaccesibil" apare fals doar din lipsa
    # de trafic. 0 = dezactivat. Implicit o data pe zi (24h < 30h, margine de 6h).
    worker_rar_keepalive_interval_s: int = 86400
    worker_sending_lease_s: int = 120     # rand 'sending' mai vechi de atat = orfan (worker mort mid-POST)
    worker_retry_base_s: int = 5          # backoff = base * 2^retry (plafonat la max)
    worker_retry_max_s: int = 300
    worker_max_retries: int = 8           # peste atat -> error + banner

    # --- Planuri de cont (PRD 5.17) ---
    # Enforcement DUR al limitelor de plan (volum + acces API). True (implicit) = activ.
    # False = kill-switch de operare: sare toate gate-urile de plan (util pentru debugging
    # sau rollback rapid fara revert de cod). Enforcement DUR e activ implicit de la deploy
    # (decizie user 2026-06-28, decizia #22 autoplan): nu exista conturi legacy, produs in TESTE.
    enforce_plans: bool = True

    # --- Embeddings (sugestie mapare, Stratul 2 PRD 5.14) ---
    # ACTIVAT implicit: editorul de mapari ofera sugestii semantice (model fastembed/ONNX).
    # Cost: prima folosire lazy-load-eaza modelul (~230MB pe disc) sincron in thread-ul de
    # cerere -> prima cerere /mapari poate dura 30-120s pana modelul intra in memorie; cererile
    # urmatoare sunt instant. SUGGESTION-ONLY: nu intra in resolve_prestatii (nu auto-trimite).
    # Pune-l pe False (start.sh/Docker/.env: AUTOPASS_EMBEDDINGS_ENABLED=false) cand vrei
    # /mapari instant la prima cerere sau suita de teste rapida (cade pe GOLD/SILVER+fuzzy).
    embeddings_enabled: bool = True

    # --- Seed corpus operatii etichetate (SILVER, PRD 5.18 US-004) ---
    # ACTIVAT implicit: la init_db, populeaza mapping_suggestions din artefactul comis
    # `app/data/operatii-etichetate.json` (INSERT OR IGNORE). Asa SILVER nu mai e gol in
    # productie -> sugestii exact-match + corpus k-NN reale. SUGGESTION-ONLY.
    # Pune-l pe False (AUTOPASS_SEED_OPERATII_ENABLED=false) cand vrei SILVER gol —
    # conftest il dezactiveaza global, testele care-l vor il pornesc punctual.
    seed_operatii_enabled: bool = True

    # --- Limita globala corp cerere (P1-4/P1-5, hardening 2026-07-03) ---
    # Plafon aplicat de un middleware ASGI pur (app/web/body_cap.py) pe TOATE rutele,
    # INAINTE ca FastAPI sa parseze body-ul (multipart e spooled pe disc altfel).
    # Verificarea per-endpoint existenta (import_parse.MAX_BYTES, 5MB) ramane strat 2.
    max_request_bytes: int = 10 * 1024 * 1024  # 10 MB

    @property
    def rar_base_url(self) -> str:
        return self.rar_base_url_prod if self.rar_env == "prod" else self.rar_base_url_test


@lru_cache
def get_settings() -> Settings:
    return Settings()


def validate_prod_invariants(settings: Settings) -> None:
    """Fail-fast la startup: refuza boot-ul in productie cand lipsesc invariantele
    de securitate obligatorii, in loc sa lase aplicatia sa porneasca intr-o
    configuratie nesigura descoperita abia dupa deploy (mirror `crypto.validate_creds_key`).

    Conditionat STRICT pe `rar_env=="prod"` — altfel ar opri suita de teste, care
    ruleaza implicit cu `rar_env="test"` si fara `session_secret`/`require_api_key`.
    """
    if settings.rar_env != "prod":
        return
    if not settings.require_api_key:
        raise RuntimeError(
            "AUTOPASS_RAR_ENV=prod dar AUTOPASS_REQUIRE_API_KEY nu e activat: in "
            "productie cheia API e obligatorie pe /v1/*, altfel orice cerere "
            "neautentificata ajunge pe contul implicit id=1. Seteaza "
            "AUTOPASS_REQUIRE_API_KEY=true."
        )
    if not settings.session_secret:
        raise RuntimeError(
            "AUTOPASS_RAR_ENV=prod dar AUTOPASS_SESSION_SECRET nu este setat: fara el, "
            "secretul de sesiune e efemer per restart si toti userii sunt delogati la "
            "fiecare redeploy. Genereaza unul persistent cu:\n"
            "  python3 -c \"import secrets; print(secrets.token_hex(32))\""
        )


def load_test_credentials(settings_xml: Path | None = None) -> dict | None:
    """Citeste credentialele <test> din settings.xml (dev local / probe test).

    Intoarce {"email", "password"} sau None daca fisierul lipseste / e template.
    NU se foloseste in productie — acolo creds vin per-cerere de la ROAAUTO.
    """
    path = settings_xml or (ROOT / "settings.xml")
    if not path.exists():
        return None
    try:
        root = ET.parse(path).getroot()
        node = root.find("./test/credentials")
        if node is None:
            return None
        email = (node.findtext("email") or "").strip()
        password = (node.findtext("password") or "").strip()
        if not email or not password or email.startswith("EMAIL_"):
            return None
        return {"email": email, "password": password}
    except ET.ParseError:
        return None
