from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import func, or_, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.auth import admin_user, current_user
from app.db import get_db
from app.models import ChannelTag, Tag, User
from app.sync.autotag import run_autotag_all

router = APIRouter(prefix="/api/tags", tags=["tags"])


@router.get("")
def list_tags(user: User = Depends(current_user), db: Session = Depends(get_db)) -> list[dict]:
    rows = db.execute(
        select(Tag, func.count(ChannelTag.id))
        .outerjoin(ChannelTag, ChannelTag.tag_id == Tag.id)
        .where(or_(Tag.user_id.is_(None), Tag.user_id == user.id))
        .group_by(Tag.id)
        .order_by(Tag.category, Tag.name)
    ).all()
    return [
        {
            "id": tag.id,
            "name": tag.name,
            "color": tag.color,
            "category": tag.category,
            "system": tag.user_id is None,
            "channel_count": count,
        }
        for tag, count in rows
    ]


@router.post("")
def create_tag(
    payload: dict,
    user: User = Depends(current_user),
    db: Session = Depends(get_db),
) -> dict:
    name = (payload.get("name") or "").strip()
    if not name:
        raise HTTPException(status_code=400, detail="name is required")
    category = payload.get("category") or "other"
    tag = Tag(
        user_id=user.id,
        name=name,
        color=payload.get("color"),
        category=category if category in ("language", "topic", "other") else "other",
    )
    db.add(tag)
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(status_code=409, detail=f"You already have a tag named “{name}”.")
    return {"id": tag.id, "name": tag.name, "color": tag.color, "category": tag.category}


def _own_tag(db: Session, user: User, tag_id: int) -> Tag:
    tag = db.get(Tag, tag_id)
    if tag is None or tag.user_id != user.id:
        # System tags (user_id NULL) are not user-editable.
        raise HTTPException(status_code=404, detail="Unknown tag")
    return tag


@router.patch("/{tag_id}")
def update_tag(
    tag_id: int,
    payload: dict,
    user: User = Depends(current_user),
    db: Session = Depends(get_db),
) -> dict:
    tag = _own_tag(db, user, tag_id)
    if "name" in payload:
        name = (payload.get("name") or "").strip()
        if not name:
            raise HTTPException(status_code=400, detail="name cannot be empty")
        tag.name = name
    if "color" in payload:
        tag.color = payload.get("color")
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(status_code=409, detail=f"You already have a tag named “{tag.name}”.")
    return {"id": tag.id, "name": tag.name, "color": tag.color, "category": tag.category}


@router.delete("/{tag_id}")
def delete_tag(
    tag_id: int,
    user: User = Depends(current_user),
    db: Session = Depends(get_db),
) -> dict:
    tag = _own_tag(db, user, tag_id)
    db.delete(tag)  # ChannelTag rows cascade on tag delete.
    db.commit()
    return {"deleted": tag_id}


@router.post("/recompute")
def recompute(
    _: User = Depends(admin_user),
    db: Session = Depends(get_db),
    only_missing: bool = False,
) -> dict:
    return run_autotag_all(db, only_missing=only_missing)
