"""
Test Registry - Test Endpoints

Endpoints for test discovery, running, and monitoring.
"""

from datetime import datetime
from typing import Dict, Any

from fastapi import APIRouter, HTTPException, BackgroundTasks

from ...models import (
    TestFramework,
    TestRegistryStats,
    SERVICE_DEFINITIONS,
)
from ..api_models import TestRunResponse, RegistryResponse
from ..config import (
    PROJECT_ROOT,
    RUN_MODE,
    check_go_available,
    check_pytest_available,
    get_go_version,
    get_pytest_version,
    get_test_runs,
    get_current_runs,
    get_running_tests,
)
from ..discovery import (
    build_service_info,
    discover_go_tests,
    discover_python_tests,
    discover_bqas_tests,
)
from ..executors import execute_test_run

router = APIRouter()


@router.get("/registry", response_model=RegistryResponse)
async def get_test_registry():
    """
    Gibt alle registrierten Tests zurueck.
    Scannt alle Services und aggregiert Test-Informationen.
    """
    services = []
    stats = TestRegistryStats()

    by_category: Dict[str, int] = {}
    by_framework: Dict[str, int] = {}

    for service_def in SERVICE_DEFINITIONS:
        info = build_service_info(service_def)

        services.append({
            "service": info.service,
            "display_name": info.display_name,
            "port": info.port,
            "language": info.language,
            "total_tests": info.total_tests,
            "passed_tests": info.passed_tests,
            "failed_tests": info.failed_tests,
            "skipped_tests": info.skipped_tests,
            "pass_rate": round(info.pass_rate, 1),
            "coverage_percent": round(info.coverage_percent, 1) if info.coverage_percent else None,
            "last_run": info.last_run.isoformat() if info.last_run else None,
            "status": info.status.value,
        })

        stats.total_tests += info.total_tests
        stats.total_passed += info.passed_tests
        stats.total_failed += info.failed_tests
        stats.total_skipped += info.skipped_tests

        # Framework-Stats
        framework_name = service_def["framework"].value
        by_framework[framework_name] = by_framework.get(framework_name, 0) + info.total_tests

        # Category basierend auf Framework
        if service_def["framework"] in [TestFramework.BQAS_GOLDEN, TestFramework.BQAS_RAG]:
            by_category["bqas"] = by_category.get("bqas", 0) + info.total_tests
        elif service_def["framework"] == TestFramework.PLAYWRIGHT:
            by_category["e2e"] = by_category.get("e2e", 0) + info.total_tests
        else:
            by_category["unit"] = by_category.get("unit", 0) + info.total_tests

    stats.services_count = len(services)
    stats.overall_pass_rate = (stats.total_passed / stats.total_tests * 100) if stats.total_tests > 0 else 0.0
    stats.by_category = by_category
    stats.by_framework = by_framework

    return RegistryResponse(
        services=services,
        stats={
            "total_tests": stats.total_tests,
            "total_passed": stats.total_passed,
            "total_failed": stats.total_failed,
            "total_skipped": stats.total_skipped,
            "overall_pass_rate": round(stats.overall_pass_rate, 1),
            "services_count": stats.services_count,
            "by_category": stats.by_category,
            "by_framework": stats.by_framework,
        },
        last_updated=datetime.now().isoformat(),
    )


@router.get("/registry/{service}")
async def get_service_tests(service: str):
    """
    Gibt Tests fuer einen spezifischen Service zurueck.
    """
    service_def = next((s for s in SERVICE_DEFINITIONS if s["service"] == service), None)
    if not service_def:
        raise HTTPException(status_code=404, detail=f"Service '{service}' nicht gefunden")

    info = build_service_info(service_def)
    base_path = PROJECT_ROOT / service_def["base_path"].lstrip("/")
    framework = service_def["framework"]

    # Test-Discovery
    if framework == TestFramework.GO_TEST:
        tests = discover_go_tests(base_path)
    elif framework == TestFramework.PYTEST:
        tests = discover_python_tests(base_path)
    elif framework in [TestFramework.BQAS_GOLDEN, TestFramework.BQAS_RAG]:
        test_type = "golden" if framework == TestFramework.BQAS_GOLDEN else "rag"
        tests = discover_bqas_tests(base_path, test_type)
    else:
        tests = []

    return {
        "service": info.service,
        "display_name": info.display_name,
        "port": info.port,
        "language": info.language,
        "total_tests": len(tests),
        "passed_tests": info.passed_tests,
        "failed_tests": info.failed_tests,
        "coverage_percent": info.coverage_percent,
        "tests": [
            {
                "id": t.id,
                "name": t.name,
                "file_path": t.file_path,
                "line_number": t.line_number,
                "framework": t.framework.value,
                "status": t.status.value,
            }
            for t in tests
        ],
    }


@router.post("/run/{suite}", response_model=TestRunResponse)
async def run_test_suite(suite: str, background_tasks: BackgroundTasks):
    """
    Startet einen Test-Run fuer eine Suite.
    Fuehrt Tests im Hintergrund aus.
    """
    service_def = next((s for s in SERVICE_DEFINITIONS if s["service"] == suite), None)
    if not service_def:
        raise HTTPException(status_code=404, detail=f"Suite '{suite}' nicht gefunden")

    run_id = f"run_{suite}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"

    # Background Task starten
    background_tasks.add_task(execute_test_run, run_id, service_def)

    return TestRunResponse(
        run_id=run_id,
        status="queued",
        message=f"Test-Run fuer {service_def['display_name']} gestartet",
    )


@router.get("/runs")
async def get_test_runs_list(limit: int = 20):
    """
    Gibt die Test-Run Historie zurueck.
    """
    test_runs = get_test_runs()
    # Sortiert nach Startzeit, neueste zuerst
    sorted_runs = sorted(test_runs, key=lambda r: r["started_at"], reverse=True)
    return {"runs": sorted_runs[:limit]}


@router.get("/progress/{service_id}")
async def get_test_progress(service_id: str):
    """
    Gibt den Fortschritt eines laufenden Tests zurueck.
    Wird vom Frontend gepollt um Live-Updates anzuzeigen.
    """
    running_tests = get_running_tests()
    if service_id in running_tests:
        return running_tests[service_id]

    # Kein laufender Test - Standard-Antwort
    return {
        "current_file": "",
        "files_done": 0,
        "files_total": 0,
        "passed": 0,
        "failed": 0,
        "status": "idle"
    }


@router.get("/progress")
async def get_all_progress():
    """
    Gibt den Fortschritt aller laufenden Tests zurueck.
    """
    return get_running_tests()


@router.get("/runs/{run_id}")
async def get_test_run(run_id: str):
    """
    Gibt Details zu einem spezifischen Test-Run zurueck.
    """
    current_runs = get_current_runs()
    test_runs = get_test_runs()

    if run_id in current_runs:
        run = current_runs[run_id]
        return {
            "id": run.id,
            "suite_id": run.suite_id,
            "service": run.service,
            "started_at": run.started_at.isoformat(),
            "completed_at": run.completed_at.isoformat() if run.completed_at else None,
            "status": run.status.value,
            "total_tests": run.total_tests,
            "passed_tests": run.passed_tests,
            "failed_tests": run.failed_tests,
            "duration_seconds": run.duration_seconds,
            "output": run.output,
        }

    # In Historie suchen
    for run in test_runs:
        if run["id"] == run_id:
            return run

    raise HTTPException(status_code=404, detail=f"Run '{run_id}' nicht gefunden")


@router.get("/coverage")
async def get_coverage():
    """
    Gibt aggregierte Coverage-Informationen zurueck.
    """
    coverage_data = []
    total_coverage = 0.0
    count = 0

    for service_def in SERVICE_DEFINITIONS:
        info = build_service_info(service_def)
        if info.coverage_percent:
            coverage_data.append({
                "service": info.service,
                "display_name": info.display_name,
                "coverage_percent": round(info.coverage_percent, 1),
                "language": info.language,
            })
            total_coverage += info.coverage_percent
            count += 1

    return {
        "services": coverage_data,
        "average_coverage": round(total_coverage / count, 1) if count > 0 else 0,
        "total_services": count,
    }


@router.get("/health")
async def get_test_health():
    """
    Gibt den Status der Test-Infrastruktur zurueck.
    """
    go_available = check_go_available()
    pytest_available = check_pytest_available()

    return {
        "status": "healthy",
        "mode": RUN_MODE,  # "docker", "local", oder "demo"
        "services_monitored": len(SERVICE_DEFINITIONS),
        "project_root": str(PROJECT_ROOT),
        "project_root_exists": PROJECT_ROOT.exists(),
        "timestamp": datetime.now().isoformat(),
        "runners": {
            "go_test": "available" if go_available else "not_installed",
            "pytest": "available" if pytest_available else "not_installed",
            "jest": "available",  # TODO: check Node.js
            "playwright": "available",  # TODO: check Playwright
            "bqas": "available",  # BQAS hat seinen eigenen Service
        },
        "versions": {
            "go": get_go_version() if go_available else None,
            "pytest": get_pytest_version() if pytest_available else None,
        },
    }


@router.get("/db-status")
async def get_db_status():
    """
    Gibt den Status der PostgreSQL-Datenbankverbindung zurueck.
    Wird vom Dashboard ServiceStatus verwendet.
    """
    import time
    from ...database import check_db_connection, DATABASE_URL

    start_time = time.time()
    is_connected = check_db_connection()
    response_time = int((time.time() - start_time) * 1000)

    # Parse host from DATABASE_URL (hide password)
    try:
        # postgresql://user:pass@host:port/db -> host:port
        url_parts = DATABASE_URL.split("@")
        if len(url_parts) > 1:
            host_part = url_parts[1].split("/")[0]
        else:
            host_part = "unknown"
    except:
        host_part = "unknown"

    return {
        "status": "online" if is_connected else "offline",
        "host": host_part,
        "response_time_ms": response_time,
        "timestamp": datetime.now().isoformat(),
    }
