"""
Tags API router.

Unified across user tags and the binary content-type classifier
('photography' | 'other') via the `kind` column.
"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import select, func, update
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import get_db
from app.models import Photo, Tag
from app.models.tags import photo_tags
from app.models.user import User
from app.dependencies import get_current_user

router = APIRouter()


# ── Schemas ───────────────────────────────────────────────────────────────

class TagCreate(BaseModel):
    name: str
    color: Optional[str] = None
    kind: str = "user"


class TagUpdate(BaseModel):
    name: Optional[str] = None
    color: Optional[str] = None


# ── Endpoints ─────────────────────────────────────────────────────────────

@router.get("")
async def list_tags(
    kind: Optional[str] = Query(None, description="Filter by kind: user, content_type"),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """List all tags with their photo counts, optionally filtered by kind.

    Photo counts here drive the Tags / People sidebar badges, so they
    exclude discarded + hidden-folder photos to match the rest of the
    cross-cutting views. A tag that only appears on hidden-folder
    photos will still show up with count=0 — we don't drop empty tags
    because the user may want to see them in the management UI.
    """
    count_subq = (
        select(
            photo_tags.c.tag_id,
            func.count(photo_tags.c.photo_id).label("photo_count"),
            func.min(photo_tags.c.photo_id).label("first_photo_id"),
        )
        .select_from(
            photo_tags.join(Photo, Photo.id == photo_tags.c.photo_id)
        )
        .where(
            Photo.user_id == current_user.id,
            Photo.is_discarded.is_(False),
            Photo.is_hidden.is_(False),
        )
        .group_by(photo_tags.c.tag_id)
        .subquery()
    )
    stmt = (
        select(Tag, count_subq.c.photo_count, count_subq.c.first_photo_id)
        .outerjoin(count_subq, Tag.id == count_subq.c.tag_id)
        .where(Tag.user_id == current_user.id)
    )
    if kind:
        stmt = stmt.where(Tag.kind == kind)
    stmt = stmt.order_by(Tag.name.asc())

    result = await db.execute(stmt)
    rows = result.all()

    return [
        {
            "id": tag.id,
            "name": tag.name,
            "color": tag.color,
            "kind": tag.kind,
            "source": tag.source,
            "representative_photo_id": first_photo_id,
            "photo_count": int(count or 0),
        }
        for tag, count, first_photo_id in rows
    ]


@router.post("", status_code=201)
async def create_tag(body: TagCreate, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)):
    """Create a new tag. The (name, kind) pair is unique — re-creating an
    existing pair returns the existing row (idempotent for autocomplete)."""
    name = (body.name or "").strip()
    if not name:
        raise HTTPException(status_code=400, detail="Tag name is required")

    existing = await db.execute(
        select(Tag).where(Tag.name == name, Tag.kind == body.kind, Tag.user_id == current_user.id)
    )
    found = existing.scalar_one_or_none()
    if found:
        return {
            "id": found.id, "name": found.name, "color": found.color,
            "kind": found.kind, "photo_count": 0,
        }

    tag = Tag(name=name, color=body.color, kind=body.kind, user_id=current_user.id)
    db.add(tag)
    await db.commit()
    await db.refresh(tag)
    return {
        "id": tag.id, "name": tag.name, "color": tag.color,
        "kind": tag.kind, "photo_count": 0,
    }


@router.patch("/{tag_id}")
async def update_tag(
    tag_id: str, body: TagUpdate, db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """Rename or recolor a tag."""
    result = await db.execute(select(Tag).where(Tag.id == tag_id, Tag.user_id == current_user.id))
    tag = result.scalar_one_or_none()
    if not tag:
        raise HTTPException(status_code=404, detail="Tag not found")

    if body.name is not None:
        name = body.name.strip()
        if not name:
            raise HTTPException(status_code=400, detail="Tag name is required")
        tag.name = name
    if body.color is not None:
        tag.color = body.color or None

    await db.commit()
    await db.refresh(tag)
    return {"id": tag.id, "name": tag.name, "color": tag.color, "kind": tag.kind}


@router.delete("/{tag_id}", status_code=204)
async def delete_tag(tag_id: str, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)):
    """Delete a tag. Photo associations cascade-delete via the FK."""
    result = await db.execute(select(Tag).where(Tag.id == tag_id, Tag.user_id == current_user.id))
    tag = result.scalar_one_or_none()
    if not tag:
        raise HTTPException(status_code=404, detail="Tag not found")
    await db.delete(tag)
    await db.commit()
    return None
