"""Tests for dashboard, flaky-tests, DX metrics, regressions, security,
contract-drift, and performance baselines endpoints."""

from unittest.mock import AsyncMock, MagicMock, patch

# The dashboard router uses `from api import db` then calls `db.func()`.
# Some functions (compute_flaky_test_scores) live in the old db.py and aren't
# re-exported by the api.db package, so we patch on the router's own `db` ref.
_DB = "api.routers.dashboard.db"


class TestDashboardStats:
    """Tests for GET /v1/dashboard/stats."""

    def test_get_dashboard_stats_success(self, client, auth_header):
        with patch(f"{_DB}.get_dashboard_stats") as mock_stats, \
             patch(f"{_DB}.get_org_github_installations") as mock_installs, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_stats.return_value = {
                "total_runs": 100,
                "passed_runs": 80,
                "failed_runs": 15,
                "flaky_runs": 5,
                "in_progress_runs": 0,
                "fixes_generated": 10,
                "fixes_applied": 3,
                "pass_rate": 80.0,
            }
            mock_installs.return_value = [{"installation_id": 1}]
            response = client.get("/v1/dashboard/stats", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert data["total_runs"] == 100
            assert data["pass_rate"] == 80.0
            assert data["has_github"] is True

    def test_get_dashboard_stats_no_github(self, client, auth_header):
        with patch(f"{_DB}.get_dashboard_stats") as mock_stats, \
             patch(f"{_DB}.get_org_github_installations") as mock_installs, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_stats.return_value = {
                "total_runs": 0, "passed_runs": 0, "failed_runs": 0,
                "flaky_runs": 0, "fixes_generated": 0, "fixes_applied": 0,
                "pass_rate": 0.0,
            }
            mock_installs.return_value = []
            response = client.get("/v1/dashboard/stats", headers=auth_header)
            assert response.status_code == 200
            assert response.json()["has_github"] is False

    def test_get_dashboard_stats_unauthorized(self, client):
        response = client.get("/v1/dashboard/stats")
        assert response.status_code == 401


class TestFlakyTests:
    """Tests for GET /v1/flaky-tests."""

    def test_list_flaky_tests_success(self, client, auth_header):
        with patch(f"{_DB}.compute_flaky_test_scores", create=True) as mock_scores, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_scores.return_value = [
                {"test_id": "t1", "test_name": "test_a", "flakiness_score": 0.8},
                {"test_id": "t2", "test_name": "test_b", "flakiness_score": 0.5},
            ]
            response = client.get("/v1/flaky-tests", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert data["total_count"] == 2
            assert len(data["tests"]) == 2

    def test_list_flaky_tests_with_pagination(self, client, auth_header):
        with patch(f"{_DB}.compute_flaky_test_scores", create=True) as mock_scores, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_scores.return_value = [
                {"test_id": f"t{i}", "test_name": f"test_{i}", "flakiness_score": 0.5}
                for i in range(5)
            ]
            response = client.get(
                "/v1/flaky-tests?limit=2&offset=1", headers=auth_header,
            )
            assert response.status_code == 200
            data = response.json()
            assert data["total_count"] == 5
            assert len(data["tests"]) == 2
            assert data["offset"] == 1

    def test_list_flaky_tests_unauthorized(self, client):
        response = client.get("/v1/flaky-tests")
        assert response.status_code == 401


class TestFlakyTestsDashboard:
    """Tests for GET /v1/flaky-tests/dashboard."""

    def test_flaky_dashboard_success(self, client, auth_header):
        project_id = "660e8400-e29b-41d4-a716-446655440000"
        with patch(f"{_DB}.get_flaky_tests_dashboard") as mock_dash, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_dash.return_value = {
                "tests": [
                    {
                        "test_id": "t1", "test_name": "test_flaky",
                        "flakiness_score": 0.9, "trend": 0.1,
                        "pass_count": 5, "fail_count": 3,
                    },
                ],
            }
            response = client.get(
                f"/v1/flaky-tests/dashboard?project_id={project_id}",
                headers=auth_header,
            )
            assert response.status_code == 200
            data = response.json()
            assert data["period_days"] == 7
            assert len(data["tests"]) == 1
            assert data["summary"]["total_flaky"] == 1

    def test_flaky_dashboard_unauthorized(self, client):
        response = client.get(
            "/v1/flaky-tests/dashboard?project_id=660e8400-e29b-41d4-a716-446655440000",
        )
        assert response.status_code == 401


class TestDXMetrics:
    """Tests for GET /v1/dx-metrics/* endpoints."""

    def test_get_productivity_metrics(self, client, auth_header):
        with patch("api.dx_metrics.DXMetricsCalculator") as mock_calc_cls:
            mock_calc = MagicMock()
            mock_calc_cls.return_value = mock_calc
            mock_metrics = MagicMock(
                speed_score=80.0, avg_fix_time_seconds=120.0,
                fix_first_try_rate=0.7, effectiveness_score=75.0,
                fix_acceptance_rate=0.8, replay_usage_rate=0.5,
                quality_score=85.0, regression_rate=0.05,
                verified_fix_rate=0.9, impact_score=70.0,
                time_saved_hours=50.0, ci_cost_saved_dollars=200.0,
                overall_score=77.5,
            )
            mock_calc.get_productivity_metrics = AsyncMock(return_value=mock_metrics)
            response = client.get("/v1/dx-metrics/productivity", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert "overall_score" in data

    def test_get_ci_stability_metrics(self, client, auth_header):
        with patch("api.dx_metrics.DXMetricsCalculator") as mock_calc_cls:
            mock_calc = MagicMock()
            mock_calc_cls.return_value = mock_calc
            mock_metrics = MagicMock(
                flake_rate=0.05, flake_rate_change=-0.02,
                fixed_flakes=10, total_runs=500,
                failed_runs=25, flaky_runs=15,
                stability_score=95.0,
            )
            mock_calc.get_ci_stability_metrics = AsyncMock(return_value=mock_metrics)
            response = client.get("/v1/dx-metrics/ci-stability", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert "stability_score" in data

    def test_dx_metrics_unauthorized(self, client):
        response = client.get("/v1/dx-metrics/productivity")
        assert response.status_code == 401


class TestRegressions:
    """Tests for GET /v1/regressions."""

    def test_list_regressions_success(self, client, auth_header):
        with patch("api.routers.dashboard._detect_regressions") as mock_detect, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            from api.routers.dashboard import RegressionItem
            mock_detect.return_value = [
                RegressionItem(
                    id="reg-abc123", test_name="test_login",
                    metric="status", previous_value=1.0, current_value=0.0,
                    change_percent=-100.0, severity="critical",
                    status="new",
                ),
            ]
            response = client.get("/v1/regressions?project_id=550e8400-e29b-41d4-a716-446655440000", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert data["stats"]["total"] == 1
            assert data["stats"]["critical"] == 1

    def test_list_regressions_empty(self, client, auth_header):
        with patch("api.routers.dashboard._detect_regressions") as mock_detect, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_detect.return_value = []
            response = client.get("/v1/regressions", headers=auth_header)
            assert response.status_code == 200
            assert response.json()["stats"]["total"] == 0

    def test_list_regressions_unauthorized(self, client):
        response = client.get("/v1/regressions")
        assert response.status_code == 401


class TestPerformanceBaselines:
    """Tests for GET /v1/performance/baselines."""

    def test_get_baselines_success(self, client, auth_header):
        with patch("api.routers.dashboard._fetch_performance_baselines") as mock_fetch, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            from api.routers.dashboard import EndpointBaselineItem
            mock_fetch.return_value = [
                EndpointBaselineItem(
                    endpoint="/api/users", method="GET",
                    p50_ms=50.0, p95_ms=120.0, p99_ms=200.0,
                    sample_count=1000, trend="stable",
                ),
            ]
            response = client.get("/v1/performance/baselines", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert data["stats"]["total_endpoints"] == 1

    def test_get_baselines_unauthorized(self, client):
        response = client.get("/v1/performance/baselines")
        assert response.status_code == 401


class TestContractDriftSummary:
    """Tests for GET /v1/contract-drift/summary."""

    def test_get_contract_drift_summary(self, client, auth_header):
        with patch("api.routers.dashboard._fetch_contract_drift_summary") as mock_fetch, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_fetch.return_value = []
            response = client.get("/v1/contract-drift/summary", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert data["stats"]["total_endpoints"] == 0

    def test_get_contract_drift_summary_unauthorized(self, client):
        response = client.get("/v1/contract-drift/summary")
        assert response.status_code == 401


class TestSecurityFindings:
    """Tests for GET /v1/security/findings."""

    def test_list_security_findings(self, client, auth_header):
        with patch("api.routers.dashboard._fetch_security_findings") as mock_fetch, \
             patch("api.routers.dashboard.check_project_access", new_callable=AsyncMock):
            mock_fetch.return_value = []
            response = client.get("/v1/security/findings", headers=auth_header)
            assert response.status_code == 200
            data = response.json()
            assert data["stats"]["total_findings"] == 0

    def test_list_security_findings_unauthorized(self, client):
        response = client.get("/v1/security/findings")
        assert response.status_code == 401
