"""Tests for dashboard configure API endpoint."""

from __future__ import annotations

from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from backend.api.middleware.api_key_auth import APIKeyContext
from backend.drift.models import DriftConfigureRequest


def _auth_context(
    features: list[str] | None = None,
    tier: str = "team",
) -> APIKeyContext:
    if features is None:
        features = [
            "hosted_dashboard",
            "drift_alerts",
            "drift_event_driven",
        ]
    return APIKeyContext(
        customer_id="cust-1",
        email="admin@example.com",
        tier=tier,
        features=features,
        project_limit=5,
        batch_limit=10,
    )


def _mock_project_doc(
    exists: bool = True,
    data: dict[str, Any] | None = None,
) -> MagicMock:
    doc = MagicMock()
    doc.exists = exists
    doc.to_dict.return_value = data or {
        "project_label": "prod",
        "cloud": "gcp",
        "cloud_account_id": "my-gcp-proj",
        "drift_mode": "scheduled",
        "drift_monitoring": False,
        "frameworks": ["cis_gcp"],
        "alert_config": {},
    }
    return doc


def _nested_doc(mock_db: MagicMock) -> MagicMock:
    """Shortcut for deeply-nested document mock."""
    col = mock_db.collection.return_value
    doc = col.document.return_value
    sub = doc.collection.return_value
    return sub.document.return_value


@pytest.fixture
def mock_db():
    with patch("backend.shared.firestore.get_db") as mock:
        db = MagicMock()
        mock.return_value = db
        yield db


_SM_PATCH = "google.cloud.secretmanager_v1"


class TestDashboardConfigure:
    """Configure endpoint tests."""

    @pytest.mark.asyncio
    async def test_configure_scheduled_mode(self, mock_db: MagicMock) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        project_doc = _mock_project_doc()
        _nested_doc(mock_db).get = AsyncMock(return_value=project_doc)
        _nested_doc(mock_db).update = AsyncMock()

        body = DriftConfigureRequest(drift_mode="scheduled", drift_alerts=True)
        resp = await configure_project_drift("proj-1", body, _auth_context())
        assert resp.status_code == 200

    @pytest.mark.asyncio
    async def test_configure_event_driven_returns_setup(
        self, mock_db: MagicMock
    ) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        project_doc = _mock_project_doc()
        _nested_doc(mock_db).get = AsyncMock(return_value=project_doc)
        _nested_doc(mock_db).update = AsyncMock()

        with patch(_SM_PATCH, create=True) as mock_sm:
            mock_sm.SecretManagerServiceClient.return_value = MagicMock()

            body = DriftConfigureRequest(drift_mode="event_driven")
            resp = await configure_project_drift("proj-1", body, _auth_context())
            assert resp.status_code == 200
            import json

            result = json.loads(resp.body)
            assert result.get("setup_instructions") is not None
            assert result.get("webhook_secret", "").startswith("cf_drift_")

    @pytest.mark.asyncio
    async def test_configure_without_drift_alerts_feature_403(
        self, mock_db: MagicMock
    ) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        body = DriftConfigureRequest(drift_alerts=True)
        auth = _auth_context(features=["hosted_dashboard"])
        resp = await configure_project_drift("proj-1", body, auth)
        assert resp.status_code == 403

    @pytest.mark.asyncio
    async def test_configure_event_driven_without_feature_403(
        self, mock_db: MagicMock
    ) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        body = DriftConfigureRequest(drift_mode="event_driven")
        auth = _auth_context(features=["hosted_dashboard", "drift_alerts"])
        resp = await configure_project_drift("proj-1", body, auth)
        assert resp.status_code == 403

    @pytest.mark.asyncio
    async def test_configure_stores_tfc_token(self, mock_db: MagicMock) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        project_doc = _mock_project_doc()
        _nested_doc(mock_db).get = AsyncMock(return_value=project_doc)
        _nested_doc(mock_db).update = AsyncMock()

        with patch(_SM_PATCH, create=True) as mock_sm:
            mock_sm.SecretManagerServiceClient.return_value = MagicMock()

            body = DriftConfigureRequest(tfc_token="my-tfc-token")
            resp = await configure_project_drift("proj-1", body, _auth_context())
            assert resp.status_code == 200

    @pytest.mark.asyncio
    async def test_configure_nonexistent_project_404(self, mock_db: MagicMock) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        project_doc = _mock_project_doc(exists=False)
        _nested_doc(mock_db).get = AsyncMock(return_value=project_doc)

        body = DriftConfigureRequest(drift_alerts=True)
        resp = await configure_project_drift("proj-1", body, _auth_context())
        assert resp.status_code == 404

    @pytest.mark.asyncio
    async def test_configure_partial_update_preserves_existing(
        self, mock_db: MagicMock
    ) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        project_doc = _mock_project_doc(
            data={
                "project_label": "prod",
                "cloud": "gcp",
                "cloud_account_id": "my-proj",
                "drift_mode": "scheduled",
                "drift_monitoring": True,
                "frameworks": ["cis_gcp", "soc2"],
                "alert_config": {
                    "alert_threshold": 10,
                    "alert_email": "old@example.com",
                },
            }
        )
        _nested_doc(mock_db).get = AsyncMock(return_value=project_doc)
        _nested_doc(mock_db).update = AsyncMock()

        body = DriftConfigureRequest(alert_email="new@example.com")
        resp = await configure_project_drift("proj-1", body, _auth_context())
        assert resp.status_code == 200

    @pytest.mark.asyncio
    async def test_configure_invalid_api_key_401(
        self,
    ) -> None:
        """Auth validation is handled by middleware."""
        from backend.api.routes.dashboard_configure import (
            _auth_dep,
        )

        assert _auth_dep is not None

    @pytest.mark.asyncio
    async def test_configure_webhook_secret_stored(self, mock_db: MagicMock) -> None:
        from backend.api.routes.dashboard_configure import (
            configure_project_drift,
        )

        project_doc = _mock_project_doc()
        _nested_doc(mock_db).get = AsyncMock(return_value=project_doc)
        _nested_doc(mock_db).update = AsyncMock()

        with patch(_SM_PATCH, create=True) as mock_sm:
            mock_sm_client = MagicMock()
            mock_sm.SecretManagerServiceClient.return_value = mock_sm_client

            body = DriftConfigureRequest(drift_mode="event_driven")
            resp = await configure_project_drift("proj-1", body, _auth_context())
            assert resp.status_code == 200
            mock_sm_client.create_secret.assert_called_once()
            mock_sm_client.add_secret_version.assert_called_once()
