from __future__ import annotations

from datetime import datetime, timezone
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.templating import Jinja2Templates

from dependencies import get_unit_of_work, require_any_role
from schemas.exports import (
    ExportFormat,
    ProjectExportRequest,
    ScenarioExportRequest,
)
from services.export_serializers import (
    export_projects_to_excel,
    export_scenarios_to_excel,
    stream_projects_to_csv,
    stream_scenarios_to_csv,
)
from services.unit_of_work import UnitOfWork

router = APIRouter(prefix="/exports", tags=["exports"])


@router.get(
    "/modal/{dataset}",
    response_model=None,
    response_class=HTMLResponse,
    include_in_schema=False,
    name="exports.modal",
)
async def export_modal(
    dataset: str,
    request: Request,
) -> HTMLResponse:
    dataset = dataset.lower()
    if dataset not in {"projects", "scenarios"}:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Unknown dataset")

    submit_url = request.url_for(
        "export_projects" if dataset == "projects" else "export_scenarios"
    )
    templates = Jinja2Templates(directory="templates")
    return templates.TemplateResponse(
        request,
        "exports/modal.html",
        {
            "dataset": dataset,
            "submit_url": submit_url,
        },
    )


def _timestamp_suffix() -> str:
    return datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")


def _ensure_repository(repo, name: str):
    if repo is None:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                            detail=f"{name} repository unavailable")
    return repo


@router.post(
    "/projects",
    status_code=status.HTTP_200_OK,
    response_class=StreamingResponse,
    dependencies=[Depends(require_any_role(
        "admin", "project_manager", "analyst"))],
)
async def export_projects(
    request: ProjectExportRequest,
    uow: Annotated[UnitOfWork, Depends(get_unit_of_work)],
) -> Response:
    project_repo = _ensure_repository(
        getattr(uow, "projects", None), "Project")
    projects = project_repo.filtered_for_export(request.filters)

    filename = f"projects-{_timestamp_suffix()}"

    if request.format == ExportFormat.CSV:
        stream = stream_projects_to_csv(projects)
        response = StreamingResponse(stream, media_type="text/csv")
        response.headers["Content-Disposition"] = f"attachment; filename={filename}.csv"
        return response

    data = export_projects_to_excel(projects)
    return StreamingResponse(
        iter([data]),
        media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        headers={
            "Content-Disposition": f"attachment; filename={filename}.xlsx",
        },
    )


@router.post(
    "/scenarios",
    status_code=status.HTTP_200_OK,
    response_class=StreamingResponse,
    dependencies=[Depends(require_any_role(
        "admin", "project_manager", "analyst"))],
)
async def export_scenarios(
    request: ScenarioExportRequest,
    uow: Annotated[UnitOfWork, Depends(get_unit_of_work)],
) -> Response:
    scenario_repo = _ensure_repository(
        getattr(uow, "scenarios", None), "Scenario")
    scenarios = scenario_repo.filtered_for_export(
        request.filters, include_project=True)

    filename = f"scenarios-{_timestamp_suffix()}"

    if request.format == ExportFormat.CSV:
        stream = stream_scenarios_to_csv(scenarios)
        response = StreamingResponse(stream, media_type="text/csv")
        response.headers["Content-Disposition"] = f"attachment; filename={filename}.csv"
        return response

    data = export_scenarios_to_excel(scenarios)
    return StreamingResponse(
        iter([data]),
        media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        headers={
            "Content-Disposition": f"attachment; filename={filename}.xlsx",
        },
    )
