import uuid
from datetime import datetime
from fastapi import APIRouter, Depends, Query, HTTPException
from sqlalchemy import select, func, or_, and_, case
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from pydantic import BaseModel

from app.core.actors import ActorType
from app.core.dependencies import GESTIONNAIRE_PLUS, get_current_user, require_role
from app.core.responses import api_response, page_response
from app.db.database import get_async_session
from app.models.signalement import Signalement
from app.models.utilisateur import Utilisateur
from app.models.note_interne import NoteInterne
from app.models.discussion import Discussion
from app.models.message import Message
from app.models.signalement_timeline import SignalementTimeline
from app.services.signalement_service import SignalementService, _serialize
from app.services.message_service import MessageService
from app.realtime import events as ws_events

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


class UpdateStatutBody(BaseModel):
    statut: str


class UpdatePrioriteBody(BaseModel):
    priorite: str


class AssignCaseBody(BaseModel):
    managerId: str
    managerName: str | None = None


class AddCaseNoteBody(BaseModel):
    content: str
    isPrivate: bool = True


class SendCaseMessageBody(BaseModel):
    content: str
    attachmentPaths: list[str] | None = None


class OpenCaseDiscussionBody(BaseModel):
    caseId: str
    caseReference: str | None = None


@router.get("/dossiers")
async def list_dossiers(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, alias="pageSize", ge=1, le=100),
    sort_by: str | None = Query(None, alias="sortBy"),
    sort_descending: bool = Query(True, alias="sortDescending"),
    status: str | None = Query(None),
    priority: str | None = Query(None),
    category: str | None = Query(None),
    assigned_to: str | None = Query(None, alias="assignedTo"),
    search: str | None = Query(None),
    date_from: str | None = Query(None, alias="dateFrom"),
    date_to: str | None = Query(None, alias="dateTo"),
    is_overdue: bool | None = Query(None, alias="isOverdue"),
    is_unassigned: bool | None = Query(None, alias="isUnassigned"),
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    """
    Enhanced compatibility endpoint for listing dossiers/cases.
    Supports all search filters, query params, pagination, and role constraints.
    """
    service = SignalementService(db)
    zero_based_page = max(page - 1, 0)

    q = select(Signalement).options(
        selectinload(Signalement.gestionnaire),
        selectinload(Signalement.pieces_jointes),
    )

    conditions = []

    if current_user.type_utilisateur == "ADMIN_SYSTEME":
        # L'admin système voit tous les dossiers sans restriction
        pass
    elif current_user.type_utilisateur == "GESTIONNAIRE":
        # Le gestionnaire voit ses dossiers assignés + les non assignés
        conditions.append(
            or_(
                Signalement.gestionnaire_id == current_user.id,
                Signalement.gestionnaire_id.is_(None),
                Signalement.utilisateur_id == current_user.id,
                Signalement.actor_user_id == current_user.id,
            )
        )
    else:
        # Utilisateur normal : voit uniquement ses propres signalements
        conditions.append(
            or_(
                Signalement.utilisateur_id == current_user.id,
                Signalement.actor_user_id == current_user.id,
            )
        )

    if status:
        norm_status = status.lower()
        if norm_status == "open":
            conditions.append(Signalement.statut == "NOUVEAU")
        elif norm_status == "inprogress":
            conditions.append(Signalement.statut.in_(["ASSIGNE", "EN_COURS"]))
        elif norm_status == "pending":
            conditions.append(Signalement.statut == "EN_ATTENTE")
        elif norm_status == "resolved":
            conditions.append(Signalement.statut == "TRAITE")
        elif norm_status == "closed":
            conditions.append(Signalement.statut == "CLOTURE")
        elif norm_status == "archived":
            conditions.append(Signalement.statut.in_(["CLOTURE", "TRAITE"]))
        else:
            conditions.append(Signalement.statut == status)

    if priority:
        norm_prio = priority.lower()
        if norm_prio == "low":
            conditions.append(Signalement.priorite == "BASSE")
        elif norm_prio == "medium":
            conditions.append(Signalement.priorite == "MOYENNE")
        elif norm_prio == "high":
            conditions.append(Signalement.priorite == "HAUTE")
        elif norm_prio == "urgent":
            conditions.append(Signalement.priorite == "URGENTE")
        else:
            conditions.append(Signalement.priorite == priority.upper())

    if category:
        conditions.append(Signalement.type == category)

    if assigned_to:
        conditions.append(Signalement.gestionnaire_id == uuid.UUID(assigned_to))

    if is_unassigned is not None:
        if is_unassigned:
            conditions.append(Signalement.gestionnaire_id.is_(None))
        else:
            conditions.append(Signalement.gestionnaire_id.is_not(None))

    if search:
        search_pattern = f"%{search}%"
        conditions.append(
            or_(
                Signalement.titre.ilike(search_pattern),
                Signalement.description.ilike(search_pattern),
                Signalement.reference.ilike(search_pattern),
            )
        )

    if date_from:
        try:
            dt_from = datetime.fromisoformat(date_from.replace("Z", "+00:00"))
            conditions.append(Signalement.date_creation >= dt_from)
        except ValueError:
            pass

    if date_to:
        try:
            dt_to = datetime.fromisoformat(date_to.replace("Z", "+00:00"))
            conditions.append(Signalement.date_creation <= dt_to)
        except ValueError:
            pass

    if conditions:
        q = q.where(and_(*conditions))

    if sort_by:
        sort_column = Signalement.date_creation
        sort_by_lower = sort_by.lower()
        if "created" in sort_by_lower:
            sort_column = Signalement.date_creation
        elif "updated" in sort_by_lower or "modification" in sort_by_lower:
            sort_column = Signalement.date_modification
        elif "priority" in sort_by_lower:
            sort_column = Signalement.priorite
        elif "status" in sort_by_lower:
            sort_column = Signalement.statut
        elif "reference" in sort_by_lower:
            sort_column = Signalement.reference

        if sort_descending:
            q = q.order_by(sort_column.desc())
        else:
            q = q.order_by(sort_column.asc())
    else:
        # Default order: open/active cases first (NOUVEAU, ASSIGNE, EN_COURS, EN_ATTENTE), then creation date desc
        status_order = case(
            (Signalement.statut.in_(["NOUVEAU", "ASSIGNE", "EN_COURS", "EN_ATTENTE"]), 0),
            else_=1
        )
        q = q.order_by(status_order.asc(), Signalement.date_creation.desc())

    total = (await db.execute(select(func.count()).select_from(q.subquery()))).scalar_one()
    rows = (await db.execute(q.offset(zero_based_page * page_size).limit(page_size))).scalars().all()

    items = []
    for r in rows:
        unread = await service._get_unread_counts_for_staff(signalement_ids=[r.id])
        unread_count = unread.get(str(r.id), 0)
        items.append(_serialize(r, with_pieces=True, unread_messages_count=unread_count))

    return page_response(items, total=total, page=zero_based_page, size=page_size)


@router.get("/dossiers/user/{userId}")
async def list_user_dossiers(
    userId: str,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    service = SignalementService(db)
    q = (
        select(Signalement)
        .options(
            selectinload(Signalement.gestionnaire),
            selectinload(Signalement.pieces_jointes),
        )
        .where(Signalement.gestionnaire_id == uuid.UUID(userId))
        .order_by(Signalement.date_creation.desc())
    )
    rows = (await db.execute(q)).scalars().all()

    items = []
    for r in rows:
        unread = await service._get_unread_counts_for_staff(signalement_ids=[r.id])
        unread_count = unread.get(str(r.id), 0)
        items.append(_serialize(r, with_pieces=True, unread_messages_count=unread_count))

    return api_response(items)


@router.get("/dossiers/gestionnaires/disponibles")
async def list_available_managers(
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    q = select(Utilisateur).where(
        Utilisateur.type_utilisateur.in_(GESTIONNAIRE_PLUS)
    )
    users = (await db.execute(q)).scalars().all()

    managers = []
    for u in users:
        count_q = select(func.count(Signalement.id)).where(
            Signalement.gestionnaire_id == u.id,
            Signalement.statut.notin_(["TRAITE", "CLOTURE"])
        )
        current_case_count = (await db.execute(count_q)).scalar() or 0

        managers.append({
            "id": str(u.id),
            "name": f"{u.prenom} {u.nom}".strip(),
            "email": u.email,
            "isOnline": u.est_actif,
            "currentCaseCount": current_case_count,
        })

    return api_response(managers)


@router.get("/dossiers/{caseId}")
async def get_dossier(
    caseId: str,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    service = SignalementService(db)
    data = await service.get_by_id(caseId, current_user)
    return api_response(data, message="Dossier récupéré")


@router.put("/dossiers/{caseId}/statut")
async def update_dossier_status(
    caseId: str,
    body: UpdateStatutBody,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(require_role(*GESTIONNAIRE_PLUS)),
):
    status = body.statut.upper()
    if status == "INPROGRESS":
        status = "EN_COURS"
    elif status == "OPEN":
        status = "NOUVEAU"
    elif status == "PENDING":
        status = "EN_ATTENTE"
    elif status == "RESOLVED":
        status = "TRAITE"
    elif status == "CLOSED" or status == "ARCHIVED":
        status = "CLOTURE"

    service = SignalementService(db)
    data = await service.update_statut(caseId, status, current_user)
    return api_response(data, message="Statut mis à jour")


@router.put("/dossiers/{caseId}/priorite")
async def update_dossier_priority(
    caseId: str,
    body: UpdatePrioriteBody,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(require_role(*GESTIONNAIRE_PLUS)),
):
    priority = body.priorite.upper()
    if priority == "LOW":
        priority = "BASSE"
    elif priority == "MEDIUM":
        priority = "MOYENNE"
    elif priority == "HIGH":
        priority = "HAUTE"
    elif priority == "URGENT":
        priority = "URGENTE"

    service = SignalementService(db)
    s = await service._get_or_404(caseId)
    old_priority = s.priorite
    s.priorite = priority

    timeline_event = SignalementTimeline(
        signalement_id=s.id,
        type_evenement="CHANGEMENT_PRIORITE",
        description=f"Priorité changée de {old_priority} vers {priority}",
        effectue_par_id=current_user.id,
        effectue_par_nom=f"{current_user.prenom} {current_user.nom}",
        valeur_precedente=old_priority,
        nouvelle_valeur=priority,
    )
    db.add(timeline_event)
    await db.commit()
    await db.refresh(s)

    result = _serialize(s, with_pieces=True)
    await ws_events.broadcast_signalement_assigned(str(s.id), result)
    return api_response(result, message="Priorité mise à jour")


@router.put("/dossiers/{caseId}/assigner")
async def assign_dossier(
    caseId: str,
    body: AssignCaseBody,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(require_role(*GESTIONNAIRE_PLUS)),
):
    service = SignalementService(db)
    data = await service.assigner(caseId, body.managerId, current_user)
    return api_response(data, message="Dossier assigné")


@router.put("/dossiers/{caseId}/desassigner")
async def unassign_dossier(
    caseId: str,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(require_role(*GESTIONNAIRE_PLUS)),
):
    service = SignalementService(db)
    s = await service._get_or_404(caseId)
    old_manager_name = ""
    if s.gestionnaire:
        old_manager_name = f"{s.gestionnaire.prenom} {s.gestionnaire.nom}".strip()

    s.gestionnaire_id = None
    s.date_assignation = None
    s.statut = "NOUVEAU"

    timeline_event = SignalementTimeline(
        signalement_id=s.id,
        type_evenement="ASSIGNATION",
        description=f"Désassigné (précédemment assigné à {old_manager_name})" if old_manager_name else "Désassigné",
        effectue_par_id=current_user.id,
        effectue_par_nom=f"{current_user.prenom} {current_user.nom}",
    )
    db.add(timeline_event)
    await db.commit()
    await db.refresh(s)

    result = _serialize(s, with_pieces=True)
    await ws_events.broadcast_signalement_assigned(str(s.id), result)
    return api_response(result, message="Dossier désassigné")


@router.get("/dossiers/{caseId}/messages")
async def get_dossier_messages(
    caseId: str,
    page: int = Query(0, ge=0),
    size: int = Query(100, ge=1, le=100),
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    discussion = (
        await db.execute(
            select(Discussion).where(Discussion.signalement_id == uuid.UUID(caseId))
        )
    ).scalar_one_or_none()

    if not discussion:
        return api_response([], message="Aucune discussion trouvée")

    service = MessageService(db)
    items, total = await service.list_messages(
        str(discussion.id),
        actor_type=ActorType.AUTH_USER.value,
        actor_id=current_user.id,
        current_user=current_user,
        page=page,
        size=size
    )

    mapped = []
    for m in items:
        mapped.append({
            "id": m["id"],
            "caseId": caseId,
            "senderId": m["auteurId"] or m["acteurId"] or "",
            "senderName": m["auteurNom"] or "Système",
            "senderRole": "GESTIONNAIRE" if m["sourceActeur"] == "AUTH_USER" else "UTILISATEUR",
            "content": m["contenu"],
            "sentAt": m["dateCreation"].isoformat() if isinstance(m["dateCreation"], datetime) else str(m["dateCreation"]),
            "isRead": m["estLu"],
            "isSystemMessage": m["type"] == "SYSTEM",
            "attachments": [
                {
                    "id": pj.get("id"),
                    "fileName": pj.get("nomFichier") or pj.get("fileName") or "",
                    "fileUrl": pj.get("url") or "",
                    "fileSize": pj.get("tailleFichier") or pj.get("fileSize") or 0,
                    "mimeType": pj.get("typeFichier") or pj.get("mimeType") or "",
                }
                for pj in (m.get("piecesJointes") or [])
            ]
        })
    return api_response(mapped)


@router.post("/dossiers/{caseId}/messages")
async def send_dossier_message(
    caseId: str,
    body: SendCaseMessageBody,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    discussion = (
        await db.execute(
            select(Discussion).where(Discussion.signalement_id == uuid.UUID(caseId))
        )
    ).scalar_one_or_none()

    if not discussion:
        sig = (
            await db.execute(
                select(Signalement).where(Signalement.id == uuid.UUID(caseId))
            )
        ).scalar_one_or_none()
        if not sig:
            raise HTTPException(status_code=404, detail="Dossier introuvable")

        discussion = Discussion(
            signalement_id=sig.id,
            signalement_reference=sig.reference,
            utilisateur_id=sig.utilisateur_id,
            actor_type=ActorType.AUTH_USER.value,
            actor_user_id=sig.utilisateur_id,
            gestionnaire_id=current_user.id,
            titre=sig.titre,
        )
        db.add(discussion)
        await db.commit()
        await db.refresh(discussion)

    service = MessageService(db)
    from app.schemas.message import MessageCreate
    msg_create = MessageCreate(
        contenu=body.content,
        piecesJointes=[{"url": path} for path in (body.attachmentPaths or [])]
    )
    m = await service.envoyer_message(
        str(discussion.id),
        msg_create,
        actor_type=ActorType.AUTH_USER.value,
        actor_id=current_user.id,
        current_user=current_user
    )

    result = {
        "id": m["id"],
        "caseId": caseId,
        "senderId": m["auteurId"] or m["acteurId"] or "",
        "senderName": m["auteurNom"] or "Système",
        "senderRole": "GESTIONNAIRE",
        "content": m["contenu"],
        "sentAt": m["dateCreation"].isoformat() if isinstance(m["dateCreation"], datetime) else str(m["dateCreation"]),
        "isRead": m["estLu"],
        "isSystemMessage": m["type"] == "SYSTEM",
        "attachments": [
            {
                "id": pj.get("id"),
                "fileName": pj.get("nomFichier") or pj.get("fileName") or "",
                "fileUrl": pj.get("url") or "",
                "fileSize": pj.get("tailleFichier") or pj.get("fileSize") or 0,
                "mimeType": pj.get("typeFichier") or pj.get("mimeType") or "",
            }
            for pj in (m.get("piecesJointes") or [])
        ]
    }
    return api_response(result)


@router.put("/dossiers/{caseId}/messages/lus")
async def mark_dossier_messages_read(
    caseId: str,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    discussion = (
        await db.execute(
            select(Discussion).where(Discussion.signalement_id == uuid.UUID(caseId))
        )
    ).scalar_one_or_none()

    if not discussion:
        return api_response(None, message="Discussion non trouvée")

    service = MessageService(db)
    await service.marquer_lu(
        str(discussion.id),
        actor_type=ActorType.AUTH_USER.value,
        actor_id=current_user.id,
        current_user=current_user
    )
    return api_response(None, message="Messages marqués comme lus")


@router.get("/dossiers/{caseId}/notes")
async def get_dossier_notes(
    caseId: str,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(require_role(*GESTIONNAIRE_PLUS)),
):
    q = (
        select(NoteInterne)
        .where(NoteInterne.signalement_id == uuid.UUID(caseId))
        .order_by(NoteInterne.date_creation.asc())
    )
    notes = (await db.execute(q)).scalars().all()

    mapped = []
    for n in notes:
        author_name = "Système"
        if n.auteur:
            author_name = f"{n.auteur.prenom} {n.auteur.nom}".strip()
        mapped.append({
            "id": str(n.id),
            "caseId": caseId,
            "authorId": str(n.auteur_id) if n.auteur_id else "",
            "authorName": author_name,
            "content": n.contenu,
            "createdAt": n.date_creation.isoformat() if isinstance(n.date_creation, datetime) else str(n.date_creation),
            "isPrivate": True
        })
    return api_response(mapped)


@router.post("/dossiers/{caseId}/notes")
async def add_dossier_note(
    caseId: str,
    body: AddCaseNoteBody,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(require_role(*GESTIONNAIRE_PLUS)),
):
    from app.schemas.signalement import NoteInterneCreate
    service = SignalementService(db)
    note_create = NoteInterneCreate(contenu=body.content)
    n = await service.ajouter_note(caseId, note_create, current_user)

    author_name = f"{current_user.prenom} {current_user.nom}".strip()
    result = {
        "id": n["id"],
        "caseId": caseId,
        "authorId": str(current_user.id),
        "authorName": author_name,
        "content": n["contenu"],
        "createdAt": n["dateCreation"].isoformat() if isinstance(n["dateCreation"], datetime) else str(n["dateCreation"]),
        "isPrivate": body.isPrivate
    }
    return api_response(result, status_code=201)


@router.get("/dossiers/{caseId}/historique")
async def get_dossier_history(
    caseId: str,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(require_role(*GESTIONNAIRE_PLUS)),
):
    service = SignalementService(db)
    history = await service.get_timeline(caseId, current_user)

    mapped = []
    for h in history:
        mapped.append({
            "id": h["id"],
            "caseId": caseId,
            "action": h["typeEvenement"],
            "actorId": h["acteurId"] or "",
            "actorName": h["effectueParNom"] or "Système",
            "timestamp": h["timestamp"].isoformat() if isinstance(h["timestamp"], datetime) else str(h["timestamp"]),
            "details": h["description"],
            "previousValue": h["valeurPrecedente"],
            "newValue": h["nouvelleValeur"],
        })
    return api_response(mapped)


@router.post("/dossiers/{caseId}/discussion")
async def open_dossier_discussion(
    caseId: str,
    body: OpenCaseDiscussionBody,
    db: AsyncSession = Depends(get_async_session),
    current_user=Depends(get_current_user),
):
    from app.models.discussion import Discussion
    from app.models.signalement import Signalement

    discussion = (
        await db.execute(
            select(Discussion).where(Discussion.signalement_id == uuid.UUID(caseId))
        )
    ).scalar_one_or_none()

    if not discussion:
        sig = (
            await db.execute(
                select(Signalement).where(Signalement.id == uuid.UUID(caseId))
            )
        ).scalar_one_or_none()
        if not sig:
            raise HTTPException(status_code=404, detail="Dossier introuvable")

        discussion = Discussion(
            signalement_id=sig.id,
            signalement_reference=sig.reference,
            utilisateur_id=sig.utilisateur_id,
            actor_type=sig.actor_type,
            actor_user_id=sig.actor_user_id,
            actor_guest_id=sig.actor_guest_id,
            gestionnaire_id=current_user.id,
            titre=sig.titre,
        )
        db.add(discussion)
        await db.commit()
        await db.refresh(discussion)

    return api_response({"discussionId": str(discussion.id)})


@router.get("/categories")
async def list_categories():
    """Compatibility endpoint for legacy frontend route `/api/v1/categories`."""
    categories = [
        "HARCELEMENT_MORAL",
        "HARCELEMENT_SEXUEL",
        "VBG",
        "DISCRIMINATION",
        "VIOLENCE",
        "AUTRE",
    ]
    return api_response(categories, message="Catégories récupérées")
