import json
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session, joinedload
from typing import List

from database import get_db
from models.message import StaffMessage, StaffMessageAck, QuickMessageTemplate
from models.user import User
from schemas.message import (
    SendMessageRequest, StaffMessageOut,
    QuickTemplateCreate, QuickTemplateUpdate, QuickTemplateOut,
)
from routers.deps import get_current_user, require_manager
from services.sse_bus import broadcast_sync

router = APIRouter()


def _load_msg(db: Session, msg_id: int) -> StaffMessage:
    """Reload a message with sender and acks eagerly loaded."""
    return db.query(StaffMessage).options(
        joinedload(StaffMessage.sender),
        joinedload(StaffMessage.acks),
    ).filter(StaffMessage.id == msg_id).one()


def _message_out(msg: StaffMessage) -> StaffMessageOut:
    sender_name = None
    try:
        sender_name = msg.sender.username if msg.sender else None
    except Exception:
        pass
    return StaffMessageOut(
        id=msg.id,
        sender_id=msg.sender_id,
        sender_name=sender_name,
        body=msg.body,
        target_waiter_ids=msg.target_waiter_ids,
        table_ids=msg.table_ids,
        created_at=msg.created_at,
        acked_by=[ack.waiter_id for ack in msg.acks],
    )


# ─── Quick templates ──────────────────────────────────────────────────────────

@router.get("/templates", response_model=List[QuickTemplateOut])
def list_templates(
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    return db.query(QuickMessageTemplate).filter(
        QuickMessageTemplate.is_active == True
    ).order_by(QuickMessageTemplate.sort_order, QuickMessageTemplate.id).all()


@router.post("/templates", response_model=QuickTemplateOut, status_code=status.HTTP_201_CREATED)
def create_template(
    body: QuickTemplateCreate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    t = QuickMessageTemplate(**body.model_dump())
    db.add(t)
    db.commit()
    db.refresh(t)
    return t


@router.put("/templates/{template_id}", response_model=QuickTemplateOut)
def update_template(
    template_id: int,
    body: QuickTemplateUpdate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    t = db.query(QuickMessageTemplate).filter(QuickMessageTemplate.id == template_id).first()
    if not t:
        raise HTTPException(status_code=404, detail="Template not found")
    for k, v in body.model_dump(exclude_unset=True).items():
        setattr(t, k, v)
    db.commit()
    db.refresh(t)
    return t


@router.delete("/templates/{template_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_template(
    template_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    t = db.query(QuickMessageTemplate).filter(QuickMessageTemplate.id == template_id).first()
    if not t:
        raise HTTPException(status_code=404, detail="Template not found")
    t.is_active = False
    db.commit()


# ─── Staff messages ───────────────────────────────────────────────────────────

@router.post("/send", response_model=StaffMessageOut, status_code=status.HTTP_201_CREATED)
def send_message(
    body: SendMessageRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    msg = StaffMessage(
        sender_id=user.id,
        body=body.body,
        target_waiter_ids=json.dumps(body.target_waiter_ids),
        table_ids=json.dumps(body.table_ids or []),
    )
    db.add(msg)
    db.commit()
    msg = _load_msg(db, msg.id)
    out = _message_out(msg)
    # Broadcast to targeted users (empty list = all connected users)
    target_ids = body.target_waiter_ids if body.target_waiter_ids else None
    broadcast_sync(
        "message_sent",
        {
            "id": out.id,
            "sender_id": out.sender_id,
            "sender_name": out.sender_name,
            "body": out.body,
            "table_ids": out.table_ids,
            "created_at": out.created_at.isoformat() if out.created_at else None,
        },
        user_ids=target_ids,
    )
    return out


@router.get("/unread", response_model=List[StaffMessageOut])
def get_unread_messages(
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """
    Returns messages targeting this waiter that they haven't acked yet.
    A message targets a waiter if their ID is in target_waiter_ids,
    OR if target_waiter_ids is empty (broadcast to all).
    """
    all_msgs = db.query(StaffMessage).options(
        joinedload(StaffMessage.sender),
        joinedload(StaffMessage.acks),
    ).order_by(StaffMessage.created_at.desc()).limit(200).all()
    acked_ids = {
        ack.message_id
        for ack in db.query(StaffMessageAck).filter(StaffMessageAck.waiter_id == user.id).all()
    }

    result = []
    for msg in all_msgs:
        if msg.id in acked_ids:
            continue
        targets = json.loads(msg.target_waiter_ids or "[]")
        # Empty list = broadcast to all
        if not targets or user.id in targets:
            result.append(_message_out(msg))

    return result


@router.get("/recent", response_model=List[StaffMessageOut])
def get_recent_messages(
    limit: int = 10,
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """Last N messages targeting this user (for notification history drawer)."""
    all_msgs = db.query(StaffMessage).options(
        joinedload(StaffMessage.sender),
        joinedload(StaffMessage.acks),
    ).order_by(StaffMessage.created_at.desc()).limit(200).all()
    result = []
    for msg in all_msgs:
        targets = json.loads(msg.target_waiter_ids or "[]")
        if not targets or user.id in targets:
            result.append(_message_out(msg))
        if len(result) >= limit:
            break
    return result


@router.post("/{message_id}/ack", status_code=status.HTTP_204_NO_CONTENT)
def ack_message(
    message_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    msg = db.query(StaffMessage).filter(StaffMessage.id == message_id).first()
    if not msg:
        raise HTTPException(status_code=404, detail="Message not found")
    existing = db.query(StaffMessageAck).filter(
        StaffMessageAck.message_id == message_id,
        StaffMessageAck.waiter_id == user.id,
    ).first()
    if not existing:
        db.add(StaffMessageAck(message_id=message_id, waiter_id=user.id))
        db.commit()


@router.get("/all", response_model=List[StaffMessageOut])
def list_all_messages(
    limit: int = 50,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    msgs = db.query(StaffMessage).options(
        joinedload(StaffMessage.sender),
        joinedload(StaffMessage.acks),
    ).order_by(StaffMessage.created_at.desc()).limit(limit).all()
    return [_message_out(m) for m in msgs]
