# #region Test.Api.ValidationTasks.Comprehensive [C:3] [TYPE Module] [SEMANTICS test,validation,tasks,comprehensive]
# @BRIEF Comprehensive tests for validation task API — CRUD, runs, parse-url, redirect.
# @RELATION BINDS_TO -> [ValidationTasksRoutes]

import os

os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-tests")
os.environ.setdefault("DEV_MODE", "true")

import sys
from datetime import datetime
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient

_src = str(Path(__file__).resolve().parent.parent.parent / "src")
if _src not in sys.path:
    sys.path.insert(0, _src)


def _make_client(overrides: dict | None = None) -> TestClient:
    from src.api.routes.validation_tasks import router, _get_task_service
    from src.core.database import get_db
    from src.dependencies import get_config_manager, get_current_user, get_scheduler_service, get_task_manager
    from src.schemas.auth import User, RoleSchema

    app = FastAPI()
    app.include_router(router, prefix="/validation-tasks")

    mock_user = User(
        id="admin-1", username="admin", email="admin@x.com",
        auth_source="LOCAL",
        created_at=__import__("datetime").datetime.now(),
        roles=[RoleSchema(id="r1", name="Admin", description="", permissions=[])],
    )

    app.dependency_overrides[_get_task_service] = lambda: MagicMock()
    app.dependency_overrides[get_db] = lambda: MagicMock()
    app.dependency_overrides[get_current_user] = lambda: mock_user
    app.dependency_overrides[get_config_manager] = lambda: MagicMock()
    app.dependency_overrides[get_scheduler_service] = lambda: MagicMock()
    app.dependency_overrides[get_task_manager] = lambda: MagicMock()
    if overrides:
        for dep, fn in overrides.items():
            app.dependency_overrides[dep] = fn
    return TestClient(app)


# ── list_tasks ──

class TestListTasks:
    """GET /validation-tasks"""

    def test_success(self):
        mock_svc = MagicMock()
        mock_svc.list_tasks.return_value = (1, [{
            "id": "task-1", "name": "Test", "environment_id": "env-1",
            "is_active": True, "dashboard_ids": ["dash-1"],
            "created_at": "2024-01-01T00:00:00",
        }])

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks")
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 1
        assert len(data["items"]) == 1

    def test_with_filters(self):
        mock_svc = MagicMock()
        mock_svc.list_tasks.return_value = (0, [])

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks?is_active=true&environment_id=env-1&search=test")
        assert resp.status_code == 200

    def test_value_error(self):
        mock_svc = MagicMock()
        mock_svc.list_tasks.side_effect = ValueError("Invalid filter")

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks")
        assert resp.status_code == 400


# ── create_task ──

class TestCreateTask:
    """POST /validation-tasks"""

    CREATE_PAYLOAD = {
        "name": "New Task",
        "environment_id": "env-1",
        "dashboard_ids": ["dash-1"],
        "provider_id": "provider-1",
    }

    def test_success(self):
        from src.schemas.validation import ValidationTaskResponse

        mock_svc = MagicMock()
        mock_svc.create_task = AsyncMock(return_value=ValidationTaskResponse(
            id="task-new", name="New Task", environment_id="env-1",
            is_active=True, dashboard_ids=["dash-1"],
            created_at=datetime.now(),
        ))
        mock_sched = MagicMock()

        from src.api.routes.validation_tasks import _get_task_service
        from src.dependencies import get_scheduler_service
        client = _make_client({
            _get_task_service: lambda: mock_svc,
            get_scheduler_service: lambda: mock_sched,
        })
        resp = client.post("/validation-tasks", json=self.CREATE_PAYLOAD)
        assert resp.status_code == 201
        mock_sched.reload_validation_policy.assert_called_once()

    def test_value_error(self):
        mock_svc = MagicMock()
        mock_svc.create_task = AsyncMock(side_effect=ValueError("Invalid provider"))

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.post("/validation-tasks", json=self.CREATE_PAYLOAD)
        assert resp.status_code == 422


# ── get_task ──

class TestGetTask:
    """GET /validation-tasks/{policy_id}"""

    def test_success(self):
        mock_svc = MagicMock()
        mock_svc.get_task.return_value = {
            "id": "task-1", "name": "Test Task", "environment_id": "env-1",
            "is_active": True, "dashboard_ids": ["dash-1"],
            "created_at": "2024-01-01T00:00:00",
        }

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/task-1")
        assert resp.status_code == 200
        assert resp.json()["id"] == "task-1"

    def test_not_found(self):
        mock_svc = MagicMock()
        mock_svc.get_task.side_effect = ValueError("Task not found")

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/unknown")
        assert resp.status_code == 404


# ── update_task ──

class TestUpdateTask:
    """PUT /validation-tasks/{policy_id}"""

    UPDATE_PAYLOAD = {"name": "Updated Task"}

    def test_success(self):
        from src.schemas.validation import ValidationTaskResponse

        mock_svc = MagicMock()
        mock_svc.update_task = AsyncMock(return_value=ValidationTaskResponse(
            id="task-1", name="Updated Task", environment_id="env-1",
            is_active=True, dashboard_ids=["dash-1"],
            created_at=datetime.now(),
        ))
        mock_sched = MagicMock()

        from src.api.routes.validation_tasks import _get_task_service
        from src.dependencies import get_scheduler_service
        client = _make_client({
            _get_task_service: lambda: mock_svc,
            get_scheduler_service: lambda: mock_sched,
        })
        resp = client.put("/validation-tasks/task-1", json=self.UPDATE_PAYLOAD)
        assert resp.status_code == 200
        mock_sched.reload_validation_policy.assert_called_once()

    def test_not_found(self):
        mock_svc = MagicMock()
        mock_svc.update_task = AsyncMock(side_effect=ValueError("Task not found"))

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.put("/validation-tasks/unknown", json=self.UPDATE_PAYLOAD)
        assert resp.status_code == 404


# ── toggle_task_status ──

class TestToggleTaskStatus:
    """PATCH /validation-tasks/{policy_id}/status"""

    def test_activate(self):
        mock_svc = MagicMock()
        mock_svc.update_task = AsyncMock(return_value={
            "id": "task-1", "name": "Task", "environment_id": "env-1",
            "is_active": True, "dashboard_ids": ["dash-1"],
            "created_at": "2024-01-01T00:00:00",
        })
        mock_sched = MagicMock()

        from src.api.routes.validation_tasks import _get_task_service
        from src.dependencies import get_scheduler_service
        client = _make_client({
            _get_task_service: lambda: mock_svc,
            get_scheduler_service: lambda: mock_sched,
        })
        resp = client.patch("/validation-tasks/task-1/status?is_active=true")
        assert resp.status_code == 200
        mock_sched.reload_validation_policy.assert_called_once()

    def test_deactivate(self):
        mock_svc = MagicMock()
        mock_svc.update_task = AsyncMock(return_value={
            "id": "task-1", "name": "Task", "environment_id": "env-1",
            "is_active": False, "dashboard_ids": ["dash-1"],
            "created_at": "2024-01-01T00:00:00",
        })
        mock_sched = MagicMock()

        from src.api.routes.validation_tasks import _get_task_service
        from src.dependencies import get_scheduler_service
        client = _make_client({
            _get_task_service: lambda: mock_svc,
            get_scheduler_service: lambda: mock_sched,
        })
        resp = client.patch("/validation-tasks/task-1/status?is_active=false")
        assert resp.status_code == 200
        mock_sched.remove_validation_job.assert_called_once()

    def test_not_found(self):
        mock_svc = MagicMock()
        mock_svc.update_task = AsyncMock(side_effect=ValueError("Task not found"))

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.patch("/validation-tasks/unknown/status?is_active=true")
        assert resp.status_code == 404


# ── trigger_run ──

class TestTriggerRun:
    """POST /validation-tasks/{policy_id}/run"""

    def test_success(self):
        mock_svc = MagicMock()
        mock_svc.trigger_run = AsyncMock(return_value={"task_id": "t-1", "run_id": "run-1"})

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.post("/validation-tasks/task-1/run")
        assert resp.status_code == 200
        data = resp.json()
        # spawned_task_id comes from result["task_id"] (the mock returns "t-1")
        assert data["spawned_task_id"] == "t-1"
        assert data["status"] == "PENDING"

    def test_value_error(self):
        mock_svc = MagicMock()
        mock_svc.trigger_run = AsyncMock(side_effect=ValueError("Cannot trigger"))

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.post("/validation-tasks/task-1/run")
        assert resp.status_code == 422


# ── list_all_runs ──

class TestListAllRuns:
    """GET /validation-tasks/runs/all"""

    def test_success(self):
        mock_svc = MagicMock()
        mock_svc.list_all_runs = AsyncMock(return_value=(1, [{
            "id": "run-1", "dashboard_id": "dash-1", "status": "SUCCESS",
        }]))

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/runs/all")
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 1

    def test_with_filters(self):
        mock_svc = MagicMock()
        mock_svc.list_all_runs = AsyncMock(return_value=(0, []))

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/runs/all?status=RUNNING&environment_id=env-1&dashboard_id=dash-1")
        assert resp.status_code == 200

    def test_value_error(self):
        mock_svc = MagicMock()
        mock_svc.list_all_runs = AsyncMock(side_effect=ValueError("Invalid filter"))

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/runs/all")
        assert resp.status_code == 400


# ── list_runs ──

class TestListRuns:
    """GET /validation-tasks/{policy_id}/runs"""

    def test_success(self):
        mock_svc = MagicMock()
        mock_svc.get_run_history.return_value = (1, [{
            "id": "run-1", "policy_id": "task-1", "status": "SUCCESS",
            "trigger": "manual", "dashboard_count": 2, "pass_count": 1,
            "warn_count": 0, "fail_count": 0, "unknown_count": 0,
        }])

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/task-1/runs")
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 1
        assert data["items"][0]["status"] == "SUCCESS"

    def test_value_error(self):
        mock_svc = MagicMock()
        mock_svc.get_run_history.side_effect = ValueError("Task not found")

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/unknown/runs")
        assert resp.status_code == 400


# ── get_run_detail ──

class TestGetRunDetail:
    """GET /validation-tasks/{policy_id}/runs/{run_id}"""

    def test_success(self):
        mock_svc = MagicMock()
        mock_svc.get_run_detail = AsyncMock(return_value={
            "run": {"id": "run-1", "policy_id": "task-1", "status": "SUCCESS",
                     "trigger": "manual", "dashboard_count": 2, "pass_count": 1,
                     "warn_count": 0, "fail_count": 0, "unknown_count": 0},
            "records": [{"dashboard_id": "dash-1", "status": "PASS"}],
        })

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/task-1/runs/run-1")
        assert resp.status_code == 200
        data = resp.json()
        assert data["run"]["status"] == "SUCCESS"
        assert len(data["records"]) == 1

    def test_not_found(self):
        mock_svc = MagicMock()
        mock_svc.get_run_detail.side_effect = ValueError("Run not found")

        from src.api.routes.validation_tasks import _get_task_service
        client = _make_client({_get_task_service: lambda: mock_svc})
        resp = client.get("/validation-tasks/task-1/runs/unknown")
        assert resp.status_code == 404


# ── parse_superset_url ──

class TestParseSupersetUrl:
    """POST /validation-tasks/parse-url"""

    PARSE_PAYLOAD = {
        "url": "https://superset.example.com/superset/dashboard/1/",
        "environment_id": "env-1",
    }

    def test_success_with_dashboard_id(self):
        mock_config = MagicMock()
        mock_config.get_environment.return_value = MagicMock()

        mock_extractor = MagicMock()
        mock_parsed = MagicMock()
        mock_parsed.dashboard_id = 1
        mock_parsed.chart_id = None
        mock_parsed.query_state = {}
        mock_parsed.partial_recovery = None
        mock_parsed.unresolved_references = None
        mock_extractor.parse_superset_link = AsyncMock(return_value=mock_parsed)
        mock_client = MagicMock()
        mock_client.get_dashboard_detail = AsyncMock(return_value={"dashboard_title": "My Dashboard"})
        mock_extractor.client = mock_client

        from src.dependencies import get_config_manager
        with patch("src.core.utils.superset_context_extractor.SupersetContextExtractor", return_value=mock_extractor):
            client = _make_client({get_config_manager: lambda: mock_config})
            resp = client.post("/validation-tasks/parse-url", json=self.PARSE_PAYLOAD)
        assert resp.status_code == 200
        data = resp.json()
        assert data["dashboard_id"] == "1"
        assert data["title"] == "My Dashboard"

    def test_success_with_chart_id(self):
        mock_config = MagicMock()
        mock_config.get_environment.return_value = MagicMock()

        mock_extractor = MagicMock()
        mock_parsed = MagicMock()
        mock_parsed.dashboard_id = None
        mock_parsed.chart_id = 42
        mock_parsed.query_state = {}
        mock_parsed.partial_recovery = None
        mock_parsed.unresolved_references = None
        mock_extractor.parse_superset_link = AsyncMock(return_value=mock_parsed)
        mock_extractor.client = MagicMock()

        from src.dependencies import get_config_manager
        with patch("src.core.utils.superset_context_extractor.SupersetContextExtractor", return_value=mock_extractor):
            client = _make_client({get_config_manager: lambda: mock_config})
            resp = client.post("/validation-tasks/parse-url", json=self.PARSE_PAYLOAD)
        assert resp.status_code == 200
        assert resp.json()["title"] == "chart:42"

    def test_environment_not_found(self):
        mock_config = MagicMock()
        mock_config.get_environment.return_value = None

        from src.dependencies import get_config_manager
        client = _make_client({get_config_manager: lambda: mock_config})
        resp = client.post("/validation-tasks/parse-url", json=self.PARSE_PAYLOAD)
        assert resp.status_code == 404

    def test_value_error(self):
        mock_config = MagicMock()
        mock_config.get_environment.return_value = MagicMock()

        mock_extractor = MagicMock()
        mock_extractor.parse_superset_link = AsyncMock(side_effect=ValueError("Invalid URL"))

        from src.dependencies import get_config_manager
        with patch("src.core.utils.superset_context_extractor.SupersetContextExtractor", return_value=mock_extractor):
            client = _make_client({get_config_manager: lambda: mock_config})
            resp = client.post("/validation-tasks/parse-url", json=self.PARSE_PAYLOAD)
        assert resp.status_code == 422

    def test_unexpected_error(self):
        mock_config = MagicMock()
        mock_config.get_environment.return_value = MagicMock()

        mock_extractor = MagicMock()
        mock_extractor.parse_superset_link = AsyncMock(side_effect=RuntimeError("Network error"))

        from src.dependencies import get_config_manager
        with patch("src.core.utils.superset_context_extractor.SupersetContextExtractor", return_value=mock_extractor):
            client = _make_client({get_config_manager: lambda: mock_config})
            resp = client.post("/validation-tasks/parse-url", json=self.PARSE_PAYLOAD)
        assert resp.status_code == 502


# ── legacy_llm_report_redirect ──

class TestLegacyLlmReportRedirect:
    """GET /validation-tasks/reports/llm/{task_id}"""

    def test_found(self):
        mock_db = MagicMock()
        mock_run = MagicMock()
        mock_run.task_id = "old-task"
        mock_run.policy_id = "policy-1"
        mock_run.id = "run-1"
        mock_db.query.return_value.filter.return_value.first.return_value = mock_run

        from src.core.database import get_db
        client = _make_client({get_db: lambda: mock_db})
        resp = client.get("/validation-tasks/reports/llm/old-task", follow_redirects=False)
        assert resp.status_code == 302
        assert "/validation-tasks/policy-1/runs/run-1" in resp.headers["location"]

    def test_not_found(self):
        mock_db = MagicMock()
        mock_db.query.return_value.filter.return_value.first.return_value = None

        from src.core.database import get_db
        client = _make_client({get_db: lambda: mock_db})
        resp = client.get("/validation-tasks/reports/llm/unknown")
        assert resp.status_code == 404
# #endregion Test.Api.ValidationTasks.Comprehensive
