# app/modules/monitoring/routes/api/admin_tests.py
"""
Test Runner API Endpoints
RESTful API for running pytest and viewing test results
"""

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

from app.api.deps import get_current_admin_api
from app.core.database import get_db
from app.modules.dev_tools.services.test_runner_service import test_runner_service
from app.modules.tenancy.schemas.auth import UserContext

admin_tests_router = APIRouter(prefix="/tests")


# Pydantic Models for API


class TestRunResponse(BaseModel):
    """Response model for a test run"""

    id: int
    timestamp: str
    total_tests: int
    passed: int
    failed: int
    errors: int
    skipped: int
    xfailed: int
    xpassed: int
    pass_rate: float
    duration_seconds: float
    coverage_percent: float | None
    triggered_by: str | None
    git_commit_hash: str | None
    git_branch: str | None
    test_path: str | None
    status: str

    class Config:
        from_attributes = True


class TestResultResponse(BaseModel):
    """Response model for a single test result"""

    id: int
    node_id: str
    test_name: str
    test_file: str
    test_class: str | None
    outcome: str
    duration_seconds: float
    error_message: str | None
    traceback: str | None

    class Config:
        from_attributes = True


class RunTestsRequest(BaseModel):
    """Request model for running tests"""

    test_path: str = Field("tests", description="Path to tests to run")
    extra_args: list[str] | None = Field(
        None, description="Additional pytest arguments"
    )


class TestDashboardStatsResponse(BaseModel):
    """Response model for dashboard statistics"""

    # Current run stats
    total_tests: int
    passed: int
    failed: int
    errors: int
    skipped: int
    pass_rate: float
    duration_seconds: float
    coverage_percent: float | None
    last_run: str | None
    last_run_status: str | None

    # Collection stats
    total_test_files: int
    collected_tests: int
    unit_tests: int
    integration_tests: int
    performance_tests: int
    system_tests: int
    last_collected: str | None

    # Trend and breakdown data
    trend: list[dict]
    by_category: dict
    top_failing: list[dict]


# API Endpoints


@admin_tests_router.post("/run", response_model=TestRunResponse)
async def run_tests(
    background_tasks: BackgroundTasks,
    request: RunTestsRequest | None = None,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_admin_api),
):
    """
    Start a pytest run in the background

    Requires admin authentication. Creates a test run record and starts
    pytest execution in the background. Returns immediately with the run ID.
    Poll GET /runs/{run_id} to check status.
    """
    test_path = request.test_path if request else "tests"
    extra_args = request.extra_args if request else None

    # Create the test run record
    run = test_runner_service.create_test_run(
        db,
        test_path=test_path,
        triggered_by=f"manual:{current_user.username}",
    )
    db.commit()

    # Dispatch via task dispatcher (supports Celery or BackgroundTasks)
    from app.tasks.dispatcher import task_dispatcher

    celery_task_id = task_dispatcher.dispatch_test_run(
        background_tasks=background_tasks,
        run_id=run.id,
        test_path=test_path,
        extra_args=extra_args,
    )

    # Store Celery task ID if using Celery
    if celery_task_id:
        run.celery_task_id = celery_task_id
        db.commit()

    return TestRunResponse(
        id=run.id,
        timestamp=run.timestamp.isoformat(),
        total_tests=run.total_tests,
        passed=run.passed,
        failed=run.failed,
        errors=run.errors,
        skipped=run.skipped,
        xfailed=run.xfailed,
        xpassed=run.xpassed,
        pass_rate=run.pass_rate,
        duration_seconds=run.duration_seconds,
        coverage_percent=run.coverage_percent,
        triggered_by=run.triggered_by,
        git_commit_hash=run.git_commit_hash,
        git_branch=run.git_branch,
        test_path=run.test_path,
        status=run.status,
    )


@admin_tests_router.get("/runs", response_model=list[TestRunResponse])
async def list_runs(
    limit: int = Query(20, ge=1, le=100, description="Number of runs to return"),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_admin_api),
):
    """
    Get test run history

    Returns recent test runs for trend analysis.
    """
    runs = test_runner_service.get_run_history(db, limit=limit)

    return [
        TestRunResponse(
            id=run.id,
            timestamp=run.timestamp.isoformat(),
            total_tests=run.total_tests,
            passed=run.passed,
            failed=run.failed,
            errors=run.errors,
            skipped=run.skipped,
            xfailed=run.xfailed,
            xpassed=run.xpassed,
            pass_rate=run.pass_rate,
            duration_seconds=run.duration_seconds,
            coverage_percent=run.coverage_percent,
            triggered_by=run.triggered_by,
            git_commit_hash=run.git_commit_hash,
            git_branch=run.git_branch,
            test_path=run.test_path,
            status=run.status,
        )
        for run in runs
    ]


@admin_tests_router.get("/runs/{run_id}", response_model=TestRunResponse)
async def get_run(
    run_id: int,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_admin_api),
):
    """
    Get a specific test run
    """
    run = test_runner_service.get_run_by_id(db, run_id)

    if not run:
        from app.exceptions.base import ResourceNotFoundException

        raise ResourceNotFoundException("TestRun", str(run_id))

    return TestRunResponse(
        id=run.id,
        timestamp=run.timestamp.isoformat(),
        total_tests=run.total_tests,
        passed=run.passed,
        failed=run.failed,
        errors=run.errors,
        skipped=run.skipped,
        xfailed=run.xfailed,
        xpassed=run.xpassed,
        pass_rate=run.pass_rate,
        duration_seconds=run.duration_seconds,
        coverage_percent=run.coverage_percent,
        triggered_by=run.triggered_by,
        git_commit_hash=run.git_commit_hash,
        git_branch=run.git_branch,
        test_path=run.test_path,
        status=run.status,
    )


@admin_tests_router.get("/runs/{run_id}/results", response_model=list[TestResultResponse])
async def get_run_results(
    run_id: int,
    outcome: str | None = Query(
        None, description="Filter by outcome (passed, failed, error, skipped)"
    ),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_admin_api),
):
    """
    Get test results for a specific run
    """
    results = test_runner_service.get_run_results(db, run_id, outcome=outcome)

    return [
        TestResultResponse(
            id=r.id,
            node_id=r.node_id,
            test_name=r.test_name,
            test_file=r.test_file,
            test_class=r.test_class,
            outcome=r.outcome,
            duration_seconds=r.duration_seconds,
            error_message=r.error_message,
            traceback=r.traceback,
        )
        for r in results
    ]


@admin_tests_router.get("/runs/{run_id}/failures", response_model=list[TestResultResponse])
async def get_run_failures(
    run_id: int,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_admin_api),
):
    """
    Get failed tests from a specific run
    """
    failures = test_runner_service.get_failed_tests(db, run_id)

    return [
        TestResultResponse(
            id=r.id,
            node_id=r.node_id,
            test_name=r.test_name,
            test_file=r.test_file,
            test_class=r.test_class,
            outcome=r.outcome,
            duration_seconds=r.duration_seconds,
            error_message=r.error_message,
            traceback=r.traceback,
        )
        for r in failures
    ]


@admin_tests_router.get("/stats", response_model=TestDashboardStatsResponse)
async def get_dashboard_stats(
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_admin_api),
):
    """
    Get dashboard statistics

    Returns comprehensive stats for the testing dashboard including:
    - Total counts by outcome
    - Pass rate
    - Trend data
    - Tests by category
    - Top failing tests
    """
    stats = test_runner_service.get_dashboard_stats(db)
    return TestDashboardStatsResponse(**stats)


@admin_tests_router.post("/collect")
async def collect_tests(
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_admin_api),
):
    """
    Collect test information without running tests

    Updates the test collection cache with current test counts.
    """
    collection = test_runner_service.collect_tests(db)
    db.commit()

    return {
        "total_tests": collection.total_tests,
        "total_files": collection.total_files,
        "unit_tests": collection.unit_tests,
        "integration_tests": collection.integration_tests,
        "performance_tests": collection.performance_tests,
        "system_tests": collection.system_tests,
        "collected_at": collection.collected_at.isoformat(),
    }
