"""Medii RAR per cont (PRD 5.20): disponibilitate + default efectiv.

Sursa UNICA de adevar pentru REQ-DISP / REQ-DEFAULT: vizibilitatea selector/toggle
in UI, validarea tintei in API si decizia worker-ului citesc TOATE de aici, ca sa
decida identic.

Un mediu ('test'|'prod') e *disponibil* pentru un cont daca e activat (bifa) SI are
credentiale (slot per-mediu non-gol). Din disponibilitate decurge tot UX-ul:
  - 0 medii  -> nicio tinta; trimiterea web e blocata, API cade pe ancora globala.
  - 1 mediu  -> tinta implicita (acel mediu), fara selector.
  - 2 medii  -> selector la import + toggle in statusbar + alegere in API.

Functii PURE (fara DB) peste un rand de cont (sqlite3.Row sau dict). Helperele cu
`conn` incarca randul si deleaga.
"""

from __future__ import annotations

import sqlite3
from typing import Any

VALID_ENVS: tuple[str, str] = ("test", "prod")


def _field(account: Any, key: str, default: Any = None) -> Any:
    """Citire toleranta a unui camp de cont (dict sau sqlite3.Row, camp posibil absent)."""
    if account is None:
        return default
    if isinstance(account, dict):
        return account.get(key, default)
    try:
        return account[key]  # sqlite3.Row
    except (IndexError, KeyError):
        return default


def _are_creds(account: Any, env: str) -> bool:
    creds = _field(account, f"rar_creds_{env}_enc", None)
    return bool(creds and str(creds).strip())


def _enabled(account: Any, env: str) -> bool:
    return int(_field(account, f"rar_{env}_enabled", 0) or 0) == 1


def medii_disponibile(account: Any) -> list[str]:
    """Subset din ('test','prod') = activat AND creds prezente. Ordine stabila test<prod."""
    return [env for env in VALID_ENVS if _enabled(account, env) and _are_creds(account, env)]


def rar_env_efectiv(account: Any) -> str | None:
    """Mediul tinta implicit al contului (REQ-DEFAULT).

    Mereu unul din mediile disponibile: default-ul contului daca inca e disponibil,
    altfel singurul disponibil; daca 0 disponibile -> None (nicio tinta).
    """
    disp = medii_disponibile(account)
    if not disp:
        return None
    default = _field(account, "rar_env_default", "prod")
    if default in disp:
        return default
    return disp[0]


# --------------------------------------------------------------------------- #
# Helpere cu conexiune                                                         #
# --------------------------------------------------------------------------- #

_ACCOUNT_ENV_COLS = (
    "id, rar_test_enabled, rar_prod_enabled, "
    "rar_creds_test_enc, rar_creds_prod_enc, rar_env_default"
)


def load_account_env(conn: sqlite3.Connection, account_id: int) -> sqlite3.Row | None:
    """Randul de cont cu exact coloanele de mediu (pentru medii_disponibile/rar_env_efectiv)."""
    from .mapping import account_or_default

    return conn.execute(
        f"SELECT {_ACCOUNT_ENV_COLS} FROM accounts WHERE id=?",
        (account_or_default(account_id),),
    ).fetchone()


def medii_disponibile_cont(conn: sqlite3.Connection, account_id: int) -> list[str]:
    return medii_disponibile(load_account_env(conn, account_id))


def rar_env_efectiv_cont(conn: sqlite3.Connection, account_id: int) -> str | None:
    return rar_env_efectiv(load_account_env(conn, account_id))


# --------------------------------------------------------------------------- #
# Exceptie si rezolvator de mediu tinta (US-004, dependent de US-002)          #
# --------------------------------------------------------------------------- #

class MediuIndisponibil(Exception):
    """Mediul RAR cerut e valid dar nu e disponibil pentru contul dat.

    Atribute
    --------
    env:        mediul cerut (ex. 'test')
    disponibile: lista mediilor disponibile pentru cont in momentul erorii
    """

    def __init__(self, env: str, disponibile: list[str]) -> None:
        self.env = env
        self.disponibile = disponibile
        super().__init__(
            f"mediu indisponibil: {env!r} (disponibile: {disponibile!r})"
        )


def rezolva_rar_env(
    conn: sqlite3.Connection,
    account_id: int,
    cerut: str | None = None,
) -> str:
    """Determina mediul RAR tinta pentru un submission la ingestie.

    Precedenta stricta (de la cea mai mare la cea mai mica):
      1. `cerut` explicit si disponibil -> intoarce `cerut`.
      2. `cerut` explicit dar indisponibil -> ridica MediuIndisponibil.
      3. `cerut` invalid (nu in VALID_ENVS) -> ridica ValueError (fara fallback silentios).
      4. `cerut` None -> incearca rar_env_efectiv_cont (default-ul contului).
      5. Daca contul nu are niciun mediu disponibil (rar_env_efectiv_cont == None)
         -> cade pe ancora globala get_settings().rar_env, normalizata la VALID_ENVS.
         Acest fallback e intentionat (PRD 5.20 §2 Non-Goals): AUTOPASS_RAR_ENV ramane
         ancora de migrare si fallback pentru actiuni fara cont (keepalive, canal API cu
         creds efemere pe conturi nou-create fara medii configurate).

    Ridica
    ------
    ValueError        -- `cerut` nu e in VALID_ENVS
    MediuIndisponibil -- `cerut` e valid dar nu e disponibil pentru cont
    """
    if cerut is not None:
        if cerut not in VALID_ENVS:
            raise ValueError(f"mediu invalid: {cerut!r}")
        disp = medii_disponibile_cont(conn, account_id)
        if cerut not in disp:
            raise MediuIndisponibil(cerut, disp)
        return cerut

    # cerut e None: incearca default-ul contului
    efectiv = rar_env_efectiv_cont(conn, account_id)
    if efectiv is not None:
        return efectiv

    # Ancora globala: 0 medii disponibile pe cont -> fallback la AUTOPASS_RAR_ENV.
    from .config import get_settings
    global_env = get_settings().rar_env
    if global_env in VALID_ENVS:
        return global_env
    return "test"  # rar_env invalid in config -> cel mai sigur default
