"""
Video endpoints for SLTK API.

Handles video discovery, info, and streaming.
"""

from __future__ import annotations

import logging
import os
from collections.abc import Generator
from pathlib import Path

from fastapi import APIRouter, Header, HTTPException, Query
from fastapi.responses import FileResponse, StreamingResponse

from sltk.api.dependencies import safe_error_detail, validate_path
from sltk.api.models import (
    VideoDiscoveryRequest,
    VideoDiscoveryResponse,
    VideoInfo,
)

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


def get_video_media_type(suffix: str) -> str:
    """Get media type for video file extension."""
    media_types = {
        ".mp4": "video/mp4",
        ".webm": "video/webm",
        ".mov": "video/quicktime",
        ".avi": "video/x-msvideo",
        ".mkv": "video/x-matroska",
    }
    return media_types.get(suffix.lower(), "video/mp4")


@router.post("/videos/discover", response_model=VideoDiscoveryResponse)
async def discover_videos(request: VideoDiscoveryRequest):
    """
    Discover all video files in a directory.

    Returns list of videos with metadata.
    """
    # Security: Validate path is within allowed directories
    root = validate_path(request.root_path, must_exist=True)
    if not root.is_dir():
        raise HTTPException(400, f"Not a directory: {request.root_path}")

    videos = []
    total_size: float = 0

    # Build set of extensions for quick lookup
    extensions_set = set(ext.lower() for ext in request.extensions)

    def scan_dir_safe(path: Path, recursive: bool) -> Generator[Path, None, None]:
        """Safely scan directory, handling permission errors gracefully."""
        if recursive:
            # Use os.walk with error handling for recursive scanning
            def handle_error(err: OSError):
                # Silently skip directories we can't access
                pass

            for dirpath, dirnames, filenames in os.walk(path, onerror=handle_error):
                dir_path = Path(dirpath)
                for filename in filenames:
                    if any(filename.lower().endswith(ext) for ext in extensions_set):
                        yield dir_path / filename
        else:
            # Non-recursive: just list the directory
            try:
                for item in path.iterdir():
                    if item.is_file() and any(
                        item.name.lower().endswith(ext) for ext in extensions_set
                    ):
                        yield item
            except PermissionError:
                pass

    for video_path in scan_dir_safe(root, request.recursive):
        try:
            stat = video_path.stat()
            size_mb = stat.st_size / (1024 * 1024)
            total_size += size_mb

            # Get video info (optional, can be slow)
            info = VideoInfo(
                path=str(video_path),
                name=video_path.name,
                size_mb=round(size_mb, 2),
            )

            # Try to get video metadata
            try:
                from sltk.extraction.base import get_video_info

                vinfo = get_video_info(video_path)
                info.fps = vinfo.get("fps")
                info.width = vinfo.get("width")
                info.height = vinfo.get("height")
                info.num_frames = vinfo.get("num_frames")
                if info.fps and info.num_frames:
                    info.duration_sec = info.num_frames / info.fps
            except (OSError, ValueError, RuntimeError) as e:
                logger.warning(f"Metadata extraction failed for {video_path}: {e}")

            videos.append(info)
        except (OSError, ValueError, RuntimeError) as e:
            logger.debug(f"Skipping inaccessible video file {video_path}: {e}")
            continue  # Skip files we can't access

    return VideoDiscoveryResponse(
        root_path=str(root),
        videos=videos,
        total_count=len(videos),
        total_size_mb=round(total_size, 2),
    )


@router.get("/videos/info")
async def get_video_info_endpoint(path: str):
    """Get detailed info about a single video."""
    video_path = validate_path(path, must_exist=True)

    from sltk.extraction.base import get_video_info

    try:
        info = get_video_info(video_path)
        stat = video_path.stat()
        return {
            "path": str(video_path),
            "name": video_path.name,
            "size_mb": round(stat.st_size / (1024 * 1024), 2),
            "fps": info.get("fps"),
            "width": info.get("width"),
            "height": info.get("height"),
            "num_frames": info.get("num_frames"),
            "duration_sec": (
                info["num_frames"] / info["fps"] if info.get("fps") and info["fps"] > 0 else None
            ),
        }
    except Exception as e:
        raise HTTPException(500, safe_error_detail("Failed to get video info", e))


@router.get("/video/stream")
async def stream_video(
    path: str = Query(..., description="Path to the video file"),
    range: str | None = Header(None, alias="Range"),
):
    """
    Stream video file with range request support for seeking.

    Supports HTTP Range requests for efficient video seeking in browsers.
    Returns partial content (206) for range requests, full content (200) otherwise.
    """
    video_path = validate_path(path, must_exist=True)

    valid_extensions = [".mp4", ".mov", ".avi", ".mkv", ".webm"]
    if video_path.suffix.lower() not in valid_extensions:
        raise HTTPException(400, f"Not a video file. Supported: {valid_extensions}")

    file_size = video_path.stat().st_size
    media_type = get_video_media_type(video_path.suffix)

    # Handle range request for video seeking
    if range:
        # Parse range header: "bytes=start-end" or "bytes=start-"
        try:
            range_str = range.replace("bytes=", "")
            range_parts = range_str.split("-")
            start = int(range_parts[0]) if range_parts[0] else 0
            end = int(range_parts[1]) if range_parts[1] else file_size - 1
        except (ValueError, IndexError):
            raise HTTPException(400, "Invalid Range header format")

        # Validate range
        if start >= file_size:
            raise HTTPException(416, "Range not satisfiable")
        end = min(end, file_size - 1)
        chunk_size = end - start + 1

        def iterfile():
            with open(video_path, "rb") as f:
                f.seek(start)
                remaining = chunk_size
                while remaining > 0:
                    read_size = min(remaining, 1024 * 1024)  # 1MB chunks
                    data = f.read(read_size)
                    if not data:
                        break
                    remaining -= len(data)
                    yield data

        headers = {
            "Content-Range": f"bytes {start}-{end}/{file_size}",
            "Accept-Ranges": "bytes",
            "Content-Length": str(chunk_size),
        }

        return StreamingResponse(
            iterfile(),
            status_code=206,
            media_type=media_type,
            headers=headers,
        )

    # No range request - return full file
    return FileResponse(
        video_path,
        media_type=media_type,
        filename=video_path.name,
        headers={"Accept-Ranges": "bytes"},
    )
