"""
Pose data endpoints for SLTK API.

Handles loading and querying pose data from H5 files.
"""

from __future__ import annotations

import asyncio
import logging

import numpy as np
from fastapi import APIRouter, HTTPException

from sltk.api.dependencies import safe_error_detail, validate_path

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

# Maximum frames to serialize in pose loading endpoint
MAX_POSE_FRAMES = 5000


@router.get("/load")
async def load_poses(
    path: str,
    start_frame: int = 0,
    end_frame: int | None = None,
    subsample: int = 1,
    max_frames: int = MAX_POSE_FRAMES,
):
    """
    Load pose data from H5 file.

    Returns pose keypoints as JSON for visualization.
    If the data exceeds max_frames, automatic subsampling is applied.
    """
    h5_path = validate_path(path, must_exist=True)

    from sltk.io.h5 import get_h5_metadata, load_poses_h5

    def _load_sync():
        return get_h5_metadata(h5_path), load_poses_h5(h5_path)

    try:
        metadata, poses = await asyncio.to_thread(_load_sync)

        # Slice if requested
        if end_frame is not None:
            poses = poses[start_frame:end_frame]
        else:
            poses = poses[start_frame:]

        # Subsample if requested
        if subsample > 1:
            poses = poses[::subsample]

        # Check if we need automatic subsampling to stay under frame limit
        warning = None
        original_frames = poses.num_frames
        if poses.num_frames > max_frames:
            # Calculate subsample factor to get under max_frames
            auto_subsample = (poses.num_frames // max_frames) + 1
            poses = poses[::auto_subsample]
            warning = (
                f"Data exceeded {max_frames} frames ({original_frames} frames). "
                f"Auto-subsampled by factor of {auto_subsample} to {poses.num_frames} frames."
            )

        result = {
            "metadata": metadata,
            "num_frames": poses.num_frames,
            "num_keypoints": poses.num_keypoints,
            "fps": poses.fps,
            "format": poses.format,
            "data": poses.data.tolist(),
            "confidence": poses.confidence.tolist() if poses.confidence is not None else None,
        }
        if warning:
            result["warning"] = warning
            result["original_num_frames"] = original_frames

        return result
    except Exception as e:
        raise HTTPException(500, safe_error_detail("Failed to load poses", e))


@router.get("/metadata")
async def get_poses_metadata(path: str):
    """Get metadata from an H5 pose file."""
    h5_path = validate_path(path, must_exist=True)

    from sltk.io.h5 import get_h5_metadata

    try:
        return await asyncio.to_thread(get_h5_metadata, h5_path)
    except Exception as e:
        raise HTTPException(500, safe_error_detail("Failed to get metadata", e))


@router.get("/frame")
async def get_pose_frame(
    path: str,
    frame: int,
):
    """Get pose data for a single frame."""
    h5_path = validate_path(path, must_exist=True)

    from sltk.io.h5 import load_poses_h5

    try:
        poses = await asyncio.to_thread(load_poses_h5, h5_path)

        if frame < 0 or frame >= poses.num_frames:
            raise HTTPException(400, f"Frame {frame} out of range [0, {poses.num_frames})")

        return {
            "frame": frame,
            "time": frame / poses.fps if poses.fps > 0 else 0.0,
            "data": poses.data[frame].tolist(),
            "confidence": (
                poses.confidence[frame].tolist() if poses.confidence is not None else None
            ),
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, safe_error_detail("Failed to get frame", e))


@router.get("/statistics")
async def get_pose_statistics(path: str):
    """
    Get detailed statistics about a pose file.
    """
    h5_path = validate_path(path, must_exist=True)

    from sltk.io.h5 import load_poses_h5

    try:
        poses = await asyncio.to_thread(load_poses_h5, h5_path)

        # Compute statistics
        data = poses.data
        valid_mask = np.any(data != 0, axis=(1, 2))
        valid_frames = valid_mask.sum()

        velocity = np.diff(data, axis=0)
        velocity_mag = np.linalg.norm(velocity, axis=(1, 2))

        return {
            "path": str(h5_path),
            "format": poses.format,
            "fps": poses.fps,
            "duration_sec": poses.duration,
            "total_frames": poses.num_frames,
            "valid_frames": int(valid_frames),
            "coverage_pct": round(valid_frames / max(poses.num_frames, 1) * 100, 1),
            "num_keypoints": poses.num_keypoints,
            "position_range": {
                "min": data[valid_mask].min().item() if valid_frames > 0 else 0,
                "max": data[valid_mask].max().item() if valid_frames > 0 else 0,
            },
            "velocity": {
                "mean": float(velocity_mag.mean()),
                "max": float(velocity_mag.max()),
                "std": float(velocity_mag.std()),
            },
        }
    except Exception as e:
        raise HTTPException(500, safe_error_detail("Failed to compute statistics", e))
