from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import func, select, update
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.auth import require_human
from app.db import get_db
from app.models import SavedView, User
from app.routes._common import owned_or_404

# Saved "smart views": per-user named snapshots of the feed's FeedFilters. Gated behind
# require_human so the shared demo account can't pollute a communal list (mirrors how other
# personal-state writes are scoped).
router = APIRouter(prefix="/api/saved-views", tags=["saved-views"])


def _serialize(v: SavedView) -> dict:
    return {
        "id": v.id,
        "name": v.name,
        "filters": v.filters or {},
        "position": v.position,
        "is_default": v.is_default,
    }


@router.get("")
def list_views(
    user: User = Depends(require_human), db: Session = Depends(get_db)
) -> list[dict]:
    rows = (
        db.execute(
            select(SavedView)
            .where(SavedView.user_id == user.id)
            .order_by(SavedView.position, SavedView.id)
        )
        .scalars()
        .all()
    )
    return [_serialize(v) for v in rows]


class CreateViewIn(BaseModel):
    name: str
    filters: dict


@router.post("")
def create_view(
    body: CreateViewIn,
    user: User = Depends(require_human),
    db: Session = Depends(get_db),
) -> dict:
    name = body.name.strip()
    if not name:
        raise HTTPException(status_code=400, detail="name is required")
    maxpos = db.execute(
        select(func.coalesce(func.max(SavedView.position), -1)).where(
            SavedView.user_id == user.id
        )
    ).scalar_one()
    view = SavedView(
        user_id=user.id, name=name[:80], filters=body.filters, position=maxpos + 1
    )
    db.add(view)
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=409, detail=f"You already have a view named “{name}”."
        )
    return _serialize(view)


def _own_view(db: Session, user: User, view_id: int) -> SavedView:
    return owned_or_404(db, SavedView, view_id, user, detail="Unknown view")


class UpdateViewIn(BaseModel):
    # PATCH: presence decides what's touched (read via model_fields_set). filters, when sent,
    # must be a real object — an explicit null is rejected (a view can't have null filters).
    name: str | None = None
    filters: dict | None = None
    is_default: bool | None = None


@router.patch("/{view_id}")
def update_view(
    view_id: int,
    body: UpdateViewIn,
    user: User = Depends(require_human),
    db: Session = Depends(get_db),
) -> dict:
    view = _own_view(db, user, view_id)
    sent = body.model_fields_set
    if "name" in sent:
        name = (body.name or "").strip()
        if not name:
            raise HTTPException(status_code=400, detail="name cannot be empty")
        view.name = name[:80]
    if "filters" in sent:
        if body.filters is None:
            raise HTTPException(status_code=400, detail="filters must be an object")
        view.filters = body.filters
    if "is_default" in sent:
        make_default = bool(body.is_default)
        if make_default:
            # At most one default per user: clear the flag on the others first.
            db.execute(
                update(SavedView)
                .where(SavedView.user_id == user.id, SavedView.id != view.id)
                .values(is_default=False)
            )
        view.is_default = make_default
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=409, detail=f"You already have a view named “{view.name}”."
        )
    return _serialize(view)


@router.delete("/{view_id}")
def delete_view(
    view_id: int,
    user: User = Depends(require_human),
    db: Session = Depends(get_db),
) -> dict:
    view = _own_view(db, user, view_id)
    db.delete(view)
    db.commit()
    return {"deleted": view_id}


class ReorderViewsIn(BaseModel):
    ids: list[int]


@router.post("/reorder")
def reorder_views(
    body: ReorderViewsIn,
    user: User = Depends(require_human),
    db: Session = Depends(get_db),
) -> dict:
    ids = body.ids
    rows = (
        db.execute(select(SavedView).where(SavedView.user_id == user.id))
        .scalars()
        .all()
    )
    by_id = {v.id: v for v in rows}
    pos = 0
    for vid in ids:
        v = by_id.get(vid)
        if v is not None:
            v.position = pos
            pos += 1
    db.commit()
    return {"ok": True}
