"""
Router tag (Label) con supporto tassonomia gerarchica (Feature N2).

Endpoint:
  - GET    /labels                     – elenca i tag del tenant (flat)
  - GET    /labels/tree                – albero tassonomico (Ambito > Processo > Classificazione)
  - POST   /labels                     – crea un nuovo tag / nodo tassonomico (admin)
  - PATCH  /labels/{id}                – modifica un tag (admin)
  - DELETE /labels/{id}                – elimina un tag (admin)

  - GET    /messages/{id}/labels       – tag di un messaggio
  - PUT    /messages/{id}/labels       – imposta i tag di un messaggio (sostituisce)
  - POST   /messages/{id}/labels/add   – aggiunge tag a un messaggio
  - POST   /messages/{id}/labels/remove – rimuove tag da un messaggio

  - POST   /messages/bulk-labels       – aggiunge/rimuove tag in blocco

Permessi:
  - GET /labels:            tutti gli utenti autenticati
  - POST/PATCH/DELETE:      solo admin
  - Operazioni su messaggi: utenti con accesso alla casella del messaggio
"""

import uuid

from fastapi import APIRouter, status
from sqlalchemy import select

from app.core.exceptions import ForbiddenError, NotFoundError
from app.dependencies import AdminUser, CurrentUser, DB
from app.models.message import Message
from app.schemas.label import (
    LabelCreate,
    LabelResponse,
    LabelTreeResponse,
    LabelUpdate,
    MessageBulkLabelRequest,
    MessageBulkLabelResponse,
    MessageLabelAddRequest,
    MessageLabelRemoveRequest,
    MessageLabelSetRequest,
)
from app.services.label_service import LabelService

router = APIRouter(tags=["Labels"])


# ─── Helpers ──────────────────────────────────────────────────────────────────

async def _check_message_access(
    message_id: uuid.UUID,
    current_user,
    db,
) -> Message:
    """Verifica che il messaggio esista e l'utente vi abbia accesso."""
    result = await db.execute(
        select(Message).where(
            Message.id == message_id,
            Message.tenant_id == current_user.tenant_id,
        )
    )
    message = result.scalar_one_or_none()
    if not message:
        raise NotFoundError(f"Messaggio {message_id} non trovato")

    if not current_user.is_admin:
        from app.services.permission_service import PermissionService
        perm_svc = PermissionService(db)
        if not await perm_svc.check_can_read(current_user, message.mailbox_id):
            raise ForbiddenError("Accesso al messaggio non autorizzato")

    return message


# ─── CRUD Label ───────────────────────────────────────────────────────────────

@router.get("/labels", response_model=list[LabelResponse])
async def list_labels(
    current_user: CurrentUser,
    db: DB,
) -> list[LabelResponse]:
    """Elenca tutti i tag del tenant corrente (lista flat, include nodi tassonomici)."""
    svc = LabelService(db)
    labels = await svc.list_labels(current_user.tenant_id)
    return [LabelResponse.model_validate(l) for l in labels]


@router.get("/labels/tree", response_model=list[LabelTreeResponse])
async def get_label_tree(
    current_user: CurrentUser,
    db: DB,
) -> list[LabelTreeResponse]:
    """
    Restituisce la tassonomia come albero annidato.

    Struttura: [ Ambito { children: [ Processo { children: [ Classificazione ] } ] } ]

    I nodi radice (parent_id=NULL) possono essere sia Ambiti tassonomici che label piatte.
    """
    svc = LabelService(db)
    return await svc.get_label_tree(current_user.tenant_id)


@router.post("/labels", response_model=LabelResponse, status_code=status.HTTP_201_CREATED)
async def create_label(
    data: LabelCreate,
    current_user: AdminUser,
    db: DB,
) -> LabelResponse:
    """Crea un nuovo tag (solo admin)."""
    svc = LabelService(db)
    label = await svc.create_label(current_user.tenant_id, data)
    return LabelResponse.model_validate(label)


@router.patch("/labels/{label_id}", response_model=LabelResponse)
async def update_label(
    label_id: uuid.UUID,
    data: LabelUpdate,
    current_user: AdminUser,
    db: DB,
) -> LabelResponse:
    """Modifica un tag esistente (solo admin)."""
    svc = LabelService(db)
    label = await svc.update_label(current_user.tenant_id, label_id, data)
    return LabelResponse.model_validate(label)


@router.delete("/labels/{label_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_label(
    label_id: uuid.UUID,
    current_user: AdminUser,
    db: DB,
) -> None:
    """Elimina un tag (solo admin). Viene rimosso automaticamente da tutti i messaggi."""
    svc = LabelService(db)
    await svc.delete_label(current_user.tenant_id, label_id)


# ─── Tag su singolo messaggio ─────────────────────────────────────────────────

@router.get("/messages/{message_id}/labels", response_model=list[LabelResponse])
async def get_message_labels(
    message_id: uuid.UUID,
    current_user: CurrentUser,
    db: DB,
) -> list[LabelResponse]:
    """Elenca i tag assegnati a un messaggio."""
    await _check_message_access(message_id, current_user, db)
    svc = LabelService(db)
    labels = await svc.get_message_labels(message_id, current_user.tenant_id)
    return [LabelResponse.model_validate(l) for l in labels]


@router.put("/messages/{message_id}/labels", response_model=list[LabelResponse])
async def set_message_labels(
    message_id: uuid.UUID,
    data: MessageLabelSetRequest,
    current_user: CurrentUser,
    db: DB,
) -> list[LabelResponse]:
    """
    Sostituisce tutti i tag di un messaggio.
    Passare una lista vuota per rimuovere tutti i tag.
    """
    await _check_message_access(message_id, current_user, db)
    svc = LabelService(db)
    labels = await svc.set_message_labels(
        message_id, current_user.tenant_id, data.label_ids
    )
    return [LabelResponse.model_validate(l) for l in labels]


@router.post("/messages/{message_id}/labels/add", response_model=list[LabelResponse])
async def add_message_labels(
    message_id: uuid.UUID,
    data: MessageLabelAddRequest,
    current_user: CurrentUser,
    db: DB,
) -> list[LabelResponse]:
    """Aggiunge tag a un messaggio senza rimuovere quelli esistenti."""
    await _check_message_access(message_id, current_user, db)
    svc = LabelService(db)
    labels = await svc.add_message_labels(
        message_id, current_user.tenant_id, data.label_ids
    )
    return [LabelResponse.model_validate(l) for l in labels]


@router.post("/messages/{message_id}/labels/remove", response_model=list[LabelResponse])
async def remove_message_labels(
    message_id: uuid.UUID,
    data: MessageLabelRemoveRequest,
    current_user: CurrentUser,
    db: DB,
) -> list[LabelResponse]:
    """Rimuove specifici tag da un messaggio."""
    await _check_message_access(message_id, current_user, db)
    svc = LabelService(db)
    labels = await svc.remove_message_labels(
        message_id, current_user.tenant_id, data.label_ids
    )
    return [LabelResponse.model_validate(l) for l in labels]


# ─── Bulk labels ──────────────────────────────────────────────────────────────

@router.post("/messages/bulk-labels", response_model=MessageBulkLabelResponse)
async def bulk_labels(
    data: MessageBulkLabelRequest,
    current_user: CurrentUser,
    db: DB,
) -> MessageBulkLabelResponse:
    """
    Aggiunge o rimuove tag da più messaggi in blocco.

    - action="add":    aggiunge i tag ai messaggi indicati
    - action="remove": rimuove i tag dai messaggi indicati

    I messaggi non accessibili all'utente vengono silenziosamente ignorati.
    """
    if not data.message_ids or not data.label_ids:
        return MessageBulkLabelResponse(updated=0)

    # Per utenti non-admin filtra per caselle accessibili
    message_ids = [str(mid) for mid in data.message_ids]
    if not current_user.is_admin:
        from app.services.permission_service import PermissionService
        perm_svc = PermissionService(db)
        visible = await perm_svc.get_visible_mailboxes(current_user)
        visible_set = set(visible) if visible else set()

        # Filtra i messaggi per caselle visibili
        result = await db.execute(
            select(Message.id).where(
                Message.id.in_(data.message_ids),
                Message.tenant_id == current_user.tenant_id,
                Message.mailbox_id.in_(visible_set),
            )
        )
        filtered_ids = list(result.scalars().all())
    else:
        filtered_ids = data.message_ids

    svc = LabelService(db)
    if data.action == "add":
        updated = await svc.bulk_add_labels(
            filtered_ids, current_user.tenant_id, data.label_ids
        )
    else:
        updated = await svc.bulk_remove_labels(
            filtered_ids, current_user.tenant_id, data.label_ids
        )

    return MessageBulkLabelResponse(updated=updated)
