"""Tests for AuthentikClient and the directory endpoint."""

from __future__ import annotations

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
from cryptography.fernet import Fernet
from fastapi.testclient import TestClient

from media_library_viewer_api.clients.authentik import AuthentikClient
from media_library_viewer_api.dependencies import get_settings_store
from media_library_viewer_api.main import app
from media_library_viewer_api.services.secrets import reset_encryption_key_cache
from media_library_viewer_api.services.settings_store import SettingsStore

TEST_KEY = Fernet.generate_key().decode()


@pytest.fixture(autouse=True)
def _encryption_key(monkeypatch: pytest.MonkeyPatch) -> None:
    """Provide a stable MANAGE_ENCRYPTION_KEY for every test."""
    monkeypatch.setenv("MANAGE_ENCRYPTION_KEY", TEST_KEY)
    reset_encryption_key_cache()
    yield
    reset_encryption_key_cache()


@pytest.fixture()
def store(tmp_path: Path) -> SettingsStore:
    s = SettingsStore(tmp_path / "settings.sqlite")
    s.ensure_defaults()
    app.dependency_overrides[get_settings_store] = lambda: s
    yield s
    app.dependency_overrides.clear()


# ---------------------------------------------------------------------------
# Client unit tests
# ---------------------------------------------------------------------------


class TestAuthentikClient:
    def test_base_url_normalizes_trailing_slash(self) -> None:
        c = AuthentikClient(base_url="https://auth.example.com/", api_token="t")
        assert c.base_url == "https://auth.example.com"

    def test_base_url_strips_api_v3_suffix(self) -> None:
        c = AuthentikClient(base_url="https://auth.example.com/api/v3", api_token="t")
        assert c.base_url == "https://auth.example.com"

    def test_bearer_header_is_set(self) -> None:
        c = AuthentikClient(base_url="https://auth.example.com", api_token="tok")
        assert c.session.headers["Authorization"] == "Bearer tok"

    def test_empty_base_url_raises(self) -> None:
        with pytest.raises(ValueError):
            AuthentikClient(base_url="", api_token="t")

    def test_empty_api_token_raises(self) -> None:
        with pytest.raises(ValueError):
            AuthentikClient(base_url="https://auth.example.com", api_token="")

    @patch.object(AuthentikClient, "get")
    def test_users_normalizes_pagination(self, mock_get: MagicMock) -> None:
        mock_get.return_value = {
            "pagination": {"count": 42, "next": 2, "previous": 0, "current": 1},
            "results": [
                {"pk": 1, "username": "alice", "email": "alice@example.com"},
                {"pk": 2, "username": "bob", "email": "bob@example.com"},
            ],
        }
        client = AuthentikClient(base_url="https://auth.example.com", api_token="t")
        result = client.users(search="ali", page=1, page_size=2)
        assert result["total"] == 42
        assert result["page"] == 1
        assert result["page_size"] == 2
        assert len(result["items"]) == 2
        assert result["items"][0]["username"] == "alice"

    @patch.object(AuthentikClient, "get")
    def test_users_handles_empty_results(self, mock_get: MagicMock) -> None:
        mock_get.return_value = {"pagination": {"count": 0}, "results": []}
        client = AuthentikClient(base_url="https://auth.example.com", api_token="t")
        result = client.users()
        assert result["items"] == []
        assert result["total"] == 0

    @patch.object(AuthentikClient, "get")
    def test_users_handles_non_dict_payload(self, mock_get: MagicMock) -> None:
        mock_get.return_value = []
        client = AuthentikClient(base_url="https://auth.example.com", api_token="t")
        result = client.users()
        assert result["items"] == []
        assert result["total"] == 0

    @patch("media_library_viewer_api.clients.authentik.requests.Session")
    def test_get_sends_correct_url_and_params(self, mock_session_cls: MagicMock) -> None:
        mock_session = MagicMock()
        mock_session_cls.return_value = mock_session
        mock_response = MagicMock()
        mock_response.json.return_value = {"results": []}
        mock_response.raise_for_status.return_value = None
        mock_session.get.return_value = mock_response

        c = AuthentikClient(base_url="https://auth.example.com", api_token="t")
        c.get("/core/users/", search="x", page=2)

        call_args = mock_session.get.call_args
        assert call_args.kwargs["params"] == {"search": "x", "page": 2}
        assert call_args.args[0] == "https://auth.example.com/api/v3/core/users/"


# ---------------------------------------------------------------------------
# Endpoint integration tests
# ---------------------------------------------------------------------------


class TestAuthentikUsersEndpoint:
    def test_not_configured_returns_empty_with_error(self, store: SettingsStore) -> None:
        client = TestClient(app)
        response = client.get("/api/services/authentik/nonexistent/users")
        assert response.status_code == 200
        data = response.json()
        assert data["items"] == []
        assert data["total"] == 0
        assert "error" in data

    @patch("media_library_viewer_api.routers.authentik_users.AuthentikClient")
    def test_success_returns_users(self, mock_client_cls: MagicMock, store: SettingsStore) -> None:
        mock_client = MagicMock()
        mock_client.users.return_value = {
            "items": [{"pk": 1, "username": "alice"}],
            "total": 1,
            "page": 1,
            "page_size": 50,
        }
        mock_client_cls.return_value = mock_client

        created = store.upsert_service(
            {
                "service_type": "authentik",
                "name": "Main",
                "config": {"base_url": "https://auth.example.com"},
                "enabled": True,
            },
            secret_values={"api_token": "secret-token"},
        )
        service_id = created["id"]

        client = TestClient(app)
        response = client.get(f"/api/services/authentik/{service_id}/users?search=ali")
        assert response.status_code == 200
        data = response.json()
        assert len(data["items"]) == 1
        assert data["items"][0]["username"] == "alice"
        assert data["total"] == 1
        assert "error" not in data

    @patch("media_library_viewer_api.routers.authentik_users.AuthentikClient")
    def test_unreachable_returns_error(self, mock_client_cls: MagicMock, store: SettingsStore) -> None:
        mock_client = MagicMock()
        mock_client.users.side_effect = ConnectionError("refused")
        mock_client_cls.return_value = mock_client

        created = store.upsert_service(
            {
                "service_type": "authentik",
                "name": "Main",
                "config": {"base_url": "https://auth.example.com"},
                "enabled": True,
            },
            secret_values={"api_token": "secret-token"},
        )
        service_id = created["id"]

        client = TestClient(app)
        response = client.get(f"/api/services/authentik/{service_id}/users")
        assert response.status_code == 200
        data = response.json()
        assert data["items"] == []
        assert "error" in data
