import json
import uuid

from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from fastapi.responses import Response
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.permissions import deny_vendor, require_permission
from app.core.security import AuthContext, get_auth_context
from app.crud.app_bundle import capture_bundle, import_bundle
from app.crud.base import CRUDBase
from app.models.app_bundle import App
from app.schemas.app_bundle import AppCreate, AppImportResult, AppRead, AppUpdate

router = APIRouter(prefix="/apps", tags=["apps"])
crud = CRUDBase[App, AppCreate, AppUpdate](App)


@router.get("", response_model=list[AppRead])
async def list_apps(
    skip: int = 0,
    limit: int = 100,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
    _: AuthContext = Depends(require_permission("apps", "view")),
) -> list[App]:
    return await crud.list(db, ctx.tenant_id, skip=skip, limit=limit)


@router.get("/{app_id}", response_model=AppRead)
async def get_app(
    app_id: uuid.UUID,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
    _: AuthContext = Depends(require_permission("apps", "view")),
) -> App:
    return await crud.get(db, ctx.tenant_id, app_id)


@router.post("", response_model=AppRead, status_code=status.HTTP_201_CREATED)
async def create_app(
    payload: AppCreate,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
    _: AuthContext = Depends(require_permission("apps", "create")),
    __: AuthContext = Depends(deny_vendor()),
) -> App:
    return await crud.create(db, ctx.tenant_id, payload, created_by=ctx.user_id)


@router.patch("/{app_id}", response_model=AppRead)
async def update_app(
    app_id: uuid.UUID,
    payload: AppUpdate,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
    _: AuthContext = Depends(require_permission("apps", "edit")),
    __: AuthContext = Depends(deny_vendor()),
) -> App:
    return await crud.update(db, ctx.tenant_id, app_id, payload)


@router.delete("/{app_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_app(
    app_id: uuid.UUID,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
    _: AuthContext = Depends(require_permission("apps", "delete")),
    __: AuthContext = Depends(deny_vendor()),
) -> None:
    await crud.delete(db, ctx.tenant_id, app_id)


@router.get("/{app_id}/export")
async def export_app(
    app_id: uuid.UUID,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
    _: AuthContext = Depends(require_permission("apps", "view")),
    __: AuthContext = Depends(deny_vendor()),
) -> Response:
    app = await crud.get(db, ctx.tenant_id, app_id)
    bundle = await capture_bundle(db, ctx.tenant_id, app.name, app.description, app.items)
    body = json.dumps(bundle, indent=2)
    filename = "".join(c if c.isalnum() or c in "-_" else "-" for c in app.name.lower()) or "app"
    return Response(
        content=body,
        media_type="application/json",
        headers={"Content-Disposition": f'attachment; filename="{filename}.json"'},
    )


@router.post("/import", response_model=AppImportResult, status_code=status.HTTP_201_CREATED)
async def import_app(
    file: UploadFile = File(...),
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
    _: AuthContext = Depends(require_permission("apps", "create")),
    __: AuthContext = Depends(deny_vendor()),
) -> AppImportResult:
    raw = await file.read()
    try:
        bundle = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise HTTPException(status_code=422, detail=f"Not a valid App export file: {exc}") from exc
    if "items" not in bundle:
        raise HTTPException(status_code=422, detail="Not a valid App export file — missing 'items'.")
    return await import_bundle(db, ctx.tenant_id, bundle, ctx.user_id)
