import os
import re
import shutil
import tempfile
import time
import uuid
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, BackgroundTasks, Request, status

from app.automation.browser_manager import browser_manager
from app.automation.runner import run_task
from app.automation.session_store import session_store
from app.automation.tasks.permissions import HudlForbiddenError
from app.core.auth_deps import AuthContext, require_auth, resolve_auth_from_token
from app.schemas.hudl_schema import (
    ExchangeSearchSchema,
    ExchangeTypeSchema,
    LoginSchema,
    VideoSelectSchema,
    PlaylistSelectSchema,
    StatsSchema,
    NotesSchema,
    DeleteFileSchema,
    VideoDetailsSchema,
    CancelSchema,
    BackSchema,
    ActiveExchangeDetailsSchema,
    ActiveExchangeActionSchema,
    VideoStreamSchema,
    LibraryActionBaseSchema,
    LibraryUpdateTypeSchema,
    LibraryUpdateEventSchema,
    LibraryRenameSchema,
    LibraryEmailDownloadSchema,
    LibraryDownloadToMediaSchema,
    LibraryDownloadByRefSchema,
    ManageLibraryDownloadSchema,
    ManageLibrarySelectSchema,
    ManageLibraryClipMetaSchema,
    SwitchHudlTeamSchema,
    OpenExchangeSchema,
)

# JWT required on every route below (from POST /api/v1/auth/login)
router = APIRouter(dependencies=[Depends(require_auth)])
stream_router = APIRouter()

# In-memory cache for active exchange details
# Keys: (exchange_id, href, name)
# Value: {"timestamp": float, "data": dict}
DETAILS_CACHE = {}
CACHE_TTL = 180  # 3 minutes in seconds

# Manage Library cache — avoids repeated slow Playwright scrapes
MANAGE_LIBRARY_CACHE: dict[str, dict] = {}
MANAGE_LIBRARY_SEASON_CACHE: dict[str, dict] = {}
MANAGE_LIBRARY_FOLDER_CACHE: dict[str, dict] = {}
MANAGE_LIBRARY_CACHE_TTL = 1800  # 30 minutes

# Background Manage Library downloads. Jobs are intentionally process-local:
# Playwright also runs in this process and serializes work through run_task().
MANAGE_LIBRARY_DOWNLOAD_JOBS: dict[str, dict] = {}
MANAGE_LIBRARY_DOWNLOAD_JOB_TTL = 86400  # 24 hours


def _hudl_http_error(exc: Exception) -> HTTPException:
    """Map automation errors to HTTP — preserve Hudl 403 permission pages."""
    if isinstance(exc, HTTPException):
        return exc
    if isinstance(exc, HudlForbiddenError):
        return HTTPException(status_code=403, detail=exc.as_dict())
    return HTTPException(status_code=500, detail=str(exc))


def _download_job_owner(auth: AuthContext) -> str:
    if auth.user is not None:
        return f"user:{auth.user.id}"
    if auth.account is not None:
        return f"account:{auth.account.id}"
    raise HTTPException(401, "Authentication required")


def _prune_download_jobs() -> None:
    cutoff = time.time() - MANAGE_LIBRARY_DOWNLOAD_JOB_TTL
    expired_ids = [
        job_id
        for job_id, job in MANAGE_LIBRARY_DOWNLOAD_JOBS.items()
        if job["created_at"] < cutoff
    ]
    for job_id in expired_ids:
        MANAGE_LIBRARY_DOWNLOAD_JOBS.pop(job_id, None)


async def _run_manage_library_download_job(job_id: str, body: ManageLibraryDownloadSchema) -> None:
    job = MANAGE_LIBRARY_DOWNLOAD_JOBS.get(job_id)
    if not job:
        return

    job.update(status="processing", message="Selecting items and starting the download on Hudl.")
    try:
        result = await run_task(
            "download_manage_library_items",
            team_id=body.team_id,
            season_label=body.season,
            items=[item.model_dump() for item in body.items],
            download_format=body.format,
            include_breakdown=body.include_breakdown,
        )
        job.update(
            status="completed",
            message=result.get(
                "message",
                "We'll email you a link when your video is ready to download.",
            ),
            result=result,
            completed_at=time.time(),
        )
    except Exception as exc:
        print(f"Manage Library background download error ({job_id}): {exc}")
        job.update(
            status="failed",
            message=str(exc) or "Failed to start the download on Hudl.",
            completed_at=time.time(),
        )


def _cache_get(cache: dict, key: str) -> dict | None:
    entry = cache.get(key)
    if not entry:
        return None
    import time
    if time.time() - entry["timestamp"] > MANAGE_LIBRARY_CACHE_TTL:
        cache.pop(key, None)
        return None
    return entry["data"]


def _cache_set(cache: dict, key: str, data: dict) -> None:
    import time
    cache[key] = {"timestamp": time.time(), "data": data}


async def _ensure_hudl_browser_ready() -> None:
    """Start the Manage Library lane. Login only if Hudl shows the identity form.

    Blank lane tabs share cookies with the rest of the browser context, so we do
    NOT treat about:blank as logged-out — that forced a full re-login on every
    download after a lane tab was created.
    """
    from app.automation.tasks.login import is_hudl_logged_in, is_login_page, run as login_run

    lane = "manage_library"
    async with browser_manager.lane_lock(lane):
        browser_manager.set_lane_busy(lane, True)
        try:
            page = await browser_manager.start(lane=lane)
            # Already on a Hudl app page → session is fine.
            if await is_hudl_logged_in(page) and not await is_login_page(page):
                return
            # Only force login when Hudl actually redirected to the identity form.
            # Blank tabs are left alone; task code navigates with shared cookies.
            if await is_login_page(page):
                print("Manage Library: session expired — logging in...")
                await login_run(page)
        finally:
            browser_manager.set_lane_busy(lane, False)
            browser_manager.record_activity()


@router.get("/status")
async def hudl_status():
    state = session_store.read()
    page = browser_manager.page
    storage_warning = session_store.get_storage_warning_cached()
    return {
        "browser_running": browser_manager.is_running,
        "logged_in": state.get("logged_in", False),
        "current_url": page.url if page and not page.is_closed() else None,
        "last_url": state.get("last_url"),
        "storage_warning": storage_warning,
        "lanes_busy": {
            "default": browser_manager.is_lane_busy("default"),
            "exchange": browser_manager.is_lane_busy("exchange"),
            "manage_library": browser_manager.is_lane_busy("manage_library"),
        },
    }


@router.get("/storage")
async def get_hudl_storage(
    team_id: str | None = None,
    refresh: bool = False,
):
    """Return Hudl storage usage for the sidebar — team-scoped when team_id is set."""
    tid = str(team_id or "").strip() or None
    if tid and not tid.isdigit():
        tid = None

    if not refresh:
        storage_warning = session_store.get_storage_warning_cached(team_id=tid)
        if storage_warning:
            return {"status": "ok", "storage_warning": storage_warning}

    try:
        from app.automation.tasks.login import run as login_run
        from app.automation.tasks.library import fetch_hudl_storage_usage

        page = await browser_manager.start()
        if not session_store.is_marked_logged_in():
            await login_run(page)
        storage_warning = await fetch_hudl_storage_usage(page, team_id=tid)
        session_store.set_storage_warning(storage_warning, team_id=tid)
        return {"status": "ok", "storage_warning": storage_warning}
    except Exception as exc:
        return {
            "status": "error",
            "message": str(exc),
            "storage_warning": {"available": False},
        }


@router.post("/browser/start")
async def start_browser():
    try:
        page = await browser_manager.start()
        return {"status": "ok", "url": page.url}
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/browser/clear-session")
async def clear_browser_session():
    """Clear the persistent browser session data completely and force a fresh login with automatic retries."""
    max_retries = 3
    last_exc = None
    for attempt in range(1, max_retries + 1):
        try:
            await browser_manager.clear_session()
            page = await browser_manager.start()
            from app.automation.tasks.login import run as login_run
            await login_run(page)
            return {"status": "ok", "message": "Browser session cleared and fresh login completed."}
        except Exception as exc:
            last_exc = exc
            print(f"Clear session attempt {attempt} failed: {exc}")
            try:
                await browser_manager.close()
            except Exception:
                pass
            if attempt < max_retries:
                import asyncio
                await asyncio.sleep(2)
    
    raise HTTPException(500, f"Failed to reset session after {max_retries} attempts. Error: {last_exc}")


@router.post("/logout")
async def hudl_logout():
    """Close the browser session and reset status upon user logout."""
    try:
        await browser_manager.close()
        state = session_store.read()
        state["logged_in"] = False
        session_store.write(state)
        return {"status": "ok", "message": "Browser session closed successfully."}
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/account-name")
async def get_hudl_account_name():
    """Returns the cached Hudl account holder's name or fetches it if not present."""
    name = session_store.get_account_holder_name()
    if not name:
        try:
            from app.automation.tasks.login import fetch_hudl_account_name, run as login_run
            page = await browser_manager.start()
            await login_run(page)
            name = await fetch_hudl_account_name(page)
        except Exception:
            name = ""
    return {"status": "ok", "name": name}


@router.post("/login")
async def hudl_site_login(body: LoginSchema = LoginSchema()):
    """Hudl website login (JWT already checked). App login = POST /api/v1/auth/login"""
    from app.automation.tasks.login import LoginVerificationError

    try:
        return await run_task("login", force=body.force)
    except (ValueError, LoginVerificationError) as exc:
        raise HTTPException(400, str(exc)) from exc
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/organizations")
async def list_hudl_organizations_api():
    """List every school/team from the Hudl team switcher (full Organizations sidebar)."""
    try:
        return await run_task("list_hudl_organizations")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/team/switch")
async def switch_hudl_team(body: SwitchHudlTeamSchema):
    """Click a Hudl team by id/name and return its team_id.

    Intentionally does NOT open Exchange here — that was blocking the switch
    modal for 30–90s. The Exchange page warms itself after navigation.
    """
    try:
        # Drop previous team's storage so the sidebar reloads for the new team.
        session_store.clear_storage_warning()
        result = await run_task(
            "switch_hudl_team_by_name",
            team_name=body.team_name,
            org_name=body.org_name,
            team_id=body.team_id,
        )
        # Optimistic: Exchange page will detect 403 and blacklist if needed.
        team_id = str(result.get("team_id") or "").strip()
        if team_id.isdigit():
            result.setdefault("has_exchange", True)
        return result
    except ValueError as exc:
        raise HTTPException(400, str(exc)) from exc
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/open")
async def open_exchange(body: OpenExchangeSchema | None = None):
    try:
        team_id = body.team_id if body else None
        return await run_task("open_exchange", team_id=team_id)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/exchange/data")
async def get_exchange_data(team_id: str | None = None):
    try:
        return await run_task("get_exchange_data", team_id=team_id)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/exchange/active")
async def get_active_exchanges_api(full_scroll: bool = False, team_id: str | None = None):
    """GET: Fetch list of active exchanges directly from page list elements."""
    try:
        return await run_task(
            "get_active_exchanges",
            full_scroll=full_scroll,
            team_id=team_id,
        )
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/active/details")
async def get_active_exchange_details_api(body: ActiveExchangeDetailsSchema):
    """POST: Fetch specific active exchange's dynamic ex-details by team name (live Playwright)."""
    try:
        res = await run_task(
            "get_active_exchange_details",
            exchange_name=body.name,
            exchange_index=body.index,
            exchange_id=body.exchange_id,
            href=body.href,
            team_id=body.team_id,
        )
        return res
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/incoming-videos/process")
async def process_incoming_video_exchanges_api():
    """POST: Auto-add all 'They sent you video' exchanges to the video library."""
    try:
        return await run_task("process_incoming_video_exchanges")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/active/action")
async def active_exchange_action_api(body: ActiveExchangeActionSchema):
    """POST: Click a header action button (delete/cancel/etc) on an active exchange."""
    try:
        res = await run_task(
            "click_active_exchange_button",
            action_type=body.action_type,
            exchange_name=body.name,
            exchange_index=body.index,
            exchange_id=body.exchange_id,
            href=body.href,
        )
        # State changed — drop cached details so the next fetch is fresh.
        if res and res.get("status") == "ok":
            for k in [
                k for k in DETAILS_CACHE
                if (body.exchange_id and k[0] == body.exchange_id)
                or (body.href and k[1] == body.href)
                or k[2] == body.name
            ]:
                DETAILS_CACHE.pop(k, None)
        return res
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/search")
async def exchange_search(body: ExchangeSearchSchema):
    """
    3rd API (POST): search box me keyword → loader wait → header text.
    File: app/automation/tasks/search.py → run_search()
    """
    try:
        return await run_task("run_search", query=body.query)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/exchange/search/teams")
async def exchange_search_teams():
    """
    3rd API (GET): search results ki team list (2nd API jaisa format).
    File: app/automation/tasks/search.py → get_search_teams()
  Call POST /exchange/search first.
    """
    try:
        return await run_task("get_search_teams")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/types")
async def exchange_types(body: ExchangeTypeSchema):
    """
    4th API: team id click → exchange type → Next.

    Code: app/automation/tasks/exchange_types.py

    Example body:
      {
        "team_id": "<opponent from GET /exchange/data suggested_teams>",
        "owner_team_id": "<Football/Basketball Hudl id from Organizations>",
        "team_source": "suggested",
        "exchange_type": "two-way"
      }
    """
    try:
        return await run_task(
            "select_exchange_type",
            team_id=body.team_id,
            team_source=body.team_source,
            exchange_type=body.exchange_type,
            owner_team_id=body.owner_team_id,
        )
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/exchange/videos")
async def get_video_list():
    """
    5th API (GET): get video list from events-container.

    Reads all <li> from //section[@id="events-container"]/div/ul.
    Skips li[@class="bad-status"] items (video still processing).

    Call after POST /exchange/types (when the video selection page is open).

    File: app/automation/tasks/videos.py → get_video_list()
    """
    try:
        return await run_task("get_video_list")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/videos/select")
async def select_videos(body: VideoSelectSchema):
    """
    6th API (POST): Select videos/games from the list.
    Football multi-step may return wizard_step=playlists (incl. empty No Playlists).
    """
    try:
        return await run_task("select_videos", video_indices=body.video_indices)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/playlists/select")
async def select_playlists(body: PlaylistSelectSchema):
    """Select playlists on Choose Playlist(s) and click Next when enabled."""
    try:
        return await run_task(
            "select_playlists", playlist_indices=body.playlist_indices
        )
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/exchange/stats")
async def get_stats_options():
    """
    7th API (GET): Get available options on the stats page before selecting.
    """
    try:
        return await run_task("get_stats_options")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/stats")
async def select_stats(body: StatsSchema):
    """
    7th API (POST): Optionally include stats and click next.
    Example body: {"include_stats": false}
    """
    try:
        return await run_task("select_stats", include_stats=body.include_stats)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/notes")
async def add_notes(body: NotesSchema):
    """
    8th API (POST): Add an optional message in the notes section and click next.
    Example body: {"message": "Here are the videos!"} (or omit/send null)
    """
    try:
        return await run_task("add_notes", message=body.message)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/exchange/confirm")
async def get_confirm_details():
    """
    9th API (GET): Read the confirmation screen details before submitting.
    Returns exchange_name, school_name, school_image, video_names, stats, message.
    """
    try:
        return await run_task("get_confirm_details")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/confirm")
async def submit_exchange():
    """
    9th API (POST): Click the Send Exchange button to finalize everything.
    """
    try:
        return await run_task("submit_exchange")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/cancel")
async def cancel_exchange(body: CancelSchema):
    """
    POST API: Cancel exchange wizard at current step index.
    """
    try:
        return await run_task("cancel_exchange", step_index=body.step_index)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/exchange/back")
async def go_back_exchange(body: BackSchema):
    """
    POST API: Click the physical back button at active index.
    """
    try:
        return await run_task("go_back_exchange", back_index=body.back_index)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/upload/open")
async def open_upload():
    """10th API (POST): Click the global upload navigation link."""
    try:
        return await run_task("open_upload")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/upload/team")
async def get_upload_team():
    """11th API (GET): Read the currently selected team from the combobox."""
    try:
        return await run_task("get_upload_team")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/upload/team/dropdown")
async def open_team_dropdown():
    """11th API (POST): Click the dropdown arrow to reveal the team list."""
    try:
        return await run_task("open_team_dropdown")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/upload/teams-list")
async def get_upload_teams_list():
    """12th API (GET): Read the list of available teams after dropdown is opened."""
    try:
        return await run_task("get_upload_teams_list")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


def delete_temp_file(path: str):
    """Background task to delete temporary file after upload."""
    if os.path.exists(path):
        os.remove(path)


@router.post("/upload/select-files")
async def select_files_api(
    background_tasks: BackgroundTasks,
    team_index: int | None = Form(None),
    file: UploadFile = File(...)
):
    """
    12th API (POST): Optionally select a team from the list by index, 
    and upload a file. The server will pass this file to Playwright automatically.
    """
    try:
        # Save uploaded file to a temporary location with its original filename
        unique_dir = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
        os.makedirs(unique_dir, exist_ok=True)
        tmp_path = os.path.join(unique_dir, file.filename)
        
        with open(tmp_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
            
        # We CANNOT delete the file immediately because the browser needs time to upload it over the network
        # after we click the upload button. The OS will clean up /tmp eventually, or we can add a cleanup cron.
        # background_tasks.add_task(delete_temp_file, tmp_path)
        
        return await run_task("select_files", team_index=team_index, file_path=tmp_path)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/upload/files")
async def get_uploaded_files():
    """13th API (GET): Read the list of uploaded files."""
    try:
        return await run_task("get_uploaded_files")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/upload/files/delete")
async def delete_uploaded_file(body: DeleteFileSchema):
    """13th API (POST): Delete an uploaded file by its index."""
    try:
        return await run_task("delete_uploaded_file", file_index=body.file_index)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/upload/trigger")
async def trigger_upload():
    """14th API (POST): Click the trigger upload button."""
    try:
        return await run_task("trigger_upload")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/upload/progress")
async def get_upload_progress():
    """14.5 API (GET): Fetch live upload progress (total and completed)."""
    try:
        return await run_task("get_upload_progress")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc



@router.post("/upload/schedule-event/open")
async def open_schedule_event():
    """15th API (POST): Click the Select-placeholder to open schedule events."""
    try:
        return await run_task("open_schedule_event")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/upload/schedule-events")
async def get_schedule_events():
    """
    15th API (GET): Fetch all parsed events from the Select-menu-outer dropdown.
    MUST be called after POST /upload/schedule-event/open so the menu is visible.
    """
    try:
        return await run_task("get_schedule_events")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/upload/details")
async def fill_video_details(body: VideoDetailsSchema):
    """
    16th API (POST): Fill out the details (video type, schedule event, title, save).
    """
    try:
        return await run_task(
            "fill_video_details",
            schedule_event_index=body.schedule_event_index,
            video_type=body.video_type,
            title=body.title,
            check_event=body.check_event
        )
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/upload/cancel")
async def cancel_upload():
    """
    17th API (POST): Cancel the current upload by clicking the cancel button and confirming.
    """
    try:
        return await run_task("cancel_upload")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/library/videos")
async def get_library_videos(team_id: str | None = None):
    """Fetch parsed video items from the Hudl library."""
    try:
        return await run_task("get_library_videos", team_id=team_id)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/manage-library")
async def get_manage_library(team_id: str | None = None, refresh: bool = True):
    """Fetch Manage Library seasons from Hudl via Playwright (no cache)."""
    try:
        await _ensure_hudl_browser_ready()
        result = await run_task("get_manage_library", team_id=team_id)
        return result
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.get("/manage-library/season")
async def expand_manage_library_season(
    team_id: str | None = None, season: str = "", refresh: bool = True
):
    """Expand a season on Hudl Library via Playwright (no cache)."""
    if not season.strip():
        raise HTTPException(400, "season query param is required")
    try:
        await _ensure_hudl_browser_ready()
        result = await run_task(
            "expand_manage_library_season",
            team_id=team_id,
            season_label=season,
        )
        return result
    except Exception as exc:
        import traceback

        print(f"manage-library/season 500 for {season!r}: {exc}")
        traceback.print_exc()
        raise _hudl_http_error(exc) from exc


@router.get("/manage-library/folder")
async def expand_manage_library_folder(
    team_id: str | None = None,
    season: str = "",
    path: str = "",
    refresh: bool = True,
):
    """Expand a folder path on Hudl Manage Library via Playwright (no cache)."""
    if not season.strip():
        raise HTTPException(400, "season query param is required")
    import re as _re
    _MONTH_STRIP = _re.compile(
        r'(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2}\b',
        _re.IGNORECASE,
    )
    _COUNT_STRIP = _re.compile(r'\s*\(\d+\)\s*$')
    folder_path = [
        _COUNT_STRIP.sub('', _MONTH_STRIP.sub('', p).strip()).strip()
        for p in path.split(",")
        if p.strip()
    ]
    folder_path = [p for p in folder_path if p]
    if not folder_path:
        raise HTTPException(400, "path query param is required (comma-separated folder labels)")
    try:
        await _ensure_hudl_browser_ready()
        result = await run_task(
            "expand_manage_library_folder",
            team_id=team_id,
            season_label=season,
            folder_path=folder_path,
        )
        return result
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/manage-library/download", status_code=status.HTTP_202_ACCEPTED)
async def download_manage_library_items(
    body: ManageLibraryDownloadSchema,
    background_tasks: BackgroundTasks,
    auth: AuthContext = Depends(require_auth),
):
    """Queue a background job that selects clips and triggers Hudl's email download."""
    if not body.items:
        raise HTTPException(400, "items is required")

    _prune_download_jobs()
    job_id = str(uuid.uuid4())
    MANAGE_LIBRARY_DOWNLOAD_JOBS[job_id] = {
        "job_id": job_id,
        "owner": _download_job_owner(auth),
        "status": "queued",
        "message": "Download queued. Sergio will complete the Hudl steps in the background.",
        "item_count": len(body.items),
        "items": [item.label for item in body.items],
        "created_at": time.time(),
        "completed_at": None,
        "result": None,
    }
    background_tasks.add_task(_run_manage_library_download_job, job_id, body)
    return {
        key: value
        for key, value in MANAGE_LIBRARY_DOWNLOAD_JOBS[job_id].items()
        if key != "owner"
    }


@router.post("/manage-library/select")
async def select_manage_library_clip(
    body: ManageLibrarySelectSchema,
    auth: AuthContext = Depends(require_auth),
):
    """Mirror a frontend clip selection onto the open Hudl Manage Library page.

    Best-effort: the download flow re-selects clips itself, so a failure here
    must never surface as a 500 or block the UI. It also skips while a download
    is in progress, since running a select on the same page would fight the
    download's own selection/navigation.
    """
    owner = _download_job_owner(auth)
    download_active = any(
        job.get("owner") == owner and job.get("status") in ("queued", "processing")
        for job in MANAGE_LIBRARY_DOWNLOAD_JOBS.values()
    )
    if download_active:
        return {
            "status": "skipped",
            "reason": "download_in_progress",
            "label": body.label,
            "selected": body.selected,
        }

    try:
        await _ensure_hudl_browser_ready()
        return await run_task(
            "set_manage_library_clip_selection",
            team_id=body.team_id,
            season_label=body.season,
            path=body.path,
            label=body.label,
            selected=body.selected,
        )
    except Exception as exc:
        # Non-fatal — log and report as a soft miss so the frontend stays happy.
        print(f"Manage Library select mirror failed (non-fatal): {exc}")
        return {
            "status": "error",
            "reason": str(exc),
            "label": body.label,
            "selected": body.selected,
        }


@router.post("/manage-library/clip-meta")
async def get_manage_library_clip_meta(
    body: ManageLibraryClipMetaSchema,
    auth: AuthContext = Depends(require_auth),
):
    """Read Hudl hover tooltip fields (Created by, Shared with, Date, …) for one clip."""
    owner = _download_job_owner(auth)
    download_active = any(
        job.get("owner") == owner and job.get("status") in ("queued", "processing")
        for job in MANAGE_LIBRARY_DOWNLOAD_JOBS.values()
    )
    if download_active:
        return {
            "status": "skipped",
            "reason": "download_in_progress",
            "label": body.label,
            "meta": {"name": body.label},
        }

    try:
        await _ensure_hudl_browser_ready()
        return await run_task(
            "get_manage_library_clip_meta",
            team_id=body.team_id,
            season_label=body.season,
            folder_path=body.path,
            label=body.label,
        )
    except Exception as exc:
        print(f"Manage Library clip meta failed (non-fatal): {exc}")
        return {
            "status": "error",
            "reason": str(exc),
            "label": body.label,
            "meta": {"name": body.label},
        }


@router.get("/manage-library/download/{job_id}")
async def get_manage_library_download_job(
    job_id: str,
    wait: float = 0,
    auth: AuthContext = Depends(require_auth),
):
    """Return the current state of a queued Manage Library download.

    Supports long-polling: pass ?wait=<seconds> and the request will block until
    the job reaches a terminal state (completed/failed) or the wait window
    elapses. This lets the frontend keep one slow request open instead of
    hammering this endpoint every couple of seconds.
    """
    import asyncio

    _prune_download_jobs()
    owner = _download_job_owner(auth)
    job = MANAGE_LIBRARY_DOWNLOAD_JOBS.get(job_id)
    if not job or job["owner"] != owner:
        raise HTTPException(404, "Download job not found")

    if wait and wait > 0:
        # Cap the hold time so we never exceed typical proxy/client timeouts.
        deadline = time.monotonic() + min(wait, 25.0)
        while job.get("status") in ("queued", "processing"):
            if time.monotonic() >= deadline:
                break
            await asyncio.sleep(0.5)
            job = MANAGE_LIBRARY_DOWNLOAD_JOBS.get(job_id)
            if not job or job["owner"] != owner:
                raise HTTPException(404, "Download job not found")

    return {key: value for key, value in job.items() if key != "owner"}


@router.post("/library/video-stream")
async def get_video_stream_url(body: VideoStreamSchema):
    """Fetch the intercepted HLS stream URL for a given watch URL."""
    try:
        return await run_task("get_video_stream_url", watch_url=body.watch_url)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/close-video")
async def close_video_player():
    """Trigger clicking the close/back button on Hudl's watch screen."""
    try:
        return await run_task("close_video_player")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/details/open")
async def open_video_details(body: LibraryActionBaseSchema):
    """Open details panel for a video and return its information."""
    try:
        return await run_task("open_library_details", video_index=body.video_index)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/details/type")
async def update_video_type(body: LibraryUpdateTypeSchema):
    """Update video type (Game, Scout, Practice) inside the open details panel."""
    try:
        return await run_task("update_library_type", video_index=body.video_index, video_type=body.video_type)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/details/event/options")
async def get_video_event_options(body: LibraryActionBaseSchema):
    """Retrieve event dropdown options in details panel."""
    try:
        return await run_task("get_library_event_options", video_index=body.video_index)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/details/event")
async def update_video_event(body: LibraryUpdateEventSchema):
    """Select a specific event in the details panel dropdown."""
    try:
        return await run_task("update_library_event", video_index=body.video_index, event_name=body.event_name)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/details/close")
async def close_video_details():
    """Close details panel."""
    try:
        return await run_task("close_library_details")
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/rename")
async def rename_video(body: LibraryRenameSchema):
    """Rename video tile (either save new_name or cancel)."""
    try:
        return await run_task("rename_library_video", video_index=body.video_index, new_name=body.new_name, cancel=body.cancel)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/email-download")
async def email_download(body: LibraryEmailDownloadSchema):
    """Trigger Email Download Link popup with stats option or cancel."""
    try:
        return await run_task("email_download_link", video_index=body.video_index, option=body.option, cancel=body.cancel)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/download")
async def download_video(body: LibraryActionBaseSchema):
    """Download video from Hudl and save under media/{id}/ (server). Local browser download kept commented."""
    # from fastapi.responses import FileResponse
    try:
        res = await run_task("download_library_video", video_index=body.video_index)
        if res.get("status") == "ok" and "local_path" in res:
            # Save to media folder with id + path — do not push a browser Downloads file.
            return {
                "status": "ok",
                "message": "Video saved to media folder.",
                "media_id": res.get("media_id"),
                "path": res.get("path"),
                "local_path": res.get("local_path"),
                "filename": res.get("filename"),
                "file_size": res.get("file_size"),
            }

            # --- Previous local browser download (kept for reference) ---
            # return FileResponse(
            #     path=res["local_path"],
            #     filename=res["filename"],
            #     media_type="application/octet-stream"
            # )
        else:
            raise HTTPException(500, res.get("message", "Download failed"))
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/download-by-ref")
async def download_video_by_ref(body: LibraryDownloadByRefSchema):
    """
    Download a Hudl library video by `video_index` or `v` and save it under media/{id}/
    on the server only. Returns JSON locations — never a browser file download.
    """
    from pathlib import Path
    from urllib.parse import quote

    from app.core.database import SessionLocal
    from app.models.media_model import MediaVideo
    from app.services.media_service import (
        BACKEND_ROOT,
        find_media_by_hudl_v,
        media_file_exists,
        serialize_media,
        source_key_from_hudl_v,
        source_key_from_watch_url,
        store_downloaded_video,
    )

    v_raw = (body.v or "").strip() or None
    v_canon = None
    if v_raw:
        # Import helpers from automation for consistent v normalization
        from app.automation.tasks.library import _canonical_hudl_v, _extract_hudl_v

        v_canon = _extract_hudl_v(v_raw) or _canonical_hudl_v(v_raw)

    def _media_payload(row, *, already_saved: bool, v_value: str | None) -> dict:
        media = serialize_media(row)
        rel = media.get("file_path") or ""
        abs_path = str(BACKEND_ROOT / rel) if rel else None
        return {
            "status": "ok",
            "message": (
                "Video already saved in media folder."
                if already_saved
                else "Video saved to media folder."
            ),
            "already_saved": already_saved,
            "v": v_value,
            "video_index": row.video_index,
            "title": media.get("title"),
            "watch_url": media.get("watch_url"),
            "media_id": media.get("id"),
            "path": rel,
            "file_path": rel,
            "local_path": abs_path,
            "media_folder_path": str(BACKEND_ROOT / "media" / media["id"]) if media.get("id") else None,
            "access_url": media.get("access_url"),
            "filename": media.get("original_filename"),
            "file_size": media.get("file_size"),
        }

    # If already in media/ for this v, return paths immediately (no Playwright / no PC download)
    if v_canon:
        with SessionLocal() as db:
            existing = find_media_by_hudl_v(db, v_canon)
            if existing and media_file_exists(existing):
                return _media_payload(existing, already_saved=True, v_value=v_canon)

    try:
        res = await run_task(
            "download_library_video_by_ref",
            video_index=body.video_index,
            v=v_raw,
        )
    except Exception as exc:
        raise _hudl_http_error(exc) from exc

    if res.get("status") != "ok" or "local_path" not in res:
        raise HTTPException(
            404 if "not found" in str(res.get("message", "")).lower() else 500,
            res.get("message", "Download failed"),
        )

    temp_path = res["local_path"]
    if not Path(temp_path).is_file():
        raise HTTPException(500, "Downloaded file missing on disk")

    v_value = (res.get("v") or v_canon or "").strip() or None
    watch_url = (res.get("watch_url") or "").strip()
    if not watch_url and v_value:
        watch_url = f"https://app.hudl.com/watch/team/analyze?v={quote(v_value, safe='=+/')}"
    if not watch_url:
        # Last resort stable key for index-only downloads
        watch_url = f"hudl://library/index/{body.video_index}"

    source_key = source_key_from_hudl_v(v_value) if v_value else source_key_from_watch_url(watch_url)

    try:
        with SessionLocal() as db:
            # Prefer attaching to an existing row for this v if present
            if v_value:
                existing = find_media_by_hudl_v(db, v_value)
                if existing:
                    source_key = existing.source_key
            row = store_downloaded_video(
                db,
                temp_path=temp_path,
                title=res.get("title") or res.get("filename") or "Video",
                watch_url=watch_url,
                original_filename=res.get("filename") or "video.mp4",
                video_index=res.get("video_index"),
                source_key=source_key,
                media_id=res.get("media_id"),
            )
            return _media_payload(row, already_saved=False, v_value=v_value)
    except Exception as exc:
        # Clean temp download if media store failed
        try:
            Path(temp_path).unlink(missing_ok=True)
        except Exception:
            pass
        raise _hudl_http_error(exc) from exc


@router.post("/library/video/download-to-media")
async def download_video_to_media(body: LibraryDownloadToMediaSchema):
    """
    Download a Hudl library video via Playwright, save under media/{id}/,
    and store id + path in the database for later direct access.
    """
    from pathlib import Path

    from app.core.database import SessionLocal
    from app.models.media_model import MediaVideo
    from app.services.media_service import (
        media_file_exists,
        serialize_media,
        source_key_from_watch_url,
        store_downloaded_video,
    )

    watch_url = (body.watch_url or "").strip()
    if not watch_url:
        raise HTTPException(400, "watch_url is required")

    from app.services.media_service import find_media_by_hudl_v

    with SessionLocal() as db:
        key = source_key_from_watch_url(watch_url)
        existing = db.query(MediaVideo).filter(MediaVideo.source_key == key).first()
        if existing and media_file_exists(existing):
            return {
                "status": "ok",
                "already_saved": True,
                "message": "Video already saved in media folder.",
                **serialize_media(existing),
            }
        # Also match by Hudl v (encoding differences)
        try:
            from app.automation.tasks.library import _extract_hudl_v

            v = _extract_hudl_v(watch_url)
            if v:
                by_v = find_media_by_hudl_v(db, v)
                if by_v and media_file_exists(by_v):
                    return {
                        "status": "ok",
                        "already_saved": True,
                        "message": "Video already saved in media folder.",
                        **serialize_media(by_v),
                    }
        except Exception:
            pass

    try:
        res = await run_task("download_library_video", video_index=body.video_index)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc

    if res.get("status") != "ok" or "local_path" not in res:
        raise HTTPException(500, res.get("message", "Download failed"))

    temp_path = res["local_path"]
    if not Path(temp_path).is_file():
        raise HTTPException(500, "Downloaded file missing on disk")

    try:
        with SessionLocal() as db:
            row = store_downloaded_video(
                db,
                temp_path=temp_path,
                title=body.title,
                watch_url=watch_url,
                original_filename=res.get("filename") or "video.mp4",
                video_index=body.video_index,
            )
            payload = serialize_media(row)
    except Exception as exc:
        try:
            Path(temp_path).unlink(missing_ok=True)
        except Exception:
            pass
        raise _hudl_http_error(exc) from exc

    return {
        "status": "ok",
        "already_saved": False,
        "message": "Video saved to media folder.",
        **payload,
    }


@router.get("/media")
async def list_media_videos():
    """List all videos saved under the media folder."""
    from app.core.database import SessionLocal
    from app.models.media_model import MediaVideo
    from app.services.media_service import serialize_media

    with SessionLocal() as db:
        rows = (
            db.query(MediaVideo)
            .order_by(MediaVideo.created_at.desc())
            .all()
        )
        return {
            "status": "ok",
            "count": len(rows),
            "videos": [serialize_media(r) for r in rows],
        }


@router.get("/media/lookup")
async def lookup_media_by_watch_url(watch_url: str):
    """Find a saved media video by Hudl watch_url or by `v` inside it."""
    from urllib.parse import unquote

    from app.core.database import SessionLocal
    from app.models.media_model import MediaVideo
    from app.services.media_service import (
        find_media_by_hudl_v,
        media_file_exists,
        serialize_media,
        source_key_from_watch_url,
    )

    raw = unquote(unquote(watch_url or "")).strip()  # handle double-encoding from FE
    with SessionLocal() as db:
        # 1) Exact source_key from watch_url (legacy + current)
        for candidate in (watch_url or "", raw):
            if not candidate:
                continue
            key = source_key_from_watch_url(candidate)
            row = db.query(MediaVideo).filter(MediaVideo.source_key == key).first()
            if row and media_file_exists(row):
                return {
                    "status": "ok",
                    "found": True,
                    "video": serialize_media(row),
                }

        # 2) Match by Hudl v id (handles encoding differences)
        try:
            from app.automation.tasks.library import _extract_hudl_v

            v = _extract_hudl_v(raw) or _extract_hudl_v(watch_url or "")
        except Exception:
            v = None
        if v:
            row = find_media_by_hudl_v(db, v)
            if row and media_file_exists(row):
                return {
                    "status": "ok",
                    "found": True,
                    "video": serialize_media(row),
                }

        return {"status": "ok", "found": False, "video": None}


@router.post("/library/video/delete")
async def delete_video(body: LibraryActionBaseSchema):
    """Delete a video item from Hudl Library."""
    try:
        return await run_task("delete_library_video", video_index=body.video_index)
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@stream_router.get("/library/stream-proxy")
async def hudl_stream_proxy(url: str, request: Request, token: str = None):
    """
    Proxy HLS stream requests and inject Hudl session cookies for authorization.
    """
    import asyncio
    import gzip
    import urllib.request
    import urllib.parse
    import re
    from fastapi import Response, HTTPException, status, Request
    
    # 0. Auth Verification (JWT)
    token_val = token
    if not token_val and request:
        auth_header = request.headers.get("Authorization")
        if auth_header and auth_header.startswith("Bearer "):
            token_val = auth_header.split(" ")[1]
            
    if not token_val:
        # Fallback: check all query params manually
        pass
        
    if not token_val:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication token required")
        
    try:
        from app.core.database import SessionLocal

        with SessionLocal() as db:
            resolve_auth_from_token(token_val, db)
    except HTTPException:
        raise
    except Exception as auth_err:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=f"Authentication failed: {auth_err}")
        
    # 1. Fetch persistent session cookies
    cookies = await browser_manager.context.cookies()
    cookie_str = "; ".join([f"{c['name']}={c['value']}" for c in cookies])
    
    # 2. Set up request
    req = urllib.request.Request(url)
    req.add_header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
    req.add_header("Cookie", cookie_str)
    
    try:
        loop = asyncio.get_running_loop()
        def fetch_remote():
            try:
                with urllib.request.urlopen(req) as response:
                    return response.status, dict(response.headers), response.read()
            except urllib.error.HTTPError as he:
                return he.code, dict(he.headers), he.read()
            except Exception as e:
                return 500, {}, str(e).encode()
                
        status_code, headers, body = await loop.run_in_executor(None, fetch_remote)
        
        # Decompress if gzipped
        is_gzipped = headers.get("Content-Encoding") == "gzip" or body.startswith(b"\x1f\x8b")
        if is_gzipped:
            try:
                body = gzip.decompress(body)
            except Exception:
                pass
                
        content_type = headers.get("Content-Type", "application/octet-stream")
        
        # If this is a HLS manifest (.m3u8), rewrite relative paths to route through this proxy
        if "mpegurl" in content_type.lower() or url.split("?")[0].endswith(".m3u8"):
            try:
                manifest_text = body.decode('utf-8')
                
                # Compute base URL of manifest
                parsed_url = urllib.parse.urlparse(url)
                base_path = os.path.dirname(parsed_url.path)
                if not base_path.endswith("/"):
                    base_path += "/"
                base_url = f"{parsed_url.scheme}://{parsed_url.netloc}{base_path}"
                
                # Rewrite loop
                lines = manifest_text.splitlines()
                new_lines = []
                for line in lines:
                    line_strip = line.strip()
                    if not line_strip:
                        new_lines.append(line)
                    elif line_strip.startswith("#"):
                        # Rewrite keys / other URI tags
                        if 'URI="' in line:
                            def replace_uri(match):
                                tag_uri = match.group(1)
                                abs_uri = urllib.parse.urljoin(base_url, tag_uri)
                                proxied_uri = f"/api/v1/hudl/library/stream-proxy?url={urllib.parse.quote(abs_uri)}&token={token_val}"
                                return f'URI="{proxied_uri}"'
                            line = re.sub(r'URI="([^"]+)"', replace_uri, line)
                        new_lines.append(line)
                    else:
                        # Convert to absolute path and wrap in proxy
                        abs_target = urllib.parse.urljoin(base_url, line_strip)
                        proxied_target = f"/api/v1/hudl/library/stream-proxy?url={urllib.parse.quote(abs_target)}&token={token_val}"
                        new_lines.append(proxied_target)
                        
                body = "\n".join(new_lines).encode('utf-8')
                content_type = "application/vnd.apple.mpegurl"
            except Exception as rewrite_err:
                print(f"Error rewriting manifest: {rewrite_err}")
                
        # Clean up response headers
        response_headers = {
            "Access-Control-Allow-Origin": "*",
        }
        for k, v in headers.items():
            k_low = k.lower()
            if k_low not in ("content-encoding", "content-length", "transfer-encoding", "server", "connection"):
                response_headers[k] = v
                
        return Response(content=body, status_code=status_code, media_type=content_type, headers=response_headers)
        
    except Exception as exc:
        raise HTTPException(500, f"Streaming proxy failed: {exc}")


@stream_router.get("/library/thumb-proxy")
async def hudl_thumb_proxy(url: str, request: Request, token: str = None):
    """
    Proxy Hudl library thumbnail images with the Playwright session cookies
    so the frontend can show original film stills (not profile avatars).
    """
    import asyncio
    import urllib.request
    import urllib.parse
    from fastapi import Response, HTTPException, status

    token_val = token
    if not token_val and request:
        auth_header = request.headers.get("Authorization")
        if auth_header and auth_header.startswith("Bearer "):
            token_val = auth_header.split(" ")[1]
    if not token_val:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication token required")

    try:
        from app.core.database import SessionLocal

        with SessionLocal() as db:
            resolve_auth_from_token(token_val, db)
    except HTTPException:
        raise
    except Exception as auth_err:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=f"Authentication failed: {auth_err}",
        )

    raw = (url or "").strip()
    if not raw.startswith("http"):
        raise HTTPException(400, "Invalid thumbnail url")
    # Block obvious non-image / profile paths
    if re.search(r"avatar|profile|headshot|/users?/", raw, re.I):
        raise HTTPException(400, "Not a video thumbnail")

    cookies = await browser_manager.context.cookies()
    cookie_str = "; ".join([f"{c['name']}={c['value']}" for c in cookies])
    req = urllib.request.Request(raw)
    req.add_header(
        "User-Agent",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    )
    req.add_header("Cookie", cookie_str)
    req.add_header("Referer", "https://www.hudl.com/")

    try:
        loop = asyncio.get_running_loop()

        def fetch_remote():
            try:
                with urllib.request.urlopen(req, timeout=20) as response:
                    return response.status, dict(response.headers), response.read()
            except urllib.error.HTTPError as he:
                return he.code, dict(he.headers), he.read()
            except Exception as e:
                return 500, {}, str(e).encode()

        status_code, headers, body = await loop.run_in_executor(None, fetch_remote)
        content_type = headers.get("Content-Type", "image/jpeg")
        if status_code >= 400 or not body:
            raise HTTPException(502, f"Thumbnail fetch failed ({status_code})")
        return Response(
            content=body,
            status_code=200,
            media_type=content_type.split(";")[0].strip() or "image/jpeg",
            headers={
                "Cache-Control": "private, max-age=3600",
                "Access-Control-Allow-Origin": "*",
            },
        )
    except HTTPException:
        raise
    except Exception as exc:
        raise HTTPException(500, f"Thumbnail proxy failed: {exc}")


@stream_router.get("/library/video/download-direct")
async def download_video_direct(video_index: int, token: str = None, background_tasks: BackgroundTasks = None):
    """
    Download video from Hudl and save under media/{id}/ on the server.
    Local browser FileResponse download is kept commented below.
    """
    # from fastapi.responses import FileResponse
    # import os
    # import asyncio

    # 0. Auth Verification (JWT)
    token_val = token
    if not token_val:
        raise HTTPException(status_code=401, detail="Authentication token required")

    try:
        from app.core.database import SessionLocal

        with SessionLocal() as db:
            resolve_auth_from_token(token_val, db)
    except HTTPException:
        raise
    except Exception as auth_err:
        raise HTTPException(status_code=401, detail=f"Authentication failed: {auth_err}")

    try:
        res = await run_task("download_library_video", video_index=video_index)
        if res.get("status") == "ok" and "local_path" in res:
            # Persist under media/{id}/ — do not stream to the browser Downloads folder.
            return {
                "status": "ok",
                "message": "Video saved to media folder.",
                "media_id": res.get("media_id"),
                "path": res.get("path"),
                "local_path": res.get("local_path"),
                "filename": res.get("filename"),
                "file_size": res.get("file_size"),
            }

            # --- Previous local browser download (kept for reference) ---
            # file_path = res["local_path"]
            #
            # def remove_file(path: str):
            #     try:
            #         if os.path.exists(path):
            #             os.remove(path)
            #     except Exception as e:
            #         print(f"Error removing temp download file: {e}")
            #
            # if background_tasks:
            #     async def delayed_remove(path: str):
            #         await asyncio.sleep(60)
            #         remove_file(path)
            #     background_tasks.add_task(delayed_remove, file_path)
            #
            # return FileResponse(
            #     path=file_path,
            #     filename=res["filename"],
            #     media_type="application/octet-stream"
            # )
        else:
            raise HTTPException(500, res.get("message", "Download failed"))
    except Exception as exc:
        raise _hudl_http_error(exc) from exc


@stream_router.get("/media/{media_id}")
async def serve_media_video(
    media_id: str,
    request: Request,
    token: str = None,
    download: bool = False,
):
    """
    Direct access to a video saved under media/{id}/.
    - Default: inline playback for <video src>
    - ?download=1: browser file download (attachment)
    """
    from fastapi.responses import FileResponse
    from urllib.parse import quote

    from app.core.database import SessionLocal
    from app.models.media_model import MediaVideo
    from app.services.media_service import absolute_media_path, media_file_exists

    token_val = token
    if not token_val and request:
        auth_header = request.headers.get("Authorization")
        if auth_header and auth_header.startswith("Bearer "):
            token_val = auth_header.split(" ")[1]
    if not token_val:
        raise HTTPException(status_code=401, detail="Authentication token required")

    try:
        with SessionLocal() as db:
            resolve_auth_from_token(token_val, db)
            row = db.query(MediaVideo).filter(MediaVideo.id == media_id).first()
            if not row:
                raise HTTPException(404, "Media video not found")
            if not media_file_exists(row):
                raise HTTPException(404, "Media file missing on disk")
            abs_path = absolute_media_path(row.file_path)
            filename = row.original_filename or abs_path.name
            path_str = str(abs_path)
            name = filename
    except HTTPException:
        raise
    except Exception as auth_err:
        raise HTTPException(status_code=401, detail=f"Authentication failed: {auth_err}")

    headers = {"Access-Control-Allow-Origin": "*"}
    # FileResponse(filename=...) forces attachment; for playback keep inline.
    if download:
        safe_name = name.replace('"', "")
        headers["Content-Disposition"] = (
            f'attachment; filename="{safe_name}"; '
            f"filename*=UTF-8''{quote(safe_name)}"
        )
        return FileResponse(
            path=path_str,
            filename=name,
            media_type="application/octet-stream",
            headers=headers,
        )

    headers["Content-Disposition"] = f'inline; filename="{name.replace(chr(34), "")}"'
    return FileResponse(
        path=path_str,
        media_type="video/mp4",
        headers=headers,
    )



