"""Saved views: a person's named views of a page's Data List (sort, column
filters, hidden columns, display, Filter Bar values). Everyone signed in —
Studio or Portal — keeps their own; nobody else sees or changes them. Saving
under a name already used replaces that view; one view per list may be the
person's default, applied when the page opens."""

import uuid

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.security import AuthContext, get_auth_context
from app.models.navigation import Page
from app.models.saved_view import SavedView
from app.schemas.saved_view import SavedViewCreate, SavedViewRead, SavedViewUpdate

router = APIRouter(prefix="/saved-views", tags=["saved-views"])

# Views per person per list.
MAX_VIEWS = 50


def _mine(ctx: AuthContext):
    return (SavedView.tenant_id == ctx.tenant_id, SavedView.user_id == ctx.user_id)


async def _own(db: AsyncSession, ctx: AuthContext, view_id: uuid.UUID) -> SavedView:
    view = (await db.execute(select(SavedView).where(SavedView.id == view_id, *_mine(ctx)))).scalar_one_or_none()
    if view is None:
        raise HTTPException(status_code=404, detail="Saved view not found")
    return view


async def _clear_default(db: AsyncSession, ctx: AuthContext, page_id: uuid.UUID, block_id: str, keep: uuid.UUID) -> None:
    await db.execute(
        update(SavedView)
        .where(*_mine(ctx), SavedView.page_id == page_id, SavedView.block_id == block_id, SavedView.is_default.is_(True), SavedView.id != keep)
        .values(is_default=False)
    )


@router.get("", response_model=list[SavedViewRead])
async def list_saved_views(
    page_id: uuid.UUID,
    block_id: str,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> list[SavedView]:
    stmt = select(SavedView).where(*_mine(ctx), SavedView.page_id == page_id, SavedView.block_id == block_id).order_by(SavedView.name)
    return list((await db.execute(stmt)).scalars().all())


@router.post("", response_model=SavedViewRead, status_code=status.HTTP_201_CREATED)
async def save_view(
    payload: SavedViewCreate,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> SavedView:
    page = (await db.execute(select(Page.id).where(Page.id == payload.page_id, Page.tenant_id == ctx.tenant_id))).scalar_one_or_none()
    if page is None:
        raise HTTPException(status_code=404, detail="Page not found")
    name = payload.name.strip()
    existing = (
        await db.execute(select(SavedView).where(*_mine(ctx), SavedView.page_id == payload.page_id, SavedView.block_id == payload.block_id))
    ).scalars().all()
    view = next((v for v in existing if v.name.lower() == name.lower()), None)
    if view is None:
        if len(existing) >= MAX_VIEWS:
            raise HTTPException(status_code=422, detail=f"You can keep up to {MAX_VIEWS} views of this list — delete one first.")
        view = SavedView(tenant_id=ctx.tenant_id, user_id=ctx.user_id, page_id=payload.page_id, block_id=payload.block_id, name=name)
        db.add(view)
    view.state = payload.state
    view.is_default = payload.is_default
    await db.flush()
    if payload.is_default:
        await _clear_default(db, ctx, payload.page_id, payload.block_id, keep=view.id)
    await db.refresh(view)
    return view


@router.patch("/{view_id}", response_model=SavedViewRead)
async def update_view(
    view_id: uuid.UUID,
    payload: SavedViewUpdate,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> SavedView:
    view = await _own(db, ctx, view_id)
    if payload.name is not None:
        view.name = payload.name.strip()
    if payload.state is not None:
        view.state = payload.state
    if payload.is_default is not None:
        view.is_default = payload.is_default
        if payload.is_default:
            await _clear_default(db, ctx, view.page_id, view.block_id, keep=view.id)
    await db.flush()
    await db.refresh(view)
    return view


@router.delete("/{view_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_view(
    view_id: uuid.UUID,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> None:
    await db.delete(await _own(db, ctx, view_id))
    await db.flush()
