"""Asset Libraries API — CRUD + .blend upload + catalog refresh."""
import uuid
import shutil
from pathlib import Path

from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel

from app.database import get_db
from app.config import settings
from app.domains.materials.models import AssetLibrary
from app.utils.auth import require_admin_or_pm

router = APIRouter(prefix="/asset-libraries", tags=["asset-libraries"])

ASSET_LIB_DIR = "asset-libraries"


def _asset_lib_dir() -> Path:
    d = Path(settings.upload_dir) / ASSET_LIB_DIR
    d.mkdir(parents=True, exist_ok=True)
    return d


# ── Schemas ──────────────────────────────────────────────────────────────────

class AssetLibraryOut(BaseModel):
    id: str
    name: str
    description: str | None
    original_filename: str | None
    catalog: dict
    is_active: bool
    created_at: str
    updated_at: str


class AssetLibraryUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    is_active: bool | None = None


def _to_out(lib: AssetLibrary) -> dict:
    return {
        "id": str(lib.id),
        "name": lib.name,
        "description": lib.description,
        "original_filename": lib.original_filename,
        "catalog": lib.catalog or {"materials": [], "node_groups": []},
        "is_active": lib.is_active,
        "created_at": lib.created_at.isoformat(),
        "updated_at": lib.updated_at.isoformat(),
    }


# ── Endpoints ─────────────────────────────────────────────────────────────────

@router.get("", response_model=list[AssetLibraryOut])
async def list_asset_libraries(
    db: AsyncSession = Depends(get_db),
    _user=Depends(require_admin_or_pm),
):
    result = await db.execute(select(AssetLibrary).order_by(AssetLibrary.name))
    return [_to_out(lib) for lib in result.scalars().all()]


@router.post("", response_model=AssetLibraryOut, status_code=status.HTTP_201_CREATED)
async def create_asset_library(
    name: str = Form(...),
    description: str | None = Form(None),
    blend_file: UploadFile = File(...),
    db: AsyncSession = Depends(get_db),
    _user=Depends(require_admin_or_pm),
):
    lib = AssetLibrary(
        name=name,
        description=description,
        original_filename=blend_file.filename,
        catalog={"materials": [], "node_groups": []},
    )
    db.add(lib)
    await db.flush()  # get the id

    # Save .blend file
    dest = _asset_lib_dir() / f"{lib.id}.blend"
    with dest.open("wb") as f:
        shutil.copyfileobj(blend_file.file, f)
    lib.blend_file_path = str(dest)

    await db.commit()
    await db.refresh(lib)

    # Queue catalog refresh
    try:
        from app.domains.materials.tasks import refresh_asset_library_catalog
        refresh_asset_library_catalog.delay(str(lib.id))
    except Exception:
        pass  # task queuing failure is non-blocking

    return _to_out(lib)


@router.get("/{lib_id}", response_model=AssetLibraryOut)
async def get_asset_library(
    lib_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    _user=Depends(require_admin_or_pm),
):
    lib = await db.get(AssetLibrary, lib_id)
    if not lib:
        raise HTTPException(status_code=404, detail="Asset library not found")
    return _to_out(lib)


@router.patch("/{lib_id}", response_model=AssetLibraryOut)
async def update_asset_library(
    lib_id: uuid.UUID,
    body: AssetLibraryUpdate,
    db: AsyncSession = Depends(get_db),
    _user=Depends(require_admin_or_pm),
):
    lib = await db.get(AssetLibrary, lib_id)
    if not lib:
        raise HTTPException(status_code=404, detail="Asset library not found")
    for field, value in body.model_dump(exclude_none=True).items():
        setattr(lib, field, value)
    await db.commit()
    await db.refresh(lib)
    return _to_out(lib)


@router.post("/{lib_id}/upload-blend", response_model=AssetLibraryOut)
async def upload_blend(
    lib_id: uuid.UUID,
    blend_file: UploadFile = File(...),
    db: AsyncSession = Depends(get_db),
    _user=Depends(require_admin_or_pm),
):
    lib = await db.get(AssetLibrary, lib_id)
    if not lib:
        raise HTTPException(status_code=404, detail="Asset library not found")

    dest = _asset_lib_dir() / f"{lib.id}.blend"
    with dest.open("wb") as f:
        shutil.copyfileobj(blend_file.file, f)
    lib.blend_file_path = str(dest)
    lib.original_filename = blend_file.filename

    await db.commit()
    await db.refresh(lib)

    try:
        from app.domains.materials.tasks import refresh_asset_library_catalog
        refresh_asset_library_catalog.delay(str(lib.id))
    except Exception:
        pass

    return _to_out(lib)


@router.post("/{lib_id}/refresh-catalog", response_model=AssetLibraryOut)
async def refresh_catalog(
    lib_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    _user=Depends(require_admin_or_pm),
):
    lib = await db.get(AssetLibrary, lib_id)
    if not lib:
        raise HTTPException(status_code=404, detail="Asset library not found")
    if not lib.blend_file_path:
        raise HTTPException(status_code=400, detail="No .blend file uploaded yet")

    from app.domains.materials.tasks import refresh_asset_library_catalog
    refresh_asset_library_catalog.delay(str(lib.id))
    return _to_out(lib)


@router.delete("/{lib_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_asset_library(
    lib_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    _user=Depends(require_admin_or_pm),
):
    lib = await db.get(AssetLibrary, lib_id)
    if not lib:
        raise HTTPException(status_code=404, detail="Asset library not found")

    # Remove .blend file from disk
    if lib.blend_file_path:
        p = Path(lib.blend_file_path)
        if p.exists():
            p.unlink()

    await db.delete(lib)
    await db.commit()
