"""REST API Endpoints for Flight Management."""

from __future__ import annotations

import asyncio
import json
from typing import Annotated

from fastapi import APIRouter, File, Form, HTTPException, Path, UploadFile
from fastapi import Depends as _Depends
from sse_starlette.sse import EventSourceResponse

from gps_denied.api.deps import ProcessorDep, SessionDep, verify_token
from gps_denied.schemas.flight import (
    BatchMetadata,
    BatchResponse,
    BatchUpdateResponse,
    DeleteResponse,
    FlightCreateRequest,
    FlightDetailResponse,
    FlightResponse,
    FlightStatusResponse,
    ObjectGPSResponse,
    ObjectToGPSRequest,
    UpdateResponse,
    UserFixRequest,
    UserFixResponse,
    Waypoint,
)

router = APIRouter(prefix="/flights", tags=["flights"], dependencies=[_Depends(verify_token)])


@router.post("", response_model=FlightResponse, status_code=201)
async def create_flight(
    req: FlightCreateRequest,
    processor: ProcessorDep,
    session: SessionDep,
) -> FlightResponse:
    """Create a new flight and trigger prefetching."""
    res = await processor.create_flight(req)
    await session.commit()
    return res


@router.get("/{flight_id}", response_model=FlightDetailResponse)
async def get_flight(
    flight_id: Annotated[str, Path(..., title="The ID of the flight")],
    processor: ProcessorDep,
) -> FlightDetailResponse:
    """Get complete flight information."""
    res = await processor.get_flight(flight_id)
    if not res:
        raise HTTPException(status_code=404, detail="Flight not found")
    return res


@router.delete("/{flight_id}", response_model=DeleteResponse)
async def delete_flight(
    flight_id: Annotated[str, Path(...)],
    processor: ProcessorDep,
    session: SessionDep,
) -> DeleteResponse:
    """Delete a flight and all associated data."""
    res = await processor.delete_flight(flight_id)
    if not res.deleted:
        raise HTTPException(status_code=404, detail="Flight not found")
    await session.commit()
    return res


@router.put("/{flight_id}/waypoints/{waypoint_id}", response_model=UpdateResponse)
async def update_waypoint(
    flight_id: Annotated[str, Path(...)],
    waypoint_id: Annotated[str, Path(...)],
    waypoint: Waypoint,
    processor: ProcessorDep,
    session: SessionDep,
) -> UpdateResponse:
    """Update a specific waypoint."""
    res = await processor.update_waypoint(flight_id, waypoint_id, waypoint)
    if not res.updated:
        raise HTTPException(status_code=404, detail="Waypoint or Flight not found")
    await session.commit()
    return res


@router.put("/{flight_id}/waypoints/batch", response_model=BatchUpdateResponse)
async def batch_update_waypoints(
    flight_id: Annotated[str, Path(...)],
    waypoints: list[Waypoint],
    processor: ProcessorDep,
    session: SessionDep,
) -> BatchUpdateResponse:
    """Batch update multiple waypoints."""
    res = await processor.batch_update_waypoints(flight_id, waypoints)
    await session.commit()
    return res


@router.post("/{flight_id}/images/batch", response_model=BatchResponse, status_code=202)
async def upload_image_batch(
    flight_id: Annotated[str, Path(...)],
    metadata: Annotated[str, Form(...)],
    images: list[UploadFile] = File(...),
    processor: ProcessorDep = None,  # type: ignore
    session: SessionDep = None,  # type: ignore
) -> BatchResponse:
    """Upload a batch of UAV images."""
    try:
        meta_dict = json.loads(metadata)
        meta_obj = BatchMetadata(**meta_dict)
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Invalid metadata JSON: {e}")

    f_info = await processor.get_flight(flight_id)
    if not f_info:
        raise HTTPException(status_code=404, detail="Flight not found")

    # P1#6: Batch size validation (allow 1-50 for dev, spec says 10-50)
    if len(images) < 1 or len(images) > 50:
        raise HTTPException(
            status_code=422,
            detail=f"Batch must contain 1-50 images, got {len(images)}",
        )

    res = await processor.queue_images(flight_id, meta_obj, len(images))

    # P1#5: Spawn background task to process each frame
    import cv2
    import numpy as np

    async def _process_batch():
        for idx, upload in enumerate(images):
            raw = await upload.read()
            arr = np.frombuffer(raw, dtype=np.uint8)
            img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
            if img is not None:
                frame_id = meta_obj.start_frame_id + idx
                await processor.process_frame(flight_id, frame_id, img)

    asyncio.create_task(_process_batch())

    await session.commit()
    return res


@router.post("/{flight_id}/user-fix", response_model=UserFixResponse)
async def submit_user_fix(
    flight_id: Annotated[str, Path(...)],
    fix_data: UserFixRequest,
    processor: ProcessorDep,
    session: SessionDep,
) -> UserFixResponse:
    """Submit a verified GPS anchor to unblock processing."""
    res = await processor.handle_user_fix(flight_id, fix_data)
    await session.commit()
    return res


@router.post("/{flight_id}/frames/{frame_id}/object-to-gps", response_model=ObjectGPSResponse)
async def convert_object_to_gps(
    flight_id: Annotated[str, Path(...)],
    frame_id: Annotated[int, Path(...)],
    req: ObjectToGPSRequest,
    processor: ProcessorDep,
) -> ObjectGPSResponse:
    """Convert a pixel coordinate to GPS coordinate for an object."""
    return await processor.convert_object_to_gps(flight_id, frame_id, (req.pixel_x, req.pixel_y))


@router.get("/{flight_id}/status", response_model=FlightStatusResponse)
async def get_flight_status(
    flight_id: Annotated[str, Path(...)],
    processor: ProcessorDep,
) -> FlightStatusResponse:
    """Get processing status of a flight."""
    res = await processor.get_flight_status(flight_id)
    if not res:
        raise HTTPException(status_code=404, detail="Flight not found")
    return res


@router.get("/{flight_id}/stream")
async def create_sse_stream(
    flight_id: Annotated[str, Path(...)],
    processor: ProcessorDep,
) -> EventSourceResponse:
    """SSE endpoint for real-time processing events."""
    f_info = await processor.get_flight(flight_id)
    if not f_info:
        raise HTTPException(status_code=404, detail="Flight not found")

    return EventSourceResponse(processor.stream_events(flight_id, client_id="default"))
