"""MediaAsset router — /api/media."""
import io
import uuid
import zipfile

from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import get_db
from app.domains.auth.models import User
from app.domains.media.models import MediaAsset, MediaAssetType
from app.domains.media.schemas import MediaAssetOut
from app.domains.media import service
from app.utils.auth import get_current_user

router = APIRouter(prefix="/api/media", tags=["media"], redirect_slashes=False)


async def _resolve_thumbnails_bulk(db: AsyncSession, assets: list) -> None:
    """Resolve thumbnail_url for assets using the same priority as product pages.

    Priority per asset (applied only when thumbnail_url is not yet set):
      1. Latest 'still' MediaAsset for the same product (rendered preview)
      2. Product's linked CadFile thumbnail (/api/cad/{id}/thumbnail)
    """
    needs = [a for a in assets if not a.thumbnail_url and a.product_id]
    if not needs:
        return

    product_ids = list({a.product_id for a in needs})

    # 1. Latest 'still' asset per product (DISTINCT ON product_id ORDER BY created_at DESC)
    still_rows = await db.execute(
        select(MediaAsset.product_id, MediaAsset.id)
        .where(
            MediaAsset.product_id.in_(product_ids),
            MediaAsset.asset_type == MediaAssetType.still,
            MediaAsset.is_archived == False,  # noqa: E712
        )
        .order_by(MediaAsset.product_id, MediaAsset.created_at.desc())
        .distinct(MediaAsset.product_id)
    )
    best_still: dict[str, str] = {str(pid): str(sid) for pid, sid in still_rows.all()}

    # 2. Fallback: product's cad_file_id → CAD thumbnail endpoint
    from app.domains.products.models import Product
    from sqlalchemy import text
    # products has RLS — bypass for this internal read-only lookup
    await db.execute(text("SET LOCAL app.current_tenant_id = 'bypass'"))
    prod_rows = await db.execute(
        select(Product.id, Product.cad_file_id).where(Product.id.in_(product_ids))
    )
    product_cad: dict[str, str] = {
        str(pid): str(cid) for pid, cid in prod_rows.all() if cid
    }

    for a in needs:
        pid = str(a.product_id)
        if pid in best_still:
            a.thumbnail_url = f"/api/media/{best_still[pid]}/download"
        elif pid in product_cad:
            a.thumbnail_url = f"/api/cad/{product_cad[pid]}/thumbnail"


@router.get("", response_model=list[MediaAssetOut])
@router.get("/", response_model=list[MediaAssetOut], include_in_schema=False)
async def list_assets(
    product_id: uuid.UUID | None = None,
    order_line_id: uuid.UUID | None = None,
    cad_file_id: uuid.UUID | None = None,
    asset_type: MediaAssetType | None = None,
    asset_types: list[MediaAssetType] = Query(default=[]),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=500),
    sort_by: str = Query("created_at"),
    sort_dir: str = Query("desc"),
    _user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    assets = await service.list_media_assets(
        db,
        product_id=product_id,
        order_line_id=order_line_id,
        cad_file_id=cad_file_id,
        asset_type=asset_type,
        asset_types=asset_types if asset_types else None,
        skip=skip,
        limit=limit,
        sort_by=sort_by,
        sort_dir=sort_dir,
    )
    for a in assets:
        a.download_url = service.get_download_url(a)
        a.thumbnail_url = service.get_thumbnail_url(a)
    await _resolve_thumbnails_bulk(db, assets)
    return assets


@router.get("/{asset_id}", response_model=MediaAssetOut)
async def get_asset(asset_id: uuid.UUID, db: AsyncSession = Depends(get_db)):
    asset = await service.get_media_asset(db, asset_id)
    if not asset:
        raise HTTPException(404, "Asset not found")
    asset.download_url = service.get_download_url(asset)
    asset.thumbnail_url = service.get_thumbnail_url(asset)
    await _resolve_thumbnails_bulk(db, [asset])
    return asset


@router.api_route("/{asset_id}/download", methods=["GET", "HEAD"])
async def download_asset(
    asset_id: uuid.UUID,
    _user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Proxy file content directly — avoids internal MinIO hostname issues."""
    from fastapi.responses import FileResponse, Response
    from pathlib import Path
    asset = await service.get_media_asset(db, asset_id)
    if not asset:
        raise HTTPException(404, "Asset not found")

    key = asset.storage_key
    mime = asset.mime_type or "application/octet-stream"

    # Local file path (absolute or relative to UPLOAD_DIR)
    from app.config import settings
    candidate = Path(key)
    if not candidate.is_absolute():
        candidate = Path(settings.upload_dir) / key
    # Legacy path remapping: /shared/renders/{uuid}/{file} → UPLOAD_DIR/renders/{uuid}/{file}
    if not candidate.exists() and "/shared/renders/" in key:
        import logging
        parts = key.split("/")
        if len(parts) >= 2:
            remapped = Path(settings.upload_dir) / "renders" / parts[-2] / parts[-1]
            if remapped.exists():
                logging.getLogger(__name__).warning(
                    "Remapped legacy path %s → %s", key, remapped
                )
                candidate = remapped
    if candidate.exists():
        ext = candidate.suffix.lstrip(".")
        fname = f"{asset.asset_type.value}_{asset_id}.{ext or 'bin'}"
        return FileResponse(
            str(candidate), media_type=mime, filename=fname,
            headers={"Cache-Control": "max-age=3600, public"},
        )

    # Fall back to MinIO
    try:
        from app.core.storage import get_storage
        data = get_storage().download_bytes(key)
        ext = key.rsplit(".", 1)[-1] if "." in key else "bin"
        fname = f"{asset.asset_type.value}_{asset_id}.{ext}"
        return Response(
            content=data,
            media_type=mime,
            headers={
                "Content-Disposition": f"attachment; filename={fname}",
                "Cache-Control": "max-age=3600, public",
            },
        )
    except Exception:
        raise HTTPException(404, "File not available")


@router.post("/zip")
async def zip_download(
    asset_ids: list[uuid.UUID],
    _user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    assets = []
    for aid in asset_ids:
        a = await service.get_media_asset(db, aid)
        if a:
            assets.append(a)
    if not assets:
        raise HTTPException(404, "No assets found")

    def generate():
        import logging
        from pathlib import Path
        from app.core.storage import get_storage
        logger = logging.getLogger(__name__)
        buf = io.BytesIO()
        seen_names: dict[str, int] = {}
        with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
            storage = get_storage()
            for a in assets:
                key = a.storage_key
                # Use filename from storage_key (always has correct extension)
                original_name = Path(key).name
                ext = Path(key).suffix.lstrip(".") or (a.mime_type or "").split("/")[-1] or "bin"
                base = original_name if original_name else f"{a.asset_type.value}_{a.id}.{ext}"
                # Deduplicate filenames within the ZIP
                if base in seen_names:
                    seen_names[base] += 1
                    stem = Path(base).stem
                    suffix = Path(base).suffix
                    fname = f"{stem}_{seen_names[base]}{suffix}"
                else:
                    seen_names[base] = 0
                    fname = base
                try:
                    # Check absolute path first (local filesystem)
                    candidate = Path(key)
                    if not candidate.is_absolute():
                        from app.config import settings
                        candidate = Path(settings.upload_dir) / key
                    if candidate.exists():
                        data = candidate.read_bytes()
                    else:
                        data = storage.download_bytes(key)
                    zf.writestr(fname, data)
                except Exception as exc:
                    logger.warning("ZIP: skipping asset %s — %s", a.id, exc)
        yield buf.getvalue()

    return StreamingResponse(
        generate(),
        media_type="application/zip",
        headers={"Content-Disposition": "attachment; filename=media-export.zip"},
    )


@router.delete("/{asset_id}")
async def archive_asset(asset_id: uuid.UUID, db: AsyncSession = Depends(get_db)):
    asset = await service.archive_media_asset(db, asset_id)
    if not asset:
        raise HTTPException(404, "Asset not found")
    return {"ok": True}


@router.delete("/{asset_id}/permanent")
async def delete_asset_permanent(asset_id: uuid.UUID, db: AsyncSession = Depends(get_db)):
    """Permanently remove a MediaAsset record from the database."""
    deleted = await service.delete_media_asset(db, asset_id)
    if not deleted:
        raise HTTPException(404, "Asset not found")
    return {"ok": True}
