"""Teste US-006 (PRD 5.20) — sesiuni si trimitere worker per (cont, env).

Verifica:
- AccountSessions re-cheiat pe (account_id, rar_env): doua env ale aceluiasi cont
  au sesiuni distincte.
- RarClient creat cu base_url-ul mediului (test -> rar_base_url_test,
  prod -> rar_base_url_prod), nu ancora globala.
- Creds extrase din slotul accounts.rar_creds_{env}_enc corect per env.
- Purjarea creds efemere scoped pe (account_id, rar_env): login pe test NU sterge
  creds efemere ale submission-urilor PROD ale aceluiasi cont (auto-fix E1/1a).
- recover_orphans per (cont, env): orfanii prod reconciliati contra endpoint prod,
  nu contra test (auto-fix 1b/E6).
"""

from __future__ import annotations

import json
import os
import tempfile

import pytest
from cryptography.fernet import Fernet


# ---------------------------------------------------------------------------
# Fixture DB
# ---------------------------------------------------------------------------

@pytest.fixture()
def env(monkeypatch):
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db"))
    monkeypatch.setenv("AUTOPASS_CREDS_KEY", Fernet.generate_key().decode())
    monkeypatch.setenv("AUTOPASS_WORKER_USE_TEST_CREDS", "false")
    monkeypatch.setenv("AUTOPASS_REQUIRE_API_KEY", "false")
    from app.config import get_settings
    from app import crypto
    get_settings.cache_clear()
    crypto.reset_cache()
    from app.db import get_connection, init_db
    init_db()
    conn = get_connection()
    settings = get_settings()
    yield conn, settings
    conn.close()
    get_settings.cache_clear()
    crypto.reset_cache()


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_CONTENT = {
    "vin": "WVWZZZ1KZAW000123", "nr_inmatriculare": "B999TST",
    "data_prestatie": "2026-06-15", "odometru_final": "123456",
    "prestatii": [{"cod_prestatie": "OE-1"}], "sistem_reparat": "null",
}


def _insert_sub(conn, account_id=1, rar_env="test", creds_enc=None, status="queued"):
    """Insereaza un submission cu env si creds explicite."""
    content = _CONTENT.copy()
    cur = conn.execute(
        "INSERT INTO submissions "
        "(idempotency_key, account_id, status, payload_json, rar_env, rar_creds_enc) "
        "VALUES (?, ?, ?, ?, ?, ?)",
        (f"k-{os.urandom(4).hex()}", account_id, status, json.dumps(content), rar_env, creds_enc),
    )
    return int(cur.lastrowid)


def _row(conn, sid):
    return conn.execute("SELECT * FROM submissions WHERE id=?", (sid,)).fetchone()


# Captura base_url-urilor clientilor creati de AccountSessions
_created_clients: list = []


class FakeRarClient:
    """RarClient stub care captura base_url-ul pentru assertii."""

    def __init__(self, settings=None, *, base_url=None, login_exc=None):
        self.base_url = base_url
        self._login_exc = login_exc
        self.login_calls = 0
        self.closed = False
        _created_clients.append(self)

    def login(self, email, password):
        self.login_calls += 1
        if self._login_exc:
            raise self._login_exc
        return f"TOK-{email}-{self.base_url}"

    def get_nomenclator(self, token):
        return []

    def close(self):
        self.closed = True


# ---------------------------------------------------------------------------
# test_sesiune_separata_per_env
# ---------------------------------------------------------------------------

def test_sesiune_separata_per_env(env, monkeypatch):
    """Doua submission-uri ale aceluiasi cont, env test + prod -> doua login-uri distincte.

    Cheia sesiunii e (account_id, rar_env): sesiunile test si prod sunt independente.
    """
    import app.worker.__main__ as w

    _created_clients.clear()
    monkeypatch.setattr(w, "RarClient", FakeRarClient)

    conn, settings = env
    # Cont secundar (contul 1 e default din schema)
    conn.execute("INSERT INTO accounts (id, name) VALUES (2, 'Cont2')")
    conn.commit()

    sessions = w.AccountSessions(settings)

    creds_test = {"email": "test@example.ro", "password": "ptest"}
    creds_prod = {"email": "prod@example.ro", "password": "pprod"}

    tok_test = sessions.get_token(conn, 2, creds_test, "test")
    tok_prod = sessions.get_token(conn, 2, creds_prod, "prod")

    # Doua login-uri distincte
    assert len(_created_clients) == 2
    assert _created_clients[0].login_calls == 1
    assert _created_clients[1].login_calls == 1

    # Tokenuri distincte (de la email-uri diferite)
    assert tok_test != tok_prod

    # Sesiunile active: doua intrari, ambele pt cont 2, env diferite
    active = sessions.active()
    assert len(active) == 2
    envs_active = {env for _, env, _, _ in active}
    assert envs_active == {"test", "prod"}

    # Al doilea apel cu acelasi (cont, env) -> cache, NU re-login
    tok_test2 = sessions.get_token(conn, 2, creds_test, "test")
    assert tok_test2 == tok_test
    assert len(_created_clients) == 2  # niciun client nou creat


# ---------------------------------------------------------------------------
# test_base_url_dupa_submission
# ---------------------------------------------------------------------------

def test_base_url_dupa_submission(env, monkeypatch):
    """Un submission prod foloseste rar_base_url_prod; un submission test foloseste rar_base_url_test."""
    import app.worker.__main__ as w

    _created_clients.clear()
    monkeypatch.setattr(w, "RarClient", FakeRarClient)

    conn, settings = env
    sessions = w.AccountSessions(settings)

    creds = {"email": "x@example.ro", "password": "pw"}
    sessions.get_token(conn, 1, creds, "test")
    sessions.get_token(conn, 1, creds, "prod")

    urls = {c.base_url for c in _created_clients}
    assert settings.rar_base_url_test in urls, f"URL test asteptat in {urls}"
    assert settings.rar_base_url_prod in urls, f"URL prod asteptat in {urls}"
    # Cele doua URL-uri trebuie sa fie diferite (sisteme RAR separate)
    assert settings.rar_base_url_test != settings.rar_base_url_prod


# ---------------------------------------------------------------------------
# test_creds_din_slotul_env
# ---------------------------------------------------------------------------

def test_creds_din_slotul_env(env, monkeypatch):
    """Cand submissions.rar_creds_enc lipseste, worker ia din accounts.rar_creds_{env}_enc.

    Prod ia din rar_creds_prod_enc, nu din slotul test (auto-fix 1c/E8 + fallback per-env).
    """
    import app.worker.__main__ as w
    from app.crypto import encrypt_creds

    conn, settings = env

    enc_test = encrypt_creds({"email": "test@rar.ro", "password": "ptest"})
    enc_prod = encrypt_creds({"email": "prod@rar.ro", "password": "pprod"})

    # Salveaza creds in ambele sloturi per-env
    conn.execute(
        "UPDATE accounts SET rar_creds_test_enc=?, rar_creds_prod_enc=? WHERE id=1",
        (enc_test, enc_prod),
    )
    conn.commit()

    # Fara creds efemere pe submission -> fallback la slotul per-env
    creds_test = w._creds_from_account(conn, 1, "test")
    creds_prod = w._creds_from_account(conn, 1, "prod")

    assert creds_test is not None, "slotul test trebuia sa aiba creds"
    assert creds_test["email"] == "test@rar.ro"

    assert creds_prod is not None, "slotul prod trebuia sa aiba creds"
    assert creds_prod["email"] == "prod@rar.ro"

    # Crucialmente: prod NU ia creds din slotul test
    assert creds_prod["email"] != creds_test["email"]


# ---------------------------------------------------------------------------
# test_purge_creds_doar_pe_env (auto-fix E1/1a)
# ---------------------------------------------------------------------------

def test_purge_creds_doar_pe_env(env, monkeypatch):
    """Dupa login pe env=test, creds efemere ale submission-urilor PROD raman neatinse.

    Scopul purjarii: WHERE account_id=? AND rar_env=?. Altfel un login TEST sterge
    creds ale submission-urilor PROD -> prod blocat (auto-fix E1/1a).
    """
    import app.worker.__main__ as w
    from app.crypto import encrypt_creds

    _created_clients.clear()
    monkeypatch.setattr(w, "RarClient", FakeRarClient)

    conn, settings = env

    enc = encrypt_creds({"email": "u@rar.ro", "password": "pw"})

    # Doua submission-uri ale aceluiasi cont: unul test, unul prod (ambele cu creds efemere)
    sid_test = _insert_sub(conn, account_id=1, rar_env="test", creds_enc=enc)
    sid_prod = _insert_sub(conn, account_id=1, rar_env="prod", creds_enc=enc)

    sessions = w.AccountSessions(settings)

    # Login pe env=test
    sessions.get_token(conn, 1, {"email": "u@rar.ro", "password": "pw"}, "test")

    # Creds efemere ale submission-ului TEST trebuie sterse (purjare normala)
    row_test = _row(conn, sid_test)
    assert row_test["rar_creds_enc"] is None, "creds test trebuiau sterse dupa login test"

    # Creds efemere ale submission-ului PROD trebuie PASTRATE (nu sunt pentru env=test)
    row_prod = _row(conn, sid_prod)
    assert row_prod["rar_creds_enc"] is not None, \
        "creds prod NU trebuiau sterse la login test (auto-fix E1/1a)"


# ---------------------------------------------------------------------------
# test_reconcile_pe_env_corect (auto-fix 1b/E6)
# ---------------------------------------------------------------------------

def test_reconcile_pe_env_corect(env, monkeypatch):
    """Un orfan env=prod e reconciliat contra endpoint PROD, nu contra test.

    auto-fix 1b/E6: recover_orphans filtreaza pe rar_env si foloseste clientul/token-ul
    env-ului corect. Orfanii prod contra endpoint test -> no-match -> re-POST prod =
    DUPLICAT real ireversibil.
    """
    import app.worker.__main__ as w

    conn, settings = env

    # Submission prod orfan (sending de mult timp)
    sid_prod = _insert_sub(conn, account_id=1, rar_env="prod", status="sending")
    conn.execute(
        "UPDATE submissions SET sending_since=datetime('now', '-1 hour') WHERE id=?", (sid_prod,)
    )
    conn.commit()

    # Submission test orfan (de verificat ca NU e atins de recover_orphans(rar_env='prod'))
    sid_test = _insert_sub(conn, account_id=1, rar_env="test", status="sending")
    conn.execute(
        "UPDATE submissions SET sending_since=datetime('now', '-1 hour') WHERE id=?", (sid_test,)
    )
    conn.commit()

    # Clientul prod fake — "gaseste" prezentarea prod la RAR
    class FakeProdRar:
        def __init__(self):
            self.get_finalizate_calls = 0

        def get_finalizate(self, token):
            self.get_finalizate_calls += 1
            return [{"id": 9999, "vin": "WVWZZZ1KZAW000123",
                     "dataPrestatie": "2026-06-15", "odometruFinal": 123456}]

        def post_prezentare(self, token, payload):
            return {"id": 9999}

    # Clientul test fake — nu gaseste nimic (sistemul test nu are prezentarea)
    class FakeTestRar:
        def __init__(self):
            self.get_finalizate_calls = 0

        def get_finalizate(self, token):
            self.get_finalizate_calls += 1
            return []  # nu e la RAR test

        def post_prezentare(self, token, payload):
            return {"id": 1111}

    rar_prod = FakeProdRar()
    rar_test = FakeTestRar()

    # Apelam recover_orphans cu clientul PROD si env='prod' -> trebuie sa gaseasca orfanul prod
    n = w.recover_orphans(conn, settings, rar_prod, "tok-prod", account_id=1, rar_env="prod")
    assert n == 1, f"trebuia sa reconcilieze 1 orfan prod, a gasit {n}"

    row_prod = _row(conn, sid_prod)
    assert row_prod["status"] == "sent", f"orfanul prod trebuia marcat sent, e {row_prod['status']}"
    assert row_prod["id_prezentare"] == 9999

    # Submission-ul TEST nu trebuia atins de recover_orphans cu rar_env='prod'
    row_test = _row(conn, sid_test)
    assert row_test["status"] == "sending", \
        f"orfanul test NU trebuia atins de recover cu env=prod, e {row_test['status']}"

    # Confirmare ca clientul prod a interogat finalizate (reconciliere pe endpoint corect)
    assert rar_prod.get_finalizate_calls == 1
    # Clientul test NU trebuia folosit (recover_orphans cu env=prod NU atinge endpoint test)
    assert rar_test.get_finalizate_calls == 0
