"""API v2 reports endpoints with async generation."""

from uuid import UUID
from datetime import datetime
from typing import Optional

from fastapi import (
    APIRouter,
    Depends,
    Query,
    status,
    Request,
    Header,
    BackgroundTasks,
)
from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession

from src.api.deps import get_db
from src.api.v2.rate_limiter import TieredRateLimit
from src.repositories.scenario import scenario_repository
from src.repositories.report import report_repository
from src.schemas.report import (
    ReportCreateRequest,
    ReportResponse,
    ReportList,
    ReportStatus,
    ReportFormat,
)
from src.core.exceptions import NotFoundException, ValidationException
from src.core.config import settings
from src.core.cache import cache_manager
from src.core.monitoring import metrics
from src.core.audit_logger import audit_logger, AuditEventType
from src.tasks.reports import generate_pdf_report, generate_csv_report


router = APIRouter()
rate_limiter = TieredRateLimit()


@router.post(
    "/{scenario_id}",
    response_model=dict,
    status_code=status.HTTP_202_ACCEPTED,
    summary="Generate report",
    description="Generate a report asynchronously using Celery.",
    responses={
        202: {"description": "Report generation queued"},
        404: {"description": "Scenario not found"},
        429: {"description": "Rate limit exceeded"},
    },
)
async def create_report(
    request: Request,
    scenario_id: UUID,
    request_data: ReportCreateRequest,
    background_tasks: BackgroundTasks,
    db: AsyncSession = Depends(get_db),
    x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
    x_user_id: Optional[str] = Header(None, alias="X-User-ID"),
):
    """Generate a report for a scenario asynchronously.

    The report generation is queued and processed in the background.
    Use the returned report_id to check status and download when ready.

    - **scenario_id**: ID of the scenario to generate report for
    - **format**: Report format (pdf or csv)
    - **sections**: Sections to include (for PDF)
    - **include_logs**: Include log entries (for CSV)
    - **date_from**: Optional start date filter
    - **date_to**: Optional end date filter
    """
    # Rate limiting (stricter for report generation)
    await rate_limiter.check_rate_limit(request, x_api_key, tier="premium", burst=5)

    # Validate scenario
    scenario = await scenario_repository.get(db, scenario_id)
    if not scenario:
        raise NotFoundException("Scenario")

    # Create report record
    from uuid import uuid4

    report_id = uuid4()

    report = await report_repository.create(
        db,
        obj_in={
            "id": report_id,
            "scenario_id": scenario_id,
            "format": request_data.format.value,
            "file_path": f"{settings.reports_storage_path}/{scenario_id}/{report_id}.{request_data.format.value}",
            "generated_by": "api_v2",
            "status": "pending",
            "extra_data": {
                "include_logs": request_data.include_logs,
                "sections": [s.value for s in request_data.sections],
                "date_from": request_data.date_from.isoformat()
                if request_data.date_from
                else None,
                "date_to": request_data.date_to.isoformat()
                if request_data.date_to
                else None,
            },
        },
    )

    # Queue report generation task
    if request_data.format == ReportFormat.PDF:
        task = generate_pdf_report.delay(
            scenario_id=str(scenario_id),
            report_id=str(report_id),
            include_sections=[s.value for s in request_data.sections],
            date_from=request_data.date_from.isoformat()
            if request_data.date_from
            else None,
            date_to=request_data.date_to.isoformat() if request_data.date_to else None,
        )
    else:
        task = generate_csv_report.delay(
            scenario_id=str(scenario_id),
            report_id=str(report_id),
            include_logs=request_data.include_logs,
            date_from=request_data.date_from.isoformat()
            if request_data.date_from
            else None,
            date_to=request_data.date_to.isoformat() if request_data.date_to else None,
        )

    # Audit log
    audit_logger.log(
        event_type=AuditEventType.REPORT_GENERATED,
        action="queue_report_generation",
        user_id=UUID(x_user_id) if x_user_id else None,
        resource_type="report",
        resource_id=report_id,
        ip_address=request.client.host if request.client else None,
        details={
            "scenario_id": str(scenario_id),
            "format": request_data.format.value,
            "task_id": task.id,
        },
    )

    return {
        "report_id": str(report_id),
        "task_id": task.id,
        "status": "queued",
        "message": "Report generation queued. Check status at /api/v2/reports/{id}/status",
        "status_url": f"/api/v2/reports/{report_id}/status",
    }


@router.get(
    "/{report_id}/status",
    response_model=dict,
    summary="Get report status",
    description="Get the status of a report generation task.",
)
async def get_report_status(
    request: Request,
    report_id: UUID,
    db: AsyncSession = Depends(get_db),
    x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
):
    """Get the status of a report generation."""
    # Rate limiting
    await rate_limiter.check_rate_limit(request, x_api_key, tier="free")

    report = await report_repository.get(db, report_id)
    if not report:
        raise NotFoundException("Report")

    # Get task status from Celery
    from src.core.celery_app import celery_app

    task_id = report.extra_data.get("task_id") if report.extra_data else None

    task_status = None
    if task_id:
        result = celery_app.AsyncResult(task_id)
        task_status = {
            "state": result.state,
            "info": result.info if result.state != "PENDING" else None,
        }

    return {
        "report_id": str(report_id),
        "status": report.status,
        "format": report.format,
        "created_at": report.created_at.isoformat() if report.created_at else None,
        "completed_at": report.completed_at.isoformat()
        if report.completed_at
        else None,
        "file_size_bytes": report.file_size_bytes,
        "task_status": task_status,
        "download_url": f"/api/v2/reports/{report_id}/download"
        if report.status == "completed"
        else None,
    }


@router.get(
    "/{report_id}/download",
    summary="Download report",
    description="Download a generated report file.",
    responses={
        200: {"description": "Report file"},
        404: {"description": "Report not found or not ready"},
        429: {"description": "Rate limit exceeded"},
    },
)
async def download_report(
    request: Request,
    report_id: UUID,
    db: AsyncSession = Depends(get_db),
    x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
    x_user_id: Optional[str] = Header(None, alias="X-User-ID"),
):
    """Download a generated report file.

    Rate limited to prevent abuse.
    """
    # Rate limiting (strict for downloads)
    await rate_limiter.check_rate_limit(request, x_api_key, tier="free", burst=10)

    # Check cache for report metadata
    cache_key = f"report:{report_id}"
    cached = await cache_manager.get(cache_key)

    if cached:
        report_data = cached
    else:
        report = await report_repository.get(db, report_id)
        if not report:
            raise NotFoundException("Report")
        report_data = {
            "id": str(report.id),
            "scenario_id": str(report.scenario_id),
            "format": report.format,
            "file_path": report.file_path,
            "status": report.status,
            "file_size_bytes": report.file_size_bytes,
        }
        # Cache for short time
        await cache_manager.set(cache_key, report_data, ttl=60)

    # Check if report is ready
    if report_data["status"] != "completed":
        raise ValidationException("Report is not ready for download yet")

    from pathlib import Path

    file_path = Path(report_data["file_path"])
    if not file_path.exists():
        raise NotFoundException("Report file")

    # Audit log
    audit_logger.log(
        event_type=AuditEventType.REPORT_DOWNLOADED,
        action="download_report",
        user_id=UUID(x_user_id) if x_user_id else None,
        resource_type="report",
        resource_id=report_id,
        ip_address=request.client.host if request.client else None,
        details={
            "format": report_data["format"],
            "file_size": report_data["file_size_bytes"],
        },
    )

    # Track metrics
    metrics.increment_counter(
        "reports_downloaded_total",
        labels={"format": report_data["format"]},
    )

    # Get scenario name for filename
    scenario = await scenario_repository.get(db, UUID(report_data["scenario_id"]))
    filename = (
        f"{scenario.name}_{datetime.now().strftime('%Y-%m-%d')}.{report_data['format']}"
    )

    media_type = "application/pdf" if report_data["format"] == "pdf" else "text/csv"

    return FileResponse(
        path=file_path,
        media_type=media_type,
        filename=filename,
        headers={
            "X-Report-ID": str(report_id),
            "X-Report-Format": report_data["format"],
        },
    )


@router.get(
    "",
    response_model=ReportList,
    summary="List reports",
    description="List all reports with filtering.",
)
async def list_reports(
    request: Request,
    scenario_id: Optional[UUID] = Query(None, description="Filter by scenario"),
    status: Optional[str] = Query(None, description="Filter by status"),
    format: Optional[str] = Query(None, description="Filter by format"),
    page: int = Query(1, ge=1),
    page_size: int = Query(settings.default_page_size, ge=1, le=settings.max_page_size),
    db: AsyncSession = Depends(get_db),
    x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
):
    """List reports with filtering and pagination."""
    # Rate limiting
    await rate_limiter.check_rate_limit(request, x_api_key, tier="free")

    skip = (page - 1) * page_size

    if scenario_id:
        reports = await report_repository.get_by_scenario(
            db, scenario_id, skip=skip, limit=page_size
        )
        total = await report_repository.count_by_scenario(db, scenario_id)
    else:
        reports = await report_repository.get_multi(db, skip=skip, limit=page_size)
        total = await report_repository.count(db)

    return ReportList(
        items=[ReportResponse.model_validate(r) for r in reports],
        total=total,
        page=page,
        page_size=page_size,
    )
