#region ValidationTasksRoutes [C:4] [TYPE Module] [SEMANTICS fastapi, validation, api, task, run]
# @BRIEF API routes for validation task management — v2 CRUD with run history and Superset URL parsing.
# @LAYER API
# @RELATION DEPENDS_ON -> [ValidationTaskService]
# @RELATION DEPENDS_ON -> [ConfigManager]
# @RELATION DEPENDS_ON -> [TaskManager]
# @RELATION DEPENDS_ON -> [SupersetContextExtractor]

from datetime import datetime
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session

from ...core.config_manager import ConfigManager
from ...core.database import get_db
from ...core.logger import logger
from ...core.scheduler import SchedulerService
from ...core.task_manager import TaskManager
from ...dependencies import (
    get_config_manager,
    get_current_user,
    get_scheduler_service,
    get_task_manager,
    has_permission,
)
from ...schemas.auth import User
from ...schemas.validation import (
    RecordListResponse,
    RunDetailResponse,
    RunListResponse,
    RunResponse,
    TriggerRunResponse,
    ValidationTaskCreate,
    ValidationTaskListResponse,
    ValidationTaskResponse,
    ValidationTaskUpdate,
)
from ...services.validation_service import ValidationTaskService

# #region router [C:1] [TYPE Global]
# @BRIEF APIRouter instance for validation task routes.
router = APIRouter(tags=["Validation Tasks"])
# #endregion router


# #region ParseUrlRequest [C:1] [TYPE Class]
# @BRIEF Request body for the parse-superset-url endpoint.
class ParseUrlRequest(BaseModel):
    url: str = Field(..., description="Full Superset dashboard URL to parse")
    environment_id: str = Field(..., description="Target environment ID")
# #endregion ParseUrlRequest


# #region _get_task_service [C:1] [TYPE Function]
# @BRIEF Dependency injector for ValidationTaskService.
def _get_task_service(
    db: Session = Depends(get_db),
    config_manager: ConfigManager = Depends(get_config_manager),
    current_user: User = Depends(get_current_user),
) -> ValidationTaskService:
    return ValidationTaskService(db=db, config_manager=config_manager, username=current_user.username)
# #endregion _get_task_service


# #region _parse_dt [C:1] [TYPE Function]
# @BRIEF Parse ISO datetime string to datetime, or return None.
def _parse_dt(val: str | None) -> datetime | None:
    if val is None:
        return None
    try:
        return datetime.fromisoformat(val)
    except (ValueError, TypeError):
        return None
# #endregion _parse_dt


# #region _dict_to_run_response [C:1] [TYPE Function]
# @BRIEF Convert a run dict from ValidationTaskService to RunResponse model.
def _dict_to_run_response(d: dict[str, Any]) -> RunResponse:
    return RunResponse(
        id=d["id"],
        policy_id=d["policy_id"],
        task_id=d.get("task_id"),
        status=d["status"],
        trigger=d.get("trigger", "manual"),
        started_at=_parse_dt(d.get("started_at")),
        finished_at=_parse_dt(d.get("finished_at")),
        dashboard_count=d.get("dashboard_count", 0),
        pass_count=d.get("pass_count", 0),
        warn_count=d.get("warn_count", 0),
        fail_count=d.get("fail_count", 0),
        unknown_count=d.get("unknown_count", 0),
        created_at=_parse_dt(d.get("created_at")),
    )
# #endregion _dict_to_run_response


# ============================================================
# Tasks CRUD
# ============================================================


# #region create_task [C:3] [TYPE Function]
# @BRIEF Create a new validation task.
# @RELATION CALLS -> [ValidationTaskService.create_task]
# @RELATION CALLS -> [SchedulerService.reload_validation_policy]
@router.post("", response_model=ValidationTaskResponse, status_code=status.HTTP_201_CREATED)
async def create_task(
    payload: ValidationTaskCreate,
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.task", "CREATE")),
    service: ValidationTaskService = Depends(_get_task_service),
    scheduler: SchedulerService = Depends(get_scheduler_service),
):
    """Create a new validation task with provider and environment validation."""
    logger.reason(
        f"create_task — User: {current_user.username}, name: {payload.name}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        result = service.create_task(payload)
        scheduler.reload_validation_policy(result.id)
        return result
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e))
# #endregion create_task


# #region list_tasks [C:3] [TYPE Function]
# @BRIEF List all validation tasks with pagination and optional filters.
# @RELATION CALLS -> [ValidationTaskService.list_tasks]
@router.get("", response_model=ValidationTaskListResponse)
async def list_tasks(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    is_active: bool | None = Query(None),
    environment_id: str | None = Query(None),
    search: str | None = Query(None, description="Search by task name"),
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.task", "VIEW")),
    service: ValidationTaskService = Depends(_get_task_service),
):
    """List all validation tasks with pagination and optional filters."""
    logger.reason(
        f"list_tasks — User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        total, items = service.list_tasks(
            is_active=is_active, environment_id=environment_id, search=search,
            page=page, page_size=page_size,
        )
        return ValidationTaskListResponse(items=items, total=total, page=page, page_size=page_size)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
# #endregion list_tasks


# #region get_task [C:3] [TYPE Function]
# @BRIEF Get a validation task by policy ID.
# @RELATION CALLS -> [ValidationTaskService.get_task]
@router.get("/{policy_id}", response_model=ValidationTaskResponse)
async def get_task(
    policy_id: str,
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.task", "VIEW")),
    service: ValidationTaskService = Depends(_get_task_service),
):
    """Get a validation task by ID."""
    logger.reason(
        f"get_task — Policy: {policy_id}, User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        return service.get_task(policy_id)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
# #endregion get_task


# #region update_task [C:3] [TYPE Function]
# @BRIEF Update a validation task.
# @RELATION CALLS -> [ValidationTaskService.update_task]
# @RELATION CALLS -> [SchedulerService.reload_validation_policy]
@router.put("/{policy_id}", response_model=ValidationTaskResponse)
async def update_task(
    policy_id: str,
    payload: ValidationTaskUpdate,
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.task", "EDIT")),
    service: ValidationTaskService = Depends(_get_task_service),
    scheduler: SchedulerService = Depends(get_scheduler_service),
):
    """Update a validation task."""
    logger.reason(
        f"update_task — Policy: {policy_id}, User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        result = service.update_task(policy_id, payload)
        scheduler.reload_validation_policy(result.id)
        return result
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
# #endregion update_task


# #region delete_task [C:3] [TYPE Function]
# @BRIEF Delete a validation task.
# @RELATION CALLS -> [ValidationTaskService.delete_task]
# @RELATION CALLS -> [SchedulerService.remove_validation_job]
@router.delete("/{policy_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_task(
    policy_id: str,
    delete_runs: bool = Query(False, description="Also delete associated validation runs"),
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.task", "DELETE")),
    service: ValidationTaskService = Depends(_get_task_service),
    scheduler: SchedulerService = Depends(get_scheduler_service),
):
    """Delete a validation task."""
    logger.reason(
        f"delete_task — Policy: {policy_id}, User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        service.delete_task(policy_id, delete_runs=delete_runs)
        scheduler.remove_validation_job(policy_id)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
# #endregion delete_task


# #region toggle_task_status [C:3] [TYPE Function]
# @BRIEF Toggle a validation task's active status.
# @RELATION CALLS -> [ValidationTaskService.update_task]
# @RELATION CALLS -> [SchedulerService.remove_validation_job]
# @RELATION CALLS -> [SchedulerService.reload_validation_policy]
@router.patch("/{policy_id}/status", response_model=ValidationTaskResponse)
async def toggle_task_status(
    policy_id: str,
    is_active: bool = Query(..., description="New active status"),
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.task", "EDIT")),
    service: ValidationTaskService = Depends(_get_task_service),
    scheduler: SchedulerService = Depends(get_scheduler_service),
):
    """Toggle a validation task's active status."""
    logger.reason(
        f"toggle_task_status — Policy: {policy_id}, is_active: {is_active}, User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        result = service.update_task(policy_id, ValidationTaskUpdate(is_active=is_active))
        if not is_active:
            scheduler.remove_validation_job(policy_id)
        else:
            scheduler.reload_validation_policy(policy_id)
        return result
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
# #endregion toggle_task_status


# ============================================================
# Run — trigger and history
# ============================================================


# #region trigger_run [C:3] [TYPE Function]
# @BRIEF Trigger immediate validation for a task.
# @RELATION CALLS -> [ValidationTaskService.trigger_run]
@router.post("/{policy_id}/run", response_model=TriggerRunResponse)
async def trigger_run(
    policy_id: str,
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.task", "EXECUTE")),
    service: ValidationTaskService = Depends(_get_task_service),
    task_manager: TaskManager = Depends(get_task_manager),
):
    """Trigger immediate validation for a task."""
    logger.reason(
        f"trigger_run — Policy: {policy_id}, User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        result = await service.trigger_run(policy_id, task_manager)
        return TriggerRunResponse(
            task_id=policy_id,
            spawned_task_id=result["task_id"],
            run_id=result.get("run_id"),
            status="PENDING",
            message="Validation task spawned",
        )
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e))
# #endregion trigger_run


# #region list_all_runs [C:3] [TYPE Function]
# @BRIEF List validation runs across all tasks with optional filters.
# @RELATION CALLS -> [ValidationTaskService.list_all_runs]
@router.get("/runs/all", response_model=RecordListResponse)
async def list_all_runs(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    status_filter: str | None = Query(None, alias="status", description="Filter by status"),
    environment_id: str | None = Query(None, description="Filter by environment"),
    dashboard_id: str | None = Query(None, description="Filter by dashboard ID"),
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.run", "VIEW")),
    service: ValidationTaskService = Depends(_get_task_service),
):
    """List validation runs across all tasks with pagination and optional filters."""
    logger.reason(
        f"list_all_runs — User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        total, raw_items = service.list_all_runs(
            status=status_filter, environment_id=environment_id, dashboard_id=dashboard_id,
            page=page, page_size=page_size,
        )
        return RecordListResponse(items=raw_items, total=total, page=page, page_size=page_size)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
# #endregion list_all_runs


# #region list_runs [C:3] [TYPE Function]
# @BRIEF List run history for a validation task.
# @RELATION CALLS -> [ValidationTaskService.get_run_history]
@router.get("/{policy_id}/runs", response_model=RunListResponse)
async def list_runs(
    policy_id: str,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.run", "VIEW")),
    service: ValidationTaskService = Depends(_get_task_service),
):
    """List run history for a validation task."""
    logger.reason(
        f"list_runs — Policy: {policy_id}, User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        total, raw_runs = service.get_run_history(policy_id, page=page, page_size=page_size)
        items = [_dict_to_run_response(r) for r in raw_runs]
        return RunListResponse(items=items, total=total, page=page, page_size=page_size)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
# #endregion list_runs


# #region get_run_detail [C:3] [TYPE Function]
# @BRIEF Get full run detail with all records for a validation task.
# @RELATION CALLS -> [ValidationTaskService.get_run_detail]
@router.get("/{policy_id}/runs/{run_id}", response_model=RunDetailResponse)
async def get_run_detail(
    policy_id: str,
    run_id: str,
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("validation.run", "VIEW")),
    service: ValidationTaskService = Depends(_get_task_service),
):
    """Get full run detail with all records."""
    logger.reason(
        f"get_run_detail — Policy: {policy_id}, Run: {run_id}, User: {current_user.username}",
        extra={"src": "validation_tasks_routes"},
    )
    try:
        result = service.get_run_detail(run_id)
        return RunDetailResponse(
            run=_dict_to_run_response(result["run"]),
            records=result.get("records", []),
        )
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
# #endregion get_run_detail


# ============================================================
# Utilities
# ============================================================


# #region parse_superset_url [C:3] [TYPE Function]
# @BRIEF Parse a Superset dashboard URL and extract dashboard context, native filters, and recovery info.
# @RELATION CALLS -> [SupersetContextExtractor.parse_superset_link]
@router.post("/parse-url")
async def parse_superset_url(
    payload: ParseUrlRequest,
    current_user: User = Depends(get_current_user),
    config_manager: ConfigManager = Depends(get_config_manager),
):
    """Parse a Superset dashboard URL and extract context."""
    logger.reason(
        f"parse_url — User: {current_user.username}, env: {payload.environment_id}",
        extra={"src": "validation_tasks_routes"},
    )
    env = config_manager.get_environment(payload.environment_id)
    if not env:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Environment '{payload.environment_id}' not found",
        )

    try:
        from ...core.utils.superset_context_extractor import SupersetContextExtractor

        extractor = SupersetContextExtractor(environment=env)
        parsed = extractor.parse_superset_link(payload.url)

        dashboard_id = str(parsed.dashboard_id) if parsed.dashboard_id is not None else None
        title: str | None = None

        # Resolve dashboard title from Superset API when possible
        if parsed.dashboard_id:
            try:
                dashboard = extractor.client.get_dashboard_detail(parsed.dashboard_id)
                title = dashboard.get("dashboard_title", str(parsed.dashboard_id))
            except Exception:
                title = str(parsed.dashboard_id)
        elif parsed.chart_id:
            title = f"chart:{parsed.chart_id}"

        query_state = parsed.query_state or {}
        native_filters = query_state.get("native_filters")
        active_tabs = query_state.get("activeTabs")
        anchor = query_state.get("anchor")

        return {
            "dashboard_id": dashboard_id,
            "title": title,
            "native_filters": native_filters,
            "activeTabs": active_tabs,
            "anchor": anchor,
            "partial_recovery": parsed.partial_recovery,
            "warnings": list(parsed.unresolved_references) if parsed.unresolved_references else [],
        }
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e))
    except Exception as e:
        logger.error(
            f"parse_url failed: {e}",
            extra={"src": "validation_tasks_routes"},
        )
        raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e))
# #endregion parse_superset_url


# #region legacy_llm_report_redirect [C:2] [TYPE Function]
# @BRIEF Legacy redirect: /reports/llm/{taskId} → /validation-tasks/{policy_id}/runs/{run_id}
# @RELATION CALLS -> [ValidationRun]
@router.get("/reports/llm/{task_id}")
async def legacy_llm_report_redirect(
    task_id: str,
    db: Session = Depends(get_db),
):
    """
    Legacy redirect: /reports/llm/{taskId} → /validation-tasks/{policy_id}/runs/{run_id}
    
    Returns 302 redirect if task_id maps to a ValidationRun.task_id.
    Returns 404 if no match.
    """
    from ...models.llm import ValidationRun
    
    run = db.query(ValidationRun).filter(ValidationRun.task_id == task_id).first()
    if not run:
        raise HTTPException(status_code=404, detail="LLM validation run not found for this task")
    
    from fastapi.responses import RedirectResponse
    target = f"/validation-tasks/{run.policy_id}/runs/{run.id}"
    return RedirectResponse(url=target, status_code=302)
# #endregion legacy_llm_report_redirect


# #endregion ValidationTasksRoutes
