"""Admin router for master deck upload, parsing, and management."""
import os
import shutil
import uuid
from typing import List, Optional

from fastapi import APIRouter, Body, Depends, File, HTTPException, Query, UploadFile
from fastapi.responses import FileResponse
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from constants.documents import POWERPOINT_TYPES
from models.sql.master_deck import MasterDeckModel
from models.sql.user import UserModel
from services.database import get_async_session
from api.middlewares.rbac_middleware import check_team_admin
from utils.auth_dependencies import require_client_admin

MASTER_DECKS_ROUTER = APIRouter(tags=["Admin - Master Decks"])

DATA_DIR = os.environ.get("APP_DATA_DIRECTORY", os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "data"))


def _deck_dir(client_id: uuid.UUID, deck_id: uuid.UUID) -> str:
    return os.path.join(DATA_DIR, "clients", str(client_id), "master_decks", str(deck_id))


# --- Request / Response schemas ---


class MasterDeckUpdate(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None
    is_active: Optional[bool] = None


class LayoutUpdate(BaseModel):
    layout_name: Optional[str] = None
    layout_type: Optional[str] = None
    react_code: Optional[str] = None


class LayoutBulkDelete(BaseModel):
    indices: List[int]


# --- Helpers ---


async def _list_decks(client_id: uuid.UUID, include_inactive: bool, session: AsyncSession):
    stmt = select(MasterDeckModel).where(MasterDeckModel.client_id == client_id)
    if not include_inactive:
        stmt = stmt.where(MasterDeckModel.is_active == True)
    stmt = stmt.order_by(MasterDeckModel.created_at.desc())

    result = await session.execute(stmt)
    decks = result.scalars().all()

    return [
        {
            "id": str(d.id),
            "client_id": str(d.client_id),
            "name": d.name,
            "description": d.description,
            "thumbnail_path": d.thumbnail_path,
            "parse_mode": getattr(d, "parse_mode", None) or "layouts",
            "parse_status": d.parse_status,
            "is_active": d.is_active,
            "layouts": d.layouts,
            "created_at": d.created_at.isoformat() if d.created_at else None,
            "updated_at": d.updated_at.isoformat() if d.updated_at else None,
        }
        for d in decks
    ]


# --- Endpoints ---


@MASTER_DECKS_ROUTER.get("/master-decks")
async def list_master_decks_flat(
    client_id: uuid.UUID = Query(...),
    include_inactive: bool = Query(False),
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    await check_team_admin(admin, client_id, session)
    return await _list_decks(client_id, include_inactive, session)


@MASTER_DECKS_ROUTER.get("/clients/{client_id}/master-decks")
async def list_master_decks(
    client_id: uuid.UUID,
    include_inactive: bool = Query(False),
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    await check_team_admin(admin, client_id, session)
    return await _list_decks(client_id, include_inactive, session)


@MASTER_DECKS_ROUTER.post("/clients/{client_id}/master-decks")
async def upload_master_deck(
    client_id: uuid.UUID,
    file: UploadFile = File(...),
    parse_mode: str = Query("layouts", description="Parse mode: 'layouts' (default, unique slideLayouts) or 'slides' (one layout per slide)"),
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    await check_team_admin(admin, client_id, session)

    is_pptx_mime = file.content_type in POWERPOINT_TYPES
    is_pptx_ext = (file.filename or "").lower().endswith(".pptx")
    if not is_pptx_mime and not is_pptx_ext:
        raise HTTPException(status_code=400, detail="Only PPTX files are accepted")

    if hasattr(file, "size") and file.size and file.size > 100 * 1024 * 1024:
        raise HTTPException(status_code=400, detail="File too large (max 100 MB)")

    if parse_mode not in ("slides", "layouts"):
        parse_mode = "slides"

    deck_id = uuid.uuid4()
    deck_path = _deck_dir(client_id, deck_id)
    os.makedirs(deck_path, exist_ok=True)

    original_name = file.filename or "presentation.pptx"
    file_path = os.path.join(deck_path, original_name)
    content = await file.read()
    with open(file_path, "wb") as f:
        f.write(content)

    deck = MasterDeckModel(
        id=deck_id,
        client_id=client_id,
        name=os.path.splitext(original_name)[0],
        original_file_path=file_path,
        parse_mode=parse_mode,
        parse_status="pending",
        is_active=True,
    )
    session.add(deck)
    await session.commit()
    await session.refresh(deck)

    # Kick off async parsing via ARQ (fallback to asyncio.create_task)
    try:
        from models.sql.job import JobModel
        from services.redis_service import enqueue_job

        job = JobModel(
            user_id=admin.id,
            client_id=client_id,
            presentation_id=None,
            job_type="parse_master_deck",
            status="queued",
            progress=0,
            progress_message="Queued for parsing",
        )
        session.add(job)
        await session.commit()
        await enqueue_job("parse_master_deck_task", job_id=str(job.id), deck_id=str(deck_id))
    except Exception:
        import asyncio
        from services.master_deck_parser_service import parse_master_deck
        asyncio.create_task(parse_master_deck(deck_id))

    return {
        "id": str(deck.id),
        "client_id": str(deck.client_id),
        "name": deck.name,
        "parse_status": deck.parse_status,
        "created_at": deck.created_at.isoformat() if deck.created_at else None,
    }


@MASTER_DECKS_ROUTER.get("/master-decks/{deck_id}")
async def get_master_deck(
    deck_id: uuid.UUID,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    return {
        "id": str(deck.id),
        "client_id": str(deck.client_id),
        "name": deck.name,
        "description": deck.description,
        "original_file_path": deck.original_file_path,
        "thumbnail_path": deck.thumbnail_path,
        "parsed_config": deck.parsed_config,
        "layouts": deck.layouts,
        "parse_status": deck.parse_status,
        "is_active": deck.is_active,
        "created_at": deck.created_at.isoformat() if deck.created_at else None,
        "updated_at": deck.updated_at.isoformat() if deck.updated_at else None,
    }


@MASTER_DECKS_ROUTER.put("/master-decks/{deck_id}")
async def update_master_deck(
    deck_id: uuid.UUID,
    body: MasterDeckUpdate,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    if body.name is not None:
        deck.name = body.name
    if body.description is not None:
        deck.description = body.description
    if body.is_active is not None:
        deck.is_active = body.is_active

    await session.commit()
    await session.refresh(deck)

    return {"ok": True, "id": str(deck.id)}


@MASTER_DECKS_ROUTER.put("/master-decks/{deck_id}/layouts/{layout_index}")
async def update_layout(
    deck_id: uuid.UUID,
    layout_index: int,
    body: LayoutUpdate,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    if not deck.layouts or layout_index < 0 or layout_index >= len(deck.layouts):
        raise HTTPException(status_code=404, detail="Layout not found at index")

    layout = deck.layouts[layout_index]
    if body.layout_name is not None:
        layout["layout_name"] = body.layout_name
    if body.layout_type is not None:
        layout["layout_type"] = body.layout_type
    if body.react_code is not None:
        layout["react_code"] = body.react_code

    # SQLAlchemy needs to detect mutation on JSON column
    from sqlalchemy.orm.attributes import flag_modified

    flag_modified(deck, "layouts")
    await session.commit()

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


@MASTER_DECKS_ROUTER.delete("/master-decks/{deck_id}/layouts/{layout_index}")
async def delete_layout(
    deck_id: uuid.UUID,
    layout_index: int,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Delete a single layout from a master deck by index."""
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    if not deck.layouts or layout_index < 0 or layout_index >= len(deck.layouts):
        raise HTTPException(status_code=404, detail="Layout not found at index")

    deck.layouts.pop(layout_index)
    # Re-index remaining layouts
    for i, layout in enumerate(deck.layouts):
        layout["index"] = i

    from sqlalchemy.orm.attributes import flag_modified
    flag_modified(deck, "layouts")
    await session.commit()

    # Re-register template with remaining layouts
    await _re_register_template(deck, session)

    return {"ok": True, "remaining_layouts": len(deck.layouts)}


@MASTER_DECKS_ROUTER.post("/master-decks/{deck_id}/layouts/bulk-delete")
async def bulk_delete_layouts(
    deck_id: uuid.UUID,
    body: LayoutBulkDelete,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Bulk delete layouts from a master deck by indices."""
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    if not deck.layouts:
        raise HTTPException(status_code=400, detail="No layouts to delete")

    # Validate indices
    valid_indices = sorted(set(i for i in body.indices if 0 <= i < len(deck.layouts)), reverse=True)
    if not valid_indices:
        raise HTTPException(status_code=400, detail="No valid indices provided")

    # Remove from highest index first to preserve lower indices
    for idx in valid_indices:
        deck.layouts.pop(idx)

    # Re-index remaining layouts
    for i, layout in enumerate(deck.layouts):
        layout["index"] = i

    from sqlalchemy.orm.attributes import flag_modified
    flag_modified(deck, "layouts")
    await session.commit()

    # Re-register template with remaining layouts
    await _re_register_template(deck, session)

    return {"ok": True, "deleted": len(valid_indices), "remaining_layouts": len(deck.layouts)}


async def _re_register_template(deck: MasterDeckModel, session: AsyncSession):
    """Re-register a master deck's layouts as a custom template after mutations."""
    try:
        from services.master_deck_parser_service import _register_as_template
        await _register_as_template(deck.id, deck.name, deck.layouts or [], session)
    except Exception as e:
        print(f"[master_decks] Failed to re-register template: {e}")


@MASTER_DECKS_ROUTER.post("/master-decks/{deck_id}/reparse")
async def reparse_master_deck(
    deck_id: uuid.UUID,
    parse_mode: Optional[str] = Query(None, description="Parse mode: 'slides' or 'layouts'. Defaults to deck's current mode."),
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    if deck.parse_status == "processing":
        raise HTTPException(status_code=409, detail="Deck is already being parsed")

    # Update parse mode if provided
    if parse_mode and parse_mode in ("slides", "layouts"):
        deck.parse_mode = parse_mode

    deck.parse_status = "pending"
    await session.commit()

    try:
        from models.sql.job import JobModel
        from services.redis_service import enqueue_job

        job = JobModel(
            user_id=admin.id,
            client_id=deck.client_id,
            presentation_id=None,
            job_type="parse_master_deck",
            status="queued",
            progress=0,
            progress_message="Queued for re-parsing",
        )
        session.add(job)
        await session.commit()
        await enqueue_job("parse_master_deck_task", job_id=str(job.id), deck_id=str(deck_id))
    except Exception as e:
        print(f"[reparse] Failed to enqueue job, falling back to async: {e}")
        import asyncio
        from services.master_deck_parser_service import parse_master_deck
        asyncio.create_task(parse_master_deck(deck_id))

    return {"ok": True, "parse_status": "pending"}


@MASTER_DECKS_ROUTER.delete("/master-decks/{deck_id}")
async def delete_master_deck(
    deck_id: uuid.UUID,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    # Hard delete: remove physical files + all DB records
    deck_dir = _deck_dir(deck.client_id, deck_id)
    if os.path.isdir(deck_dir):
        shutil.rmtree(deck_dir, ignore_errors=True)

    # Delete PresentationLayoutCodeModel rows
    from models.sql.presentation_layout_code import PresentationLayoutCodeModel
    from models.sql.template import TemplateModel
    from sqlalchemy import delete as sql_delete
    await session.execute(
        sql_delete(PresentationLayoutCodeModel).where(
            PresentationLayoutCodeModel.presentation == deck_id
        )
    )
    # Delete TemplateModel (deck_id == template id)
    await session.execute(
        sql_delete(TemplateModel).where(TemplateModel.id == deck_id)
    )
    # Delete MasterDeckModel
    await session.delete(deck)
    await session.commit()
    return {"ok": True}


@MASTER_DECKS_ROUTER.get("/master-decks/{deck_id}/screenshot/{filename}")
async def get_master_deck_screenshot(
    deck_id: uuid.UUID,
    filename: str,
    admin: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Serve a master deck screenshot image with auth check."""
    deck = await session.get(MasterDeckModel, deck_id)
    if not deck:
        raise HTTPException(status_code=404, detail="Master deck not found")

    await check_team_admin(admin, deck.client_id, session)

    # Sanitize filename to prevent path traversal
    safe_name = os.path.basename(filename)
    screenshot_dir = os.path.join(
        DATA_DIR, "clients", str(deck.client_id),
        "master_decks", str(deck_id), "screenshots"
    )
    file_path = os.path.join(screenshot_dir, safe_name)

    if not os.path.isfile(file_path):
        raise HTTPException(status_code=404, detail="Screenshot not found")

    return FileResponse(file_path, media_type="image/png")
