"""Document content streaming endpoint.

Endpoints:
  GET /{doc_id}/content — stream document bytes from any backend (MinIO or cloud)

Sub-router carries NO prefix — prefix="/api/documents" lives in __init__.py (D-04).

Security:
  T-04-05-01: uses get_regular_user — admin role → 403 (critical security invariant).
  T-04-05-02: bytes fetched via get_object() ONLY — presigned_get_url() never called.
  T-04-05-03: Range header validated via _parse_range(); invalid range → 416.
  T-04-05-04: access gated on ownership OR active Share.recipient_id.
  DOC-04 / SEC-04: ownership assertion before serving bytes.
"""
from __future__ import annotations

import urllib.parse
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.responses import StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession

from db.models import User
from api.documents.shared import get_accessible_document
from deps.auth import get_regular_user
from deps.db import get_db
from services.rate_limiting import account_limiter
from storage.exceptions import CloudConnectionError

router = APIRouter()


def _parse_range(range_header: str, file_size: int) -> tuple:
    """Parse a 'bytes=X-Y' Range header and return (start, end).

    Returns (start, end) where both are inclusive byte offsets.
    Raises HTTP 416 on any invalid or out-of-bounds range.

    """
    try:
        h = range_header.replace("bytes=", "").split("-")
        start = int(h[0]) if h[0] != "" else 0
        end = int(h[1]) if h[1] != "" else file_size - 1
    except (ValueError, IndexError):
        raise HTTPException(status.HTTP_416_RANGE_NOT_SATISFIABLE)
    if start > end or start < 0 or end >= file_size:
        raise HTTPException(status.HTTP_416_RANGE_NOT_SATISFIABLE)
    return start, end


@router.get("/{doc_id}/content")
@account_limiter.limit("100/minute")
async def stream_document_content(
    doc_id: str,
    request: Request,
    session: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_regular_user),
):
    request.state.current_user = current_user
    doc, _is_recipient = await get_accessible_document(session, doc_id, current_user.id)

    try:
        import api.documents as _doc_pkg  # late import allows test monkeypatching via api.documents
        storage_backend = await _doc_pkg.get_storage_backend_for_document(doc, current_user, session)
        file_bytes = await storage_backend.get_object(doc.object_key)
    except CloudConnectionError as exc:
        raise HTTPException(
            status_code=503,
            detail="Cloud connection requires re-authentication. Please reconnect in Settings.",
        ) from exc
    except HTTPException:
        raise
    except Exception as exc:
        raise HTTPException(
            status_code=502,
            detail="Cloud backend unreachable. Please try again or reconnect in Settings.",
        ) from exc
    file_size = len(file_bytes)

    safe_name = urllib.parse.quote(doc.filename, safe='')
    headers = {
        "content-type": doc.content_type,
        "content-disposition": f"inline; filename*=UTF-8''{safe_name}",
        "accept-ranges": "bytes",
        "content-length": str(file_size),
    }

    range_header = request.headers.get("range")
    if range_header:
        start, end = _parse_range(range_header, file_size)
        chunk = file_bytes[start : end + 1]
        headers["content-range"] = f"bytes {start}-{end}/{file_size}"
        headers["content-length"] = str(len(chunk))
        return StreamingResponse(
            iter([chunk]),
            status_code=206,
            headers=headers,
        )

    return StreamingResponse(
        iter([file_bytes]),
        status_code=200,
        headers=headers,
    )
