from fastapi import APIRouter, Depends, HTTPException, status, Body
from sqlalchemy.orm import Session
from typing import List
from datetime import datetime, timezone, timedelta

from database import get_db
from models.table import Table, TableGroup
from models.order import Order
from models.reservation import Reservation
from models.user import User, WaiterZone
from schemas.table import (
    TableCreate, TableUpdate, TableFloorplanUpdate, TableOut, UpcomingReservation,
    TableGroupCreate, TableGroupUpdate, TableGroupOut,
    TableBatchCreate, MAX_TABLE_NAME_LENGTH,
)
from routers.deps import get_current_user, require_tables_manager
from services.sse_bus import broadcast_sync

# Tables with a pending reservation due within this many hours get the RESERVED badge
RESERVATION_LOOKAHEAD_HOURS = 4

router = APIRouter()


# ── Table Groups ──────────────────────────────────────────────────────────────

@router.get("/groups", response_model=List[TableGroupOut])
def list_groups(db: Session = Depends(get_db), user: User = Depends(get_current_user)):
    return db.query(TableGroup).order_by(TableGroup.sort_order).all()


@router.post("/groups", response_model=TableGroupOut, status_code=status.HTTP_201_CREATED)
def create_group(body: TableGroupCreate, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    if db.query(TableGroup).filter(TableGroup.name == body.name).first():
        raise HTTPException(status_code=400, detail="Group name already exists")
    sort_order = db.query(TableGroup).count()
    group = TableGroup(name=body.name, prefix=body.prefix, color=body.color, sort_order=sort_order)
    db.add(group)
    db.commit()
    db.refresh(group)
    return group


@router.put("/groups/reorder", status_code=status.HTTP_204_NO_CONTENT)
def reorder_groups(body: List[int] = Body(...), db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    for idx, group_id in enumerate(body):
        db.query(TableGroup).filter(TableGroup.id == group_id).update({"sort_order": idx})
    db.commit()


@router.put("/groups/{group_id}", response_model=TableGroupOut)
def update_group(group_id: int, body: TableGroupUpdate, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    group = db.query(TableGroup).filter(TableGroup.id == group_id).first()
    if not group:
        raise HTTPException(status_code=404, detail="Group not found")
    for field, value in body.model_dump(exclude_none=True).items():
        setattr(group, field, value)
    db.commit()
    db.refresh(group)
    return group


@router.delete("/groups/{group_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_group(group_id: int, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    group = db.query(TableGroup).filter(TableGroup.id == group_id).first()
    if not group:
        raise HTTPException(status_code=404, detail="Group not found")
    db.query(Table).filter(Table.group_id == group_id).update({"group_id": None})
    db.delete(group)
    db.commit()


# ── Tables ────────────────────────────────────────────────────────────────────

def _next_global_number(db: Session) -> int:
    last = db.query(Table).order_by(Table.number.desc()).first()
    return (last.number + 1) if last else 1


@router.get("/", response_model=List[TableOut])
def list_tables(include_inactive: bool = False, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
    q = db.query(Table)
    if not include_inactive:
        q = q.filter(Table.is_active == True)

    # Zone-based filtering for waiters
    if user.role != "superadmin" and not user.perm_access_dashboard:
        zones = db.query(WaiterZone).filter(WaiterZone.waiter_id == user.id).all()
        # No zone rows → sees nothing
        if not zones:
            return []
        # Any row with group_id=None → sees all tables (all-zones sentinel)
        has_all_zones = any(z.group_id is None for z in zones)
        if not has_all_zones:
            allowed_group_ids = [z.group_id for z in zones]
            q = q.filter(Table.group_id.in_(allowed_group_ids))

    tables = q.order_by(Table.group_id, Table.number).all()

    active_table_ids = {
        row[0] for row in db.query(Order.table_id).filter(
            Order.status.in_(["open", "partially_paid", "paid"])
        ).all()
    }

    # Fetch pending reservations due within the lookahead window for the RESERVED badge
    now = datetime.now(timezone.utc)
    lookahead = now + timedelta(hours=RESERVATION_LOOKAHEAD_HOURS)
    upcoming_reservations = (
        db.query(Reservation)
        .filter(
            Reservation.status == "pending",
            Reservation.table_id.isnot(None),
            Reservation.reserved_for >= now,
            Reservation.reserved_for <= lookahead,
        )
        .all()
    )
    # Map table_id → soonest upcoming reservation
    reservation_by_table: dict[int, Reservation] = {}
    for res in sorted(upcoming_reservations, key=lambda r: r.reserved_for):
        if res.table_id not in reservation_by_table:
            reservation_by_table[res.table_id] = res

    result = []
    for t in tables:
        out = TableOut.model_validate(t)
        out.has_active_order = t.id in active_table_ids
        res = reservation_by_table.get(t.id)
        if res:
            out.upcoming_reservation = UpcomingReservation.model_validate(res)
        result.append(out)
    return result


@router.post("/", response_model=TableOut, status_code=status.HTTP_201_CREATED)
def create_table(body: TableCreate, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    number = _next_global_number(db)
    table = Table(number=number, label=body.label, group_id=body.group_id, is_active=True, seat_count=body.seat_count)
    db.add(table)
    db.commit()
    db.refresh(table)
    broadcast_sync("table_list_changed", {"action": "created", "table_id": table.id})
    return table


@router.post("/batch", response_model=List[TableOut], status_code=status.HTTP_201_CREATED)
def batch_create_tables(body: TableBatchCreate, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    if body.count < 1 or body.count > 200:
        raise HTTPException(status_code=400, detail="Count must be between 1 and 200")

    # Group-local label numbering: find highest suffix already used in this group
    existing_in_group = (
        db.query(Table)
        .filter(Table.group_id == body.group_id)
        .all()
    ) if body.group_id else []

    # Extract trailing integers from existing labels that start with this prefix
    used = []
    for t in existing_in_group:
        if t.label and t.label.startswith(body.name_prefix):
            suffix = t.label[len(body.name_prefix):]
            if suffix.isdigit():
                used.append(int(suffix))
    start_label_n = (max(used) + 1) if used else 1

    # Guard: worst-case label is prefix + highest number that will be generated
    last_n = start_label_n + body.count - 1
    worst_case = f"{body.name_prefix}{last_n}"
    if len(worst_case) > MAX_TABLE_NAME_LENGTH:
        raise HTTPException(
            status_code=400,
            detail=f"Table name '{worst_case}' would exceed {MAX_TABLE_NAME_LENGTH} characters. Shorten the prefix or reduce the count.",
        )

    created = []
    for i in range(body.count):
        label_n = start_label_n + i
        global_number = _next_global_number(db)
        table = Table(
            number=global_number,
            label=f"{body.name_prefix}{label_n}",
            group_id=body.group_id,
            is_active=True,
        )
        db.add(table)
        db.flush()
        created.append(table)
    db.commit()
    for t in created:
        db.refresh(t)
    return created


@router.put("/{table_id}", response_model=TableOut)
def update_table(table_id: int, body: TableUpdate, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    table = db.query(Table).filter(Table.id == table_id).first()
    if not table:
        raise HTTPException(status_code=404, detail="Table not found")
    for field, value in body.model_dump(exclude_none=True).items():
        setattr(table, field, value)
    db.commit()
    db.refresh(table)
    return table


@router.delete("/{table_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_table(table_id: int, hard: bool = False, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    table = db.query(Table).filter(Table.id == table_id).first()
    if not table:
        raise HTTPException(status_code=404, detail="Table not found")
    active_order = db.query(Order).filter(
        Order.table_id == table_id,
        Order.status.in_(["open", "partially_paid", "paid"])
    ).first()
    if active_order:
        raise HTTPException(
            status_code=400,
            detail="Cannot delete or deactivate a table with an active order"
        )
    if hard:
        # Delete all past (non-active) orders for this table so FK constraint doesn't block deletion.
        # Active orders are already blocked above. Items/waiters/print_logs cascade via ORM.
        past_orders = db.query(Order).filter(Order.table_id == table_id).all()
        for order in past_orders:
            db.delete(order)
        db.flush()
        db.delete(table)
    else:
        table.is_active = False
    db.commit()


@router.get("/{table_id}/status")
def table_status(table_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
    table = db.query(Table).filter(Table.id == table_id).first()
    if not table:
        raise HTTPException(status_code=404, detail="Table not found")
    active_order = (
        db.query(Order)
        .filter(Order.table_id == table_id, Order.status.in_(["open", "partially_paid", "paid"]))
        .first()
    )
    return {
        "table": TableOut.model_validate(table),
        "active_order_id": active_order.id if active_order else None,
        "order_status": active_order.status if active_order else None,
    }


@router.put("/{table_id}/floorplan", response_model=TableOut)
def update_floorplan(table_id: int, body: TableFloorplanUpdate, db: Session = Depends(get_db), user: User = Depends(require_tables_manager)):
    table = db.query(Table).filter(Table.id == table_id).first()
    if not table:
        raise HTTPException(status_code=404, detail="Table not found")
    table.floor_x = body.floor_x
    table.floor_y = body.floor_y
    db.commit()
    db.refresh(table)
    return table
