"""Annotation export endpoints (COCO, PNG masks)."""

import io
import json
import logging
import os
import zipfile
from datetime import datetime
from typing import Any, Dict, List

import numpy as np
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session

from database import get_db
from models import Project, Annotation, Frame, Template

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/export", tags=["Export"])


def _mask_from_polygon(
    polygon: List[List[float]],
    width: int,
    height: int,
) -> np.ndarray:
    """Render a normalized polygon to a binary mask."""
    import cv2

    pts = np.array(
        [[int(p[0] * width), int(p[1] * height)] for p in polygon],
        dtype=np.int32,
    )
    mask = np.zeros((height, width), dtype=np.uint8)
    cv2.fillPoly(mask, [pts], 255)
    return mask


@router.get(
    "/{project_id}/coco",
    summary="Export annotations in COCO format",
)
def export_coco(project_id: int, db: Session = Depends(get_db)) -> StreamingResponse:
    """Export all annotations for a project as a COCO-format JSON file."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    annotations = (
        db.query(Annotation)
        .filter(Annotation.project_id == project_id)
        .all()
    )
    frames = (
        db.query(Frame)
        .filter(Frame.project_id == project_id)
        .order_by(Frame.frame_index)
        .all()
    )
    templates = db.query(Template).all()

    # Build COCO structure
    images = []
    for idx, frame in enumerate(frames):
        images.append({
            "id": frame.id,
            "file_name": frame.image_url,
            "width": frame.width or 1920,
            "height": frame.height or 1080,
            "frame_index": idx,
        })

    categories = []
    template_id_to_cat_id: Dict[int, int] = {}
    for cat_idx, tmpl in enumerate(templates, start=1):
        categories.append({
            "id": cat_idx,
            "name": tmpl.name,
            "color": tmpl.color,
        })
        template_id_to_cat_id[tmpl.id] = cat_idx

    coco_annotations = []
    ann_id = 1
    for ann in annotations:
        if not ann.mask_data:
            continue
        polygons = ann.mask_data.get("polygons", [])
        if not polygons:
            continue

        # Use first polygon for bbox / area approximation
        first_poly = polygons[0]
        xs = [p[0] for p in first_poly]
        ys = [p[1] for p in first_poly]
        width = ann.frame.width if ann.frame else 1920
        height = ann.frame.height if ann.frame else 1080
        bbox = [
            min(xs) * width,
            min(ys) * height,
            (max(xs) - min(xs)) * width,
            (max(ys) - min(ys)) * height,
        ]
        area = bbox[2] * bbox[3]

        segmentation = []
        for poly in polygons:
            flat = []
            for p in poly:
                flat.append(p[0] * width)
                flat.append(p[1] * height)
            segmentation.append(flat)

        coco_annotations.append({
            "id": ann_id,
            "image_id": ann.frame_id,
            "category_id": template_id_to_cat_id.get(ann.template_id, 0),
            "segmentation": segmentation,
            "area": area,
            "bbox": bbox,
            "iscrowd": 0,
        })
        ann_id += 1

    coco = {
        "info": {
            "description": f"Annotations for {project.name}",
            "version": "1.0",
            "year": datetime.now().year,
            "date_created": datetime.now().isoformat(),
        },
        "images": images,
        "annotations": coco_annotations,
        "categories": categories,
    }

    data = json.dumps(coco, ensure_ascii=False, indent=2).encode("utf-8")
    filename = f"project_{project_id}_coco.json"

    return StreamingResponse(
        io.BytesIO(data),
        media_type="application/json",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )


@router.get(
    "/{project_id}/masks",
    summary="Export PNG masks as a ZIP archive",
)
def export_masks(project_id: int, db: Session = Depends(get_db)) -> StreamingResponse:
    """Export all annotation masks as individual PNG files inside a ZIP archive."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    annotations = (
        db.query(Annotation)
        .filter(Annotation.project_id == project_id)
        .all()
    )

    zip_buffer = io.BytesIO()
    with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
        for ann in annotations:
            if not ann.mask_data:
                continue
            polygons = ann.mask_data.get("polygons", [])
            if not polygons:
                continue

            width = ann.frame.width if ann.frame else 1920
            height = ann.frame.height if ann.frame else 1080
            combined = np.zeros((height, width), dtype=np.uint8)

            for poly in polygons:
                mask = _mask_from_polygon(poly, width, height)
                combined = np.maximum(combined, mask)

            # Encode PNG
            import cv2
            _, encoded = cv2.imencode(".png", combined)
            fname = f"mask_{ann.id:06d}.png"
            zf.writestr(fname, encoded.tobytes())

    zip_buffer.seek(0)
    filename = f"project_{project_id}_masks.zip"

    return StreamingResponse(
        zip_buffer,
        media_type="application/zip",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )
