"""
Zugriffsrechte Profile: POST/GET/DELETE /api/profiles/{pid}.

TestClient + Dependency-Override für Sessions; GET mockt profile_document, DELETE/POST mocken DB,
damit keine echte Datenbank nötig ist.
"""
from __future__ import annotations

import os

import pytest
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient

os.environ.setdefault("SKIP_DB_MIGRATE", "1")

from auth import require_auth
from main import app


@pytest.fixture
def client() -> TestClient:
    return TestClient(app)


@pytest.fixture(autouse=True)
def _clear_auth_override():
    yield
    app.dependency_overrides.pop(require_auth, None)


def test_get_profile_forbidden_other_user(client: TestClient) -> None:
    def auth_viewer() -> dict:
        return {"profile_id": 42, "role": "trainer"}

    app.dependency_overrides[require_auth] = auth_viewer
    with patch("routers.profiles.profile_document") as pd_mock:
        r = client.get("/api/profiles/99", headers={"X-Auth-Token": "dummy"})
        assert r.status_code == 403
        assert pd_mock.call_count == 0


def test_get_profile_ok_same_numeric_id(client: TestClient) -> None:
    payload = {"id": 42, "name": "Self"}

    def auth_self() -> dict:
        return {"profile_id": 42, "role": "trainer"}

    app.dependency_overrides[require_auth] = auth_self
    with patch("routers.profiles.profile_document", return_value=payload) as pd_mock:
        r = client.get("/api/profiles/42", headers={"X-Auth-Token": "dummy"})
        assert r.status_code == 200
        assert r.json() == payload
        pd_mock.assert_called_once_with("42")


def test_get_profile_ok_same_string_session_id(client: TestClient) -> None:
    """Session liefert profile_id als String (Backward-Compatibility)."""
    payload = {"id": 7, "name": "S"}

    def auth_self_str() -> dict:
        return {"profile_id": "7", "role": "user"}

    app.dependency_overrides[require_auth] = auth_self_str
    with patch("routers.profiles.profile_document", return_value=payload):
        r = client.get("/api/profiles/7", headers={"X-Auth-Token": "dummy"})
        assert r.status_code == 200


@pytest.mark.parametrize("admin_role", ["admin", "superadmin"])
def test_get_profile_ok_platform_admin_views_other(client: TestClient, admin_role: str) -> None:
    payload = {"id": 99, "name": "Other"}

    def auth_admin() -> dict:
        return {"profile_id": 1, "role": admin_role}

    app.dependency_overrides[require_auth] = auth_admin
    with patch("routers.profiles.profile_document", return_value=payload) as pd_mock:
        r = client.get("/api/profiles/99", headers={"X-Auth-Token": "dummy"})
        assert r.status_code == 200
        assert r.json() == payload
        pd_mock.assert_called_once_with("99")


def test_delete_profile_forbidden_non_admin(client: TestClient) -> None:
    def auth_trainer() -> dict:
        return {"profile_id": 1, "role": "trainer"}

    app.dependency_overrides[require_auth] = auth_trainer
    with patch("routers.profiles.get_db") as mock_get_db:
        r = client.delete("/api/profiles/1", headers={"X-Auth-Token": "dummy"})
        assert r.status_code == 403
        mock_get_db.assert_not_called()


def test_delete_profile_admin_last_profile_protected(client: TestClient) -> None:
    mock_conn = MagicMock()
    mock_cm = MagicMock()
    mock_cm.__enter__.return_value = mock_conn
    mock_cm.__exit__.return_value = False
    mock_cur = MagicMock()
    mock_cur.fetchone.return_value = {"count": 1}

    app.dependency_overrides[require_auth] = lambda: {"profile_id": 99, "role": "admin"}
    with patch("routers.profiles.get_db", return_value=mock_cm), patch(
        "routers.profiles.get_cursor", return_value=mock_cur
    ):
        r = client.delete("/api/profiles/5", headers={"X-Auth-Token": "dummy"})
        assert r.status_code == 400


def test_delete_profile_admin_ok_when_multiple_profiles(client: TestClient) -> None:
    mock_conn = MagicMock()
    mock_cm = MagicMock()
    mock_cm.__enter__.return_value = mock_conn
    mock_cm.__exit__.return_value = False
    mock_cur = MagicMock()
    mock_cur.fetchone.return_value = {"count": 4}

    app.dependency_overrides[require_auth] = lambda: {"profile_id": 99, "role": "superadmin"}
    with patch("routers.profiles.get_db", return_value=mock_cm), patch(
        "routers.profiles.get_cursor", return_value=mock_cur
    ):
        r = client.delete("/api/profiles/12", headers={"X-Auth-Token": "dummy"})
        assert r.status_code == 200
        assert r.json() == {"ok": True}

    calls = mock_cur.execute.call_args_list
    assert any(
        args and "DELETE FROM profiles" in str(args[0][0]) for args in calls
    )


def test_create_profile_forbidden_non_admin(client: TestClient) -> None:
    app.dependency_overrides[require_auth] = lambda: {"profile_id": 1, "role": "trainer"}
    with patch("routers.profiles.get_db") as mock_get_db:
        r = client.post(
            "/api/profiles",
            json={"name": "Neu", "email": "neu@example.com"},
            headers={"X-Auth-Token": "dummy"},
        )
        assert r.status_code == 403
        mock_get_db.assert_not_called()


def test_create_profile_admin_duplicate_email(client: TestClient) -> None:
    mock_cm = MagicMock()
    mock_conn = MagicMock()
    mock_cm.__enter__.return_value = mock_conn
    mock_cm.__exit__.return_value = False
    mock_cur = MagicMock()
    mock_cur.fetchone.return_value = {"id": 99}

    app.dependency_overrides[require_auth] = lambda: {"profile_id": 1, "role": "admin"}
    with patch("routers.profiles.get_db", return_value=mock_cm), patch(
        "routers.profiles.get_cursor", return_value=mock_cur
    ), patch("routers.profiles.hash_pin", return_value="hashed"):
        r = client.post(
            "/api/profiles",
            json={"name": "Dup", "email": "dup@example.com"},
            headers={"X-Auth-Token": "dummy"},
        )
        assert r.status_code == 409


def test_create_profile_admin_success(client: TestClient) -> None:
    mock_cm = MagicMock()
    mock_conn = MagicMock()
    mock_cm.__enter__.return_value = mock_conn
    mock_cm.__exit__.return_value = False
    mock_cur = MagicMock()
    mock_cur.fetchone.side_effect = [None, {"id": 501}]

    doc = {"id": 501, "name": "Nu User", "email": "nu@example.com"}

    app.dependency_overrides[require_auth] = lambda: {"profile_id": 1, "role": "superadmin"}
    with patch("routers.profiles.get_db", return_value=mock_cm), patch(
        "routers.profiles.get_cursor", return_value=mock_cur
    ), patch("routers.profiles.profile_document", return_value=doc), patch(
        "routers.profiles.hash_pin", return_value="hashed"
    ):
        r = client.post(
            "/api/profiles",
            json={"name": "Nu User", "email": "nu@example.com"},
            headers={"X-Auth-Token": "dummy"},
        )
        assert r.status_code == 200
        assert r.json() == doc

    insert_calls = [
        c.args[0] for c in mock_cur.execute.call_args_list if c.args and "INSERT INTO profiles" in str(c.args[0])
    ]
    assert len(insert_calls) >= 1
