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

from database import get_db
from models.tabs import Tab, TabEntry, TabPayment
from models.customers import Customer
from models.order import Order, OrderItem
from models.user import User
from schemas.tabs import TabOut, TabEntryOut, TabPaymentOut, TabPayRequest, TabForgiveRequest, TabCloseRequest
from routers.deps import require_manager

router = APIRouter()


def _utcnow():
    return datetime.now(timezone.utc)


def _tab_out(tab: Tab) -> dict:
    c = tab.customer
    customer_name = c.name if c else None
    if c and c.nickname:
        customer_name = f"{c.name} «{c.nickname}»"
    customer_phone = c.phone if c else None

    total_charged = round(sum(e.amount for e in tab.entries), 2)
    total_paid = round(sum(p.amount for p in tab.payments), 2)
    balance = round(total_charged - total_paid, 2)

    closed_by_name = None
    if tab.closed_by:
        closed_by_name = tab.closed_by.full_name or tab.closed_by.username

    entries_out = []
    for e in sorted(tab.entries, key=lambda x: x.created_at):
        creator = e.created_by.full_name or e.created_by.username if e.created_by else None
        entries_out.append({
            "id": e.id, "tab_id": e.tab_id, "order_id": e.order_id,
            "order_item_id": e.order_item_id, "amount": e.amount,
            "description": e.description, "created_by_id": e.created_by_id,
            "created_by_name": creator, "created_at": e.created_at,
        })

    payments_out = []
    for p in sorted(tab.payments, key=lambda x: x.created_at):
        receiver = p.received_by.full_name or p.received_by.username if p.received_by else None
        payments_out.append({
            "id": p.id, "tab_id": p.tab_id, "amount": p.amount,
            "payment_method": p.payment_method, "received_by_id": p.received_by_id,
            "received_by_name": receiver, "notes": p.notes, "created_at": p.created_at,
        })

    return {
        "id": tab.id,
        "customer_id": tab.customer_id,
        "customer_name": customer_name,
        "customer_phone": customer_phone,
        "status": tab.status,
        "opened_at": tab.opened_at,
        "closed_at": tab.closed_at,
        "closed_by_id": tab.closed_by_id,
        "notes": tab.notes,
        "balance": balance,
        "total_charged": total_charged,
        "total_paid": total_paid,
        "entries": entries_out,
        "payments": payments_out,
    }


@router.get("/", response_model=List[TabOut])
def list_tabs(
    tab_status: Optional[str] = None,
    customer_id: Optional[int] = None,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    q = db.query(Tab)
    if tab_status:
        q = q.filter(Tab.status == tab_status)
    else:
        q = q.filter(Tab.status == "open")  # default: only open tabs
    if customer_id:
        q = q.filter(Tab.customer_id == customer_id)
    tabs = q.order_by(Tab.opened_at.desc()).all()
    result = [_tab_out(t) for t in tabs]
    # Sort open tabs by balance descending
    result.sort(key=lambda x: x["balance"], reverse=True)
    return result


@router.get("/{tab_id}", response_model=TabOut)
def get_tab(tab_id: int, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    tab = db.query(Tab).filter(Tab.id == tab_id).first()
    if not tab:
        raise HTTPException(status_code=404, detail="Tab not found")
    return _tab_out(tab)


@router.post("/", response_model=TabOut, status_code=status.HTTP_201_CREATED)
def open_tab(customer_id: int, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    customer = db.query(Customer).filter(Customer.id == customer_id, Customer.is_active == True).first()
    if not customer:
        raise HTTPException(status_code=404, detail="Customer not found")
    existing = db.query(Tab).filter(Tab.customer_id == customer_id, Tab.status == "open").first()
    if existing:
        raise HTTPException(status_code=400, detail="Customer already has an open tab")
    tab = Tab(customer_id=customer_id)
    db.add(tab)
    db.commit()
    db.refresh(tab)
    return _tab_out(tab)


@router.post("/{tab_id}/entries", response_model=TabOut)
def add_tab_entry(
    tab_id: int,
    order_id: Optional[int] = None,
    order_item_id: Optional[int] = None,
    amount: float = 0.0,
    description: Optional[str] = None,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    tab = db.query(Tab).filter(Tab.id == tab_id, Tab.status == "open").first()
    if not tab:
        raise HTTPException(status_code=404, detail="Open tab not found")
    if amount <= 0:
        raise HTTPException(status_code=400, detail="Amount must be positive")
    entry = TabEntry(
        tab_id=tab_id, order_id=order_id, order_item_id=order_item_id,
        amount=amount, description=description, created_by_id=user.id,
    )
    db.add(entry)
    db.commit()
    db.refresh(tab)
    return _tab_out(tab)


@router.post("/{tab_id}/pay", response_model=TabOut)
def pay_tab(
    tab_id: int,
    body: TabPayRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    tab = db.query(Tab).filter(Tab.id == tab_id, Tab.status == "open").first()
    if not tab:
        raise HTTPException(status_code=404, detail="Open tab not found")
    if body.amount <= 0:
        raise HTTPException(status_code=400, detail="Amount must be positive")
    total_charged = sum(e.amount for e in tab.entries)
    total_paid = sum(p.amount for p in tab.payments)
    balance = total_charged - total_paid
    if body.amount > balance + 0.005:
        raise HTTPException(status_code=400, detail=f"Payment exceeds balance ({balance:.2f})")
    payment = TabPayment(
        tab_id=tab_id, amount=body.amount,
        payment_method=body.payment_method, notes=body.notes,
        received_by_id=user.id,
    )
    db.add(payment)
    db.commit()
    db.refresh(tab)
    return _tab_out(tab)


@router.post("/{tab_id}/close", response_model=TabOut)
def close_tab(
    tab_id: int,
    body: TabCloseRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    tab = db.query(Tab).filter(Tab.id == tab_id, Tab.status == "open").first()
    if not tab:
        raise HTTPException(status_code=404, detail="Open tab not found")
    total_charged = sum(e.amount for e in tab.entries)
    total_paid = sum(p.amount for p in tab.payments)
    balance = round(total_charged - total_paid, 2)
    if balance > 0.005:
        raise HTTPException(status_code=400, detail=f"Tab still has a balance of €{balance:.2f} — pay it off first or use /forgive")
    tab.status = "closed"
    tab.closed_at = _utcnow()
    tab.closed_by_id = user.id
    if body.notes:
        tab.notes = body.notes
    db.commit()
    db.refresh(tab)
    return _tab_out(tab)


@router.post("/{tab_id}/forgive", response_model=TabOut)
def forgive_tab(
    tab_id: int,
    body: TabForgiveRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    tab = db.query(Tab).filter(Tab.id == tab_id, Tab.status == "open").first()
    if not tab:
        raise HTTPException(status_code=404, detail="Open tab not found")
    tab.status = "forgiven"
    tab.closed_at = _utcnow()
    tab.closed_by_id = user.id
    if body.reason:
        tab.notes = body.reason
    db.commit()
    db.refresh(tab)
    return _tab_out(tab)


# ── Customer tabs ──────────────────────────────────────────────────────────────

@router.get("/customer/{customer_id}", response_model=List[TabOut])
def customer_tabs(customer_id: int, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    tabs = db.query(Tab).filter(Tab.customer_id == customer_id).order_by(
        Tab.status.asc(),  # open first
        Tab.opened_at.desc(),
    ).all()
    return [_tab_out(t) for t in tabs]
