"""Teste US-010 (PRD 3.3b): rol admin + bootstrap + guard require_admin.

Fisiere testate: app/users.py (helper-e admin), app/web/session.py (require_admin),
tools/account.py (subcomanda set-admin).
"""

from __future__ import annotations

import os
import tempfile

import pytest
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.middleware.sessions import SessionMiddleware
from starlette.testclient import TestClient


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

@pytest.fixture()
def conn(monkeypatch):
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "test_admin_role.db"))
    from app.config import get_settings
    get_settings.cache_clear()
    from app.db import get_connection, init_db
    init_db()
    c = get_connection()
    yield c
    c.close()
    get_settings.cache_clear()


@pytest.fixture()
def account_id(conn):
    """Cont de test (nu default id=1)."""
    from app.accounts import create_account
    return create_account(conn, "Service Test Admin")


@pytest.fixture()
def user_id(conn, account_id):
    """User de test pe contul de test."""
    from app.users import create_user
    return create_user(conn, account_id, "admin_test@exemplu.ro", "parola_sigura_123")


@pytest.fixture()
def env_cli(monkeypatch):
    """Fixture pentru CLI: DB separata + clear settings cache."""
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "test_admin_cli.db"))
    from app.config import get_settings
    get_settings.cache_clear()
    from app.db import init_db
    init_db()
    yield
    get_settings.cache_clear()


# ---------------------------------------------------------------------------
# Helper app minimal pentru require_admin
# ---------------------------------------------------------------------------

def _make_app_admin() -> FastAPI:
    """App FastAPI minimal cu ruta protejata de require_admin."""
    mini = FastAPI()
    mini.add_middleware(
        SessionMiddleware,
        secret_key="test-secret-admin",
        session_cookie="autopass_session",
        https_only=False,
        same_site="strict",
    )

    from app.web.session import LoginRequired, AdminRequired

    @mini.exception_handler(LoginRequired)
    async def login_required_handler(request: Request, exc: LoginRequired):
        return JSONResponse(status_code=401, content={"detail": "neautentificat"})

    @mini.exception_handler(AdminRequired)
    async def admin_required_handler(request: Request, exc: AdminRequired):
        return JSONResponse(status_code=403, content={"detail": "acces interzis (necesita admin)"})

    @mini.get("/set-session")
    def set_sess(request: Request, account_id: int = 1, user_id: int = 1):
        from app.web.session import set_session
        set_session(request, account_id, user_id)
        return {"ok": True}

    @mini.get("/admin-only")
    def admin_only(request: Request):
        from app.web.session import require_admin
        aid = require_admin(request)
        return {"account_id": aid}

    return mini


# ---------------------------------------------------------------------------
# Teste helper-e app/users.py
# ---------------------------------------------------------------------------

def test_count_admins_initial_zero(conn):
    """Fara niciun user cu is_admin=1, count_admins returneaza 0."""
    from app.users import count_admins
    assert count_admins(conn) == 0


def test_set_admin_marcheaza_userii_contului(conn, account_id, user_id):
    """set_admin(conn, account_id) seteaza is_admin=1 pe toti userii contului."""
    from app.users import set_admin, count_admins
    assert count_admins(conn) == 0
    set_admin(conn, account_id, is_admin=True)
    assert count_admins(conn) == 1


def test_create_user_is_admin_flag(conn, account_id):
    """create_user cu is_admin=True seteaza coloana la 1."""
    from app.users import create_user, count_admins
    create_user(conn, account_id, "newadmin@exemplu.ro", "parola_sigura_456", is_admin=True)
    assert count_admins(conn) == 1


def test_is_account_admin(conn, account_id, user_id):
    """is_account_admin returneaza False inainte si True dupa set_admin."""
    from app.users import is_account_admin, set_admin
    assert is_account_admin(conn, account_id) is False
    set_admin(conn, account_id, is_admin=True)
    assert is_account_admin(conn, account_id) is True


def test_list_admin_emails(conn, account_id):
    """list_admin_emails returneaza emailurile userilor cu is_admin=1."""
    from app.users import create_user, set_admin, list_admin_emails
    uid = create_user(conn, account_id, "admin1@exemplu.ro", "parola_sigura_789")
    # Inainte de set_admin -> lista goala
    assert list_admin_emails(conn) == []
    set_admin(conn, account_id, is_admin=True)
    emails = list_admin_emails(conn)
    assert "admin1@exemplu.ro" in emails


def test_set_admin_cont_inexistent_valueerror(conn):
    """set_admin pe cont care nu exista ridica ValueError."""
    from app.users import set_admin
    with pytest.raises(ValueError, match="cont inexistent"):
        set_admin(conn, 9999, is_admin=True)


def test_set_admin_cont_fara_users_silentios(conn):
    """set_admin pe cont existent fara useri e no-op silentios (nu ridica exceptie)."""
    from app.accounts import create_account
    from app.users import set_admin, count_admins
    acct_fara_user = create_account(conn, "Cont Fara User")
    # Nu trebuie sa ridice
    set_admin(conn, acct_fara_user, is_admin=True)
    assert count_admins(conn) == 0  # niciun user modificat


# ---------------------------------------------------------------------------
# Teste require_admin (app/web/session.py) prin TestClient
# ---------------------------------------------------------------------------

@pytest.fixture()
def client_admin(monkeypatch):
    """TestClient pe app cu require_admin; DB cu cont admin + cont non-admin."""
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "test_require_admin.db"))
    monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "true")
    from app.config import get_settings
    get_settings.cache_clear()
    from app.db import get_connection, init_db
    init_db()
    conn = get_connection()
    from app.accounts import create_account
    from app.users import create_user, set_admin
    # Cont admin (id=2)
    acct_admin = create_account(conn, "Admin Corp")
    uid_admin = create_user(conn, acct_admin, "admin@corp.ro", "parola_admin_001")
    set_admin(conn, acct_admin, is_admin=True)
    # Cont non-admin (id=3)
    acct_user = create_account(conn, "User Corp")
    uid_user = create_user(conn, acct_user, "user@corp.ro", "parola_user_001")
    conn.close()

    app = _make_app_admin()
    with TestClient(app, follow_redirects=False) as c:
        yield c, acct_admin, uid_admin, acct_user, uid_user
    get_settings.cache_clear()


def test_require_admin_blocheaza_non_admin(client_admin):
    """User logat NON-admin pe ruta admin -> 403."""
    client, acct_admin, uid_admin, acct_user, uid_user = client_admin
    client.get(f"/set-session?account_id={acct_user}&user_id={uid_user}")
    resp = client.get("/admin-only")
    assert resp.status_code == 403
    assert "admin" in resp.json()["detail"]


def test_require_admin_lasa_admin(client_admin):
    """User logat ADMIN pe ruta admin -> 200 cu account_id."""
    client, acct_admin, uid_admin, acct_user, uid_user = client_admin
    client.get(f"/set-session?account_id={acct_admin}&user_id={uid_admin}")
    resp = client.get("/admin-only")
    assert resp.status_code == 200
    assert resp.json()["account_id"] == acct_admin


def test_require_admin_nelogat_ridica_login_required(client_admin):
    """Fara sesiune, require_admin ridica LoginRequired (-> 401 in app-ul nostru de test)."""
    client, *_ = client_admin
    resp = client.get("/admin-only")
    # In app-ul de test, LoginRequired -> 401
    assert resp.status_code == 401


# ---------------------------------------------------------------------------
# Teste CLI tools/account.py set-admin
# ---------------------------------------------------------------------------

def _run_account(argv):
    from tools.account import main
    return main(argv)


def test_cli_set_admin_marcheaza_contul(env_cli, capsys):
    """CLI set-admin --account N seteaza is_admin=1 pe userii contului."""
    # Creeaza cont cu user
    from app.db import get_connection
    from app.accounts import create_account
    from app.users import create_user, is_account_admin
    conn = get_connection()
    acct_id = create_account(conn, "Service CLI Admin")
    create_user(conn, acct_id, "cli_admin@corp.ro", "parola_cli_admin_1")
    conn.close()

    rc = _run_account(["set-admin", "--account", str(acct_id)])
    out = capsys.readouterr().out
    assert rc == 0
    assert str(acct_id) in out or "admin" in out.lower()

    conn2 = get_connection()
    assert is_account_admin(conn2, acct_id) is True
    conn2.close()


def test_cli_set_admin_remove(env_cli, capsys):
    """CLI set-admin --account N --remove scoate adminul."""
    from app.db import get_connection
    from app.accounts import create_account
    from app.users import create_user, set_admin, is_account_admin
    conn = get_connection()
    acct_id = create_account(conn, "Service CLI Remove")
    create_user(conn, acct_id, "remove@corp.ro", "parola_remove_001")
    set_admin(conn, acct_id, is_admin=True)
    conn.close()

    rc = _run_account(["set-admin", "--account", str(acct_id), "--remove"])
    assert rc == 0

    conn2 = get_connection()
    assert is_account_admin(conn2, acct_id) is False
    conn2.close()


def test_cli_set_admin_cont_inexistent_exit_2(env_cli, capsys):
    """CLI set-admin pe cont inexistent -> exit code 2 + mesaj pe stderr."""
    rc = _run_account(["set-admin", "--account", "9999"])
    err = capsys.readouterr().err
    assert rc == 2
    assert "inexistent" in err or "eroare" in err.lower()
