"""Report API endpoints."""

from datetime import datetime
from pathlib import Path
from uuid import UUID, uuid4

from fastapi import (
    APIRouter,
    Depends,
    Query,
    status,
    BackgroundTasks,
    Request,
)
from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession
from slowapi import Limiter
from slowapi.util import get_remote_address

from src.api.deps import get_db
from src.core.config import settings
from src.core.exceptions import NotFoundException, ValidationException
from src.repositories.scenario import scenario_repository
from src.repositories.report import report_repository
from src.schemas.report import (
    ReportCreateRequest,
    ReportResponse,
    ReportList,
    ReportStatus,
    ReportStatusResponse,
    ReportGenerateResponse,
    ReportFormat,
)
from src.services.report_service import report_service

# Separate routers for different route groups
scenario_reports_router = APIRouter()
reports_router = APIRouter()

# In-memory store for report generation status (use Redis in production)
_report_status_store: dict[UUID, dict] = {}

# Rate limiter for downloads
limiter = Limiter(key_func=get_remote_address)


def _update_report_status(
    report_id: UUID,
    status: ReportStatus,
    progress: int = 0,
    message: str = None,
    file_path: str = None,
    file_size_bytes: int = None,
):
    """Update report generation status in store."""
    _report_status_store[report_id] = {
        "status": status,
        "progress": progress,
        "message": message,
        "file_path": file_path,
        "file_size_bytes": file_size_bytes,
        "completed_at": datetime.now()
        if status in [ReportStatus.COMPLETED, ReportStatus.FAILED]
        else None,
    }


async def _generate_report_task(
    db: AsyncSession,
    scenario_id: UUID,
    report_id: UUID,
    request_data: ReportCreateRequest,
):
    """Background task for report generation."""
    try:
        _update_report_status(
            report_id,
            ReportStatus.PROCESSING,
            progress=10,
            message="Compiling metrics...",
        )

        if request_data.format == ReportFormat.PDF:
            _update_report_status(
                report_id,
                ReportStatus.PROCESSING,
                progress=30,
                message="Generating PDF...",
            )
            file_path = await report_service.generate_pdf(
                db=db,
                scenario_id=scenario_id,
                report_id=report_id,
                include_sections=[s.value for s in request_data.sections],
                date_from=request_data.date_from,
                date_to=request_data.date_to,
            )
        else:  # CSV
            _update_report_status(
                report_id,
                ReportStatus.PROCESSING,
                progress=30,
                message="Generating CSV...",
            )
            file_path = await report_service.generate_csv(
                db=db,
                scenario_id=scenario_id,
                report_id=report_id,
                include_logs=request_data.include_logs,
                date_from=request_data.date_from,
                date_to=request_data.date_to,
            )

        # Update report with file size
        file_size = file_path.stat().st_size
        await report_repository.update_file_size(db, report_id, file_size)

        _update_report_status(
            report_id,
            ReportStatus.COMPLETED,
            progress=100,
            message="Report generation completed",
            file_path=str(file_path),
            file_size_bytes=file_size,
        )
    except Exception as e:
        _update_report_status(
            report_id,
            ReportStatus.FAILED,
            progress=0,
            message=f"Report generation failed: {str(e)}",
        )


# Scenario-scoped routes (prefixed with /scenarios)
@scenario_reports_router.post(
    "/{scenario_id}/reports",
    response_model=ReportGenerateResponse,
    status_code=status.HTTP_202_ACCEPTED,
)
async def create_report(
    scenario_id: UUID,
    request_data: ReportCreateRequest,
    background_tasks: BackgroundTasks,
    db: AsyncSession = Depends(get_db),
):
    """Generate a report for a scenario.

    Returns 202 Accepted with report_id. Use GET /reports/{id}/status to check progress.
    """
    # Validate scenario exists
    scenario = await scenario_repository.get(db, scenario_id)
    if not scenario:
        raise NotFoundException("Scenario")

    # Create report record
    report_id = uuid4()
    await report_repository.create(
        db,
        obj_in={
            "id": report_id,
            "scenario_id": scenario_id,
            "format": request_data.format.value,
            "file_path": str(
                report_service._get_file_path(
                    scenario_id, report_id, request_data.format.value
                )
            ),
            "generated_by": "api",
            "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,
            },
        },
    )

    # Initialize status
    _update_report_status(
        report_id,
        ReportStatus.PENDING,
        progress=0,
        message="Report queued for generation",
    )

    # Start background task
    background_tasks.add_task(
        _generate_report_task,
        db,
        scenario_id,
        report_id,
        request_data,
    )

    return ReportGenerateResponse(
        report_id=report_id,
        status=ReportStatus.PENDING,
        message="Report generation started. Check status at /reports/{id}/status",
    )


@scenario_reports_router.get(
    "/{scenario_id}/reports",
    response_model=ReportList,
)
async def list_reports(
    scenario_id: UUID,
    page: int = Query(1, ge=1, description="Page number"),
    page_size: int = Query(
        settings.default_page_size,
        ge=1,
        le=settings.max_page_size,
        description="Items per page",
    ),
    db: AsyncSession = Depends(get_db),
):
    """List all reports for a scenario."""
    # Validate scenario exists
    scenario = await scenario_repository.get(db, scenario_id)
    if not scenario:
        raise NotFoundException("Scenario")

    skip = (page - 1) * page_size

    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)

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


# Report-scoped routes (prefixed with /reports)
@reports_router.get(
    "/{report_id}/status",
    response_model=ReportStatusResponse,
)
async def get_report_status(
    report_id: UUID,
    db: AsyncSession = Depends(get_db),
):
    """Get the status of a report generation."""
    report = await report_repository.get(db, report_id)
    if not report:
        raise NotFoundException("Report")

    # Check in-memory status store
    status_info = _report_status_store.get(report_id, {})

    return ReportStatusResponse(
        report_id=report_id,
        status=status_info.get("status", ReportStatus.PENDING),
        progress=status_info.get("progress", 0),
        message=status_info.get("message"),
        file_path=status_info.get("file_path") or report.file_path,
        file_size_bytes=status_info.get("file_size_bytes") or report.file_size_bytes,
        created_at=report.created_at,
        completed_at=status_info.get("completed_at"),
    )


@reports_router.get(
    "/{report_id}/download",
    responses={
        200: {
            "description": "Report file download",
            "content": {
                "application/pdf": {},
                "text/csv": {},
            },
        },
    },
)
@limiter.limit(f"{settings.reports_rate_limit_per_minute}/minute")
async def download_report(
    request: Request,
    report_id: UUID,
    db: AsyncSession = Depends(get_db),
):
    """Download a generated report file.

    Rate limited to 10 downloads per minute.
    """
    report = await report_repository.get(db, report_id)
    if not report:
        raise NotFoundException("Report")

    # Check if report is completed
    status_info = _report_status_store.get(report_id, {})
    if status_info.get("status") != ReportStatus.COMPLETED:
        raise ValidationException("Report is not ready for download yet")

    file_path = Path(report.file_path)
    if not file_path.exists():
        raise NotFoundException("Report file")

    # Determine media type
    media_type = "application/pdf" if report.format == "pdf" else "text/csv"
    extension = report.format

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

    return FileResponse(
        path=file_path,
        media_type=media_type,
        filename=filename,
        headers={
            "Content-Disposition": f'attachment; filename="{filename}"',
        },
    )


@reports_router.delete(
    "/{report_id}",
    status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_report(
    report_id: UUID,
    db: AsyncSession = Depends(get_db),
):
    """Delete a report and its associated file."""
    report = await report_repository.get(db, report_id)
    if not report:
        raise NotFoundException("Report")

    # Delete file if it exists
    file_path = Path(report.file_path)
    if file_path.exists():
        file_path.unlink()

    # Delete from database
    await report_repository.delete(db, id=report_id)

    # Clean up status store
    _report_status_store.pop(report_id, None)

    return None
