"""Admin router for storage management — list, download, delete presentations."""
import os
import uuid
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import FileResponse
from pydantic import BaseModel
from sqlalchemy import func, select, and_, delete
from sqlalchemy.ext.asyncio import AsyncSession

from models.sql.client import ClientModel
from models.sql.master_deck import MasterDeckModel
from models.sql.presentation import PresentationModel
from models.sql.slide import SlideModel
from models.sql.user import UserModel
from services import audit_service
from services.access_service import get_accessible_client_ids
from services.database import get_async_session
from utils.auth_dependencies import require_client_admin
from utils.datetime_utils import get_current_utc_datetime

STORAGE_ROUTER = APIRouter(tags=["Admin - Storage"])


async def _resolve_client_filter(client_id, user, session):
    """Return SQLAlchemy filter or None (super_admin sees all)."""
    if client_id:
        return PresentationModel.client_id == client_id
    if user.role == "super_admin":
        return None
    cids = await get_accessible_client_ids(user, session)
    if not cids:
        return PresentationModel.client_id == None  # noqa: E711
    if len(cids) == 1:
        return PresentationModel.client_id == cids[0]
    return PresentationModel.client_id.in_(cids)


@STORAGE_ROUTER.get("/storage/summary")
async def storage_summary(
    client_id: Optional[uuid.UUID] = Query(None),
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Storage summary: total presentations, files, and disk usage."""
    cf = await _resolve_client_filter(client_id, admin, session)

    filters = [PresentationModel.deleted_at.is_(None)]
    if cf is not None:
        filters.append(cf)

    # Count presentations
    count_q = select(func.count()).where(and_(*filters))
    total_presentations = (await session.execute(count_q)).scalar() or 0

    # Get all file_paths to compute size
    path_filters = [
        PresentationModel.deleted_at.is_(None),
        PresentationModel.file_paths.isnot(None),
    ]
    if cf is not None:
        path_filters.append(cf)
    paths_q = select(PresentationModel.file_paths).where(and_(*path_filters))
    result = await session.execute(paths_q)
    all_paths = result.scalars().all()

    total_files = 0
    total_size_bytes = 0
    for file_paths in all_paths:
        if not file_paths:
            continue
        for path in file_paths:
            if path and os.path.isfile(path):
                total_files += 1
                total_size_bytes += os.path.getsize(path)

    # Count soft-deleted presentations
    deleted_filters = [PresentationModel.deleted_at.isnot(None)]
    if cf is not None:
        deleted_filters.append(cf)
    deleted_count_q = select(func.count()).select_from(PresentationModel).where(and_(*deleted_filters))
    total_deleted = (await session.execute(deleted_count_q)).scalar() or 0

    # Scan master decks
    deck_filters = []
    if cf is not None:
        # Reuse client filter but on MasterDeckModel
        if client_id:
            deck_filters.append(MasterDeckModel.client_id == client_id)
        elif admin.role != "super_admin":
            cids = await get_accessible_client_ids(admin, session)
            if cids:
                deck_filters.append(MasterDeckModel.client_id.in_(cids))

    deck_count_q = select(func.count()).select_from(MasterDeckModel)
    if deck_filters:
        deck_count_q = deck_count_q.where(and_(*deck_filters))
    total_master_decks = (await session.execute(deck_count_q)).scalar() or 0

    deck_q = select(MasterDeckModel)
    if deck_filters:
        deck_q = deck_q.where(and_(*deck_filters))
    deck_result = await session.execute(deck_q)
    decks = deck_result.scalars().all()

    master_deck_files = 0
    master_deck_size = 0
    for deck in decks:
        if deck.original_file_path and os.path.isfile(deck.original_file_path):
            master_deck_files += 1
            master_deck_size += os.path.getsize(deck.original_file_path)
        if deck.thumbnail_path and os.path.isfile(deck.thumbnail_path):
            master_deck_files += 1
            master_deck_size += os.path.getsize(deck.thumbnail_path)
        if deck.layouts:
            for layout in deck.layouts:
                sp = layout.get("screenshot_path")
                if sp and os.path.isfile(sp):
                    master_deck_files += 1
                    master_deck_size += os.path.getsize(sp)

    return {
        "total_presentations": total_presentations,
        "total_files": total_files,
        "total_size_bytes": total_size_bytes,
        "total_deleted": total_deleted,
        "total_master_decks": total_master_decks,
        "master_deck_files": master_deck_files,
        "master_deck_size_bytes": master_deck_size,
    }


@STORAGE_ROUTER.get("/storage/presentations")
async def list_storage_presentations(
    client_id: Optional[uuid.UUID] = Query(None),
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """List presentations with file metadata for storage management."""
    cf = await _resolve_client_filter(client_id, admin, session)

    filters = [PresentationModel.deleted_at.is_(None)]
    if cf is not None:
        filters.append(cf)

    stmt = (
        select(PresentationModel)
        .where(and_(*filters))
        .order_by(PresentationModel.created_at.desc())
    )
    result = await session.execute(stmt)
    presentations = result.scalars().all()

    items = []
    for p in presentations:
        file_count = 0
        total_size = 0
        if p.file_paths:
            for path in p.file_paths:
                if path and os.path.isfile(path):
                    file_count += 1
                    total_size += os.path.getsize(path)

        items.append({
            "id": str(p.id),
            "title": p.title,
            "status": p.status,
            "created_at": p.created_at.isoformat() if p.created_at else None,
            "file_count": file_count,
            "total_size_bytes": total_size,
            "has_export": bool(p.file_paths and any(
                fp.endswith(".pptx") for fp in p.file_paths if fp
            )),
        })

    return items


@STORAGE_ROUTER.get("/storage/presentations/{presentation_id}/download")
async def download_presentation(
    presentation_id: uuid.UUID,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Download the PPTX export file for a presentation."""
    presentation = await session.get(PresentationModel, presentation_id)
    if not presentation:
        raise HTTPException(status_code=404, detail="Presentation not found")

    # Verify access
    if presentation.client_id:
        cids = await get_accessible_client_ids(admin, session)
        if presentation.client_id not in cids:
            raise HTTPException(status_code=403, detail="Access denied")

    if not presentation.file_paths:
        raise HTTPException(status_code=404, detail="No export files available")

    # Find the PPTX file
    pptx_path = next(
        (p for p in presentation.file_paths if p and p.endswith(".pptx") and os.path.isfile(p)),
        None,
    )
    if not pptx_path:
        raise HTTPException(status_code=404, detail="PPTX file not found on disk")

    filename = f"{presentation.title or 'presentation'}.pptx"
    return FileResponse(
        pptx_path,
        filename=filename,
        media_type="application/vnd.openxmlformats-officedocument.presentationml.presentation",
    )


@STORAGE_ROUTER.delete("/storage/presentations/{presentation_id}")
async def delete_presentation_storage(
    presentation_id: uuid.UUID,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Soft-delete a presentation (files cleaned up by retention service)."""
    presentation = await session.get(PresentationModel, presentation_id)
    if not presentation:
        raise HTTPException(status_code=404, detail="Presentation not found")

    # Verify access
    if presentation.client_id:
        cids = await get_accessible_client_ids(admin, session)
        if presentation.client_id not in cids:
            raise HTTPException(status_code=403, detail="Access denied")

    presentation.deleted_at = get_current_utc_datetime()
    await session.commit()

    audit_service.log(
        user_id=admin.id,
        action="admin_delete",
        resource_type="presentation",
        resource_id=presentation.id,
        client_id=presentation.client_id,
    )

    return {"ok": True}


class BulkDeleteRequest(BaseModel):
    ids: List[uuid.UUID]


@STORAGE_ROUTER.get("/storage/breakdown")
async def storage_breakdown(
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Per-client storage breakdown."""
    # Only super_admin can see breakdown of all clients
    if admin.role != "super_admin":
        raise HTTPException(status_code=403, detail="Super admin only")

    # Get all clients
    clients_result = await session.execute(select(ClientModel))
    clients = clients_result.scalars().all()

    breakdown = []
    for client in clients:
        cf = PresentationModel.client_id == client.id

        # Count presentations
        count_q = select(func.count()).select_from(PresentationModel).where(
            and_(PresentationModel.deleted_at.is_(None), cf)
        )
        pres_count = (await session.execute(count_q)).scalar() or 0

        # Presentation file sizes
        paths_q = select(PresentationModel.file_paths).where(
            and_(PresentationModel.deleted_at.is_(None), PresentationModel.file_paths.isnot(None), cf)
        )
        result = await session.execute(paths_q)
        all_paths = result.scalars().all()
        pres_files = 0
        pres_size = 0
        for file_paths in all_paths:
            if not file_paths:
                continue
            for path in file_paths:
                if path and os.path.isfile(path):
                    pres_files += 1
                    pres_size += os.path.getsize(path)

        # Master deck count & size
        deck_q = select(MasterDeckModel).where(MasterDeckModel.client_id == client.id)
        deck_result = await session.execute(deck_q)
        decks = deck_result.scalars().all()
        deck_files = 0
        deck_size = 0
        for deck in decks:
            if deck.original_file_path and os.path.isfile(deck.original_file_path):
                deck_files += 1
                deck_size += os.path.getsize(deck.original_file_path)
            if deck.thumbnail_path and os.path.isfile(deck.thumbnail_path):
                deck_files += 1
                deck_size += os.path.getsize(deck.thumbnail_path)
            if deck.layouts:
                for layout in deck.layouts:
                    sp = layout.get("screenshot_path")
                    if sp and os.path.isfile(sp):
                        deck_files += 1
                        deck_size += os.path.getsize(sp)

        breakdown.append({
            "client_id": str(client.id),
            "client_name": client.name,
            "presentations": pres_count,
            "presentation_files": pres_files,
            "presentation_size_bytes": pres_size,
            "master_decks": len(decks),
            "master_deck_files": deck_files,
            "master_deck_size_bytes": deck_size,
            "total_size_bytes": pres_size + deck_size,
        })

    breakdown.sort(key=lambda x: x["total_size_bytes"], reverse=True)
    return breakdown


@STORAGE_ROUTER.post("/storage/presentations/bulk-delete")
async def bulk_delete_presentations(
    body: BulkDeleteRequest,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Soft-delete multiple presentations at once."""
    deleted_count = 0
    for pid in body.ids:
        presentation = await session.get(PresentationModel, pid)
        if not presentation or presentation.deleted_at:
            continue
        # Access check
        if presentation.client_id:
            cids = await get_accessible_client_ids(admin, session)
            if presentation.client_id not in cids:
                continue
        presentation.deleted_at = get_current_utc_datetime()
        deleted_count += 1

    await session.commit()

    audit_service.log(
        user_id=admin.id,
        action="admin_bulk_delete",
        resource_type="presentation",
        details={"count": deleted_count, "ids": [str(i) for i in body.ids]},
    )

    return {"ok": True, "deleted_count": deleted_count}


@STORAGE_ROUTER.post("/storage/purge")
async def purge_deleted_storage(
    client_id: Optional[uuid.UUID] = Query(None),
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Hard-delete files for soft-deleted presentations (exports + generated images)."""
    if admin.role != "super_admin":
        raise HTTPException(status_code=403, detail="Super admin only")

    filters = [PresentationModel.deleted_at.isnot(None)]
    if client_id:
        filters.append(PresentationModel.client_id == client_id)

    stmt = select(PresentationModel).where(and_(*filters))
    result = await session.execute(stmt)
    deleted_presentations = result.scalars().all()

    purged_files = 0
    purged_bytes = 0
    purged_presentations = 0
    purged_images = 0

    print(f"[PURGE] Found {len(deleted_presentations)} soft-deleted presentations")

    for p in deleted_presentations:
        # Delete export files (PDF/PPTX)
        if p.file_paths:
            for path in p.file_paths:
                if path and os.path.isfile(path):
                    try:
                        size = os.path.getsize(path)
                        os.remove(path)
                        purged_files += 1
                        purged_bytes += size
                    except OSError:
                        pass
            p.file_paths = []

        # Delete generated images from slides
        slides_stmt = select(SlideModel).where(SlideModel.presentation == p.id)
        slides_result = await session.execute(slides_stmt)
        slides = slides_result.scalars().all()
        print(f"[PURGE] Presentation {p.id}: {len(slides)} slides")

        for slide in slides:
            if slide.content and isinstance(slide.content, dict):
                # Extract image path from content.image.__image_url__
                image_data = slide.content.get("image")
                if image_data and isinstance(image_data, dict):
                    image_url = image_data.get("__image_url__")
                    print(f"[PURGE]   Slide has image URL: {image_url}")
                    if image_url and image_url.startswith("/app_data/images/"):
                        # URL is already an absolute path inside container
                        image_path = image_url
                        print(f"[PURGE]   Checking path: {image_path}, exists: {os.path.isfile(image_path)}")
                        if os.path.isfile(image_path):
                            try:
                                size = os.path.getsize(image_path)
                                os.remove(image_path)
                                purged_images += 1
                                purged_bytes += size
                                print(f"[PURGE]   ✓ Deleted: {image_path} ({size} bytes)")
                            except OSError as e:
                                print(f"[PURGE]   ✗ Error deleting {image_path}: {e}")

        purged_presentations += 1

    print(f"[PURGE] TOTAL: {purged_presentations} presentations, {purged_files} files, {purged_images} images, {purged_bytes} bytes")

    # Hard-delete presentation records and their slides from DB
    print(f"[PURGE] Hard-deleting {len(deleted_presentations)} presentation records from DB...")
    for p in deleted_presentations:
        # Slides will be cascade-deleted due to FK ondelete="CASCADE"
        await session.delete(p)

    print(f"[PURGE] ✓ Deleted {len(deleted_presentations)} presentations from database")

    await session.commit()

    audit_service.log(
        user_id=admin.id,
        action="admin_purge",
        resource_type="storage",
        details={
            "presentations": purged_presentations,
            "files": purged_files,
            "images": purged_images,
            "bytes": purged_bytes
        },
    )

    return {
        "ok": True,
        "purged_presentations": purged_presentations,
        "purged_files": purged_files,
        "purged_images": purged_images,
        "purged_bytes": purged_bytes,
    }
