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

from database import get_db
from models.expenses import Contact, Expense, ExpensePayment
from models.user import User
from schemas.expenses import (
    ContactCreate, ContactUpdate, ContactOut,
    ExpenseCreate, ExpenseUpdate, ExpenseOut,
    ExpensePaymentCreate, ExpensePaymentOut,
)
from routers.deps import require_manager

contacts_router = APIRouter()
expenses_router = APIRouter()


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


def _expense_status(total: float, paid: float) -> str:
    if paid >= total:
        return "paid"
    if paid > 0:
        return "partial"
    return "due"


def _expense_out(e: Expense) -> dict:
    contact_name = None
    if e.contact:
        contact_name = e.contact.name
    creator_name = None
    if e.created_by:
        creator_name = e.created_by.full_name or e.created_by.username
    due_amount = round(max(0.0, e.total_amount - e.paid_amount), 2)
    payments_out = []
    for p in e.payments:
        payer_name = None
        if p.paid_by:
            payer_name = p.paid_by.full_name or p.paid_by.username
        payments_out.append({
            "id": p.id,
            "expense_id": p.expense_id,
            "amount": p.amount,
            "paid_at": p.paid_at,
            "paid_by_id": p.paid_by_id,
            "paid_by_name": payer_name,
            "notes": p.notes,
        })
    return {
        "id": e.id,
        "description": e.description,
        "category": e.category,
        "contact_id": e.contact_id,
        "contact_name": contact_name,
        "total_amount": e.total_amount,
        "paid_amount": e.paid_amount,
        "due_amount": due_amount,
        "status": _expense_status(e.total_amount, e.paid_amount),
        "due_date": e.due_date,
        "business_day_id": e.business_day_id,
        "created_by_id": e.created_by_id,
        "created_by_name": creator_name,
        "created_at": e.created_at,
        "notes": e.notes,
        "payments": payments_out,
    }


# ── Contacts ──────────────────────────────────────────────────────────────────

@contacts_router.get("/", response_model=List[ContactOut])
def list_contacts(
    include_inactive: bool = False,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    q = db.query(Contact)
    if not include_inactive:
        q = q.filter(Contact.is_active == True)
    return q.order_by(Contact.name).all()


@contacts_router.post("/", response_model=ContactOut, status_code=status.HTTP_201_CREATED)
def create_contact(body: ContactCreate, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    contact = Contact(**body.model_dump())
    db.add(contact)
    db.commit()
    db.refresh(contact)
    return contact


@contacts_router.put("/{contact_id}", response_model=ContactOut)
def update_contact(contact_id: int, body: ContactUpdate, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    contact = db.query(Contact).filter(Contact.id == contact_id).first()
    if not contact:
        raise HTTPException(status_code=404, detail="Contact not found")
    for field, value in body.model_dump(exclude_none=True).items():
        setattr(contact, field, value)
    db.commit()
    db.refresh(contact)
    return contact


@contacts_router.delete("/{contact_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_contact(contact_id: int, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    contact = db.query(Contact).filter(Contact.id == contact_id).first()
    if not contact:
        raise HTTPException(status_code=404, detail="Contact not found")
    contact.is_active = False
    db.commit()


# ── Expenses ──────────────────────────────────────────────────────────────────

@expenses_router.get("/summary")
def expense_summary(
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    expenses = db.query(Expense).all()
    total_due = sum(max(0.0, e.total_amount - e.paid_amount) for e in expenses if e.paid_amount < e.total_amount)
    total_paid = sum(e.paid_amount for e in expenses)
    by_category: dict = {}
    for e in expenses:
        c = e.category
        if c not in by_category:
            by_category[c] = {"category": c, "total": 0.0, "paid": 0.0, "due": 0.0}
        by_category[c]["total"] += e.total_amount
        by_category[c]["paid"] += e.paid_amount
        by_category[c]["due"] += max(0.0, e.total_amount - e.paid_amount)
    return {
        "total_due": round(total_due, 2),
        "total_paid": round(total_paid, 2),
        "by_category": list(by_category.values()),
    }


@expenses_router.get("/", response_model=List[ExpenseOut])
def list_expenses(
    expense_status: Optional[str] = Query(default=None, alias="status"),
    category: Optional[str] = None,
    contact_id: Optional[int] = None,
    business_day_id: Optional[int] = None,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    expenses = db.query(Expense).order_by(Expense.created_at.desc()).all()
    result = [_expense_out(e) for e in expenses]
    if expense_status:
        result = [e for e in result if e["status"] == expense_status]
    if category:
        result = [e for e in result if e["category"] == category]
    if contact_id:
        result = [e for e in result if e["contact_id"] == contact_id]
    if business_day_id:
        result = [e for e in result if e["business_day_id"] == business_day_id]
    return result


@expenses_router.post("/", response_model=ExpenseOut, status_code=status.HTTP_201_CREATED)
def create_expense(body: ExpenseCreate, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    expense = Expense(**body.model_dump(), created_by_id=user.id)
    db.add(expense)
    db.commit()
    db.refresh(expense)
    return _expense_out(expense)


@expenses_router.put("/{expense_id}", response_model=ExpenseOut)
def update_expense(expense_id: int, body: ExpenseUpdate, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    expense = db.query(Expense).filter(Expense.id == expense_id).first()
    if not expense:
        raise HTTPException(status_code=404, detail="Expense not found")
    for field, value in body.model_dump(exclude_none=True).items():
        setattr(expense, field, value)
    db.commit()
    db.refresh(expense)
    return _expense_out(expense)


@expenses_router.delete("/{expense_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_expense(expense_id: int, db: Session = Depends(get_db), user: User = Depends(require_manager)):
    expense = db.query(Expense).filter(Expense.id == expense_id).first()
    if not expense:
        raise HTTPException(status_code=404, detail="Expense not found")
    db.delete(expense)
    db.commit()


@expenses_router.post("/{expense_id}/pay", response_model=ExpenseOut)
def record_payment(
    expense_id: int,
    body: ExpensePaymentCreate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    expense = db.query(Expense).filter(Expense.id == expense_id).first()
    if not expense:
        raise HTTPException(status_code=404, detail="Expense not found")
    if body.amount <= 0:
        raise HTTPException(status_code=400, detail="Payment amount must be positive")
    payment = ExpensePayment(
        expense_id=expense_id,
        amount=body.amount,
        paid_by_id=user.id,
        notes=body.notes,
    )
    db.add(payment)
    db.flush()
    # recompute paid_amount from all payments (never trust client totals)
    all_payments = db.query(ExpensePayment).filter(ExpensePayment.expense_id == expense_id).all()
    expense.paid_amount = round(sum(p.amount for p in all_payments), 2)
    db.commit()
    db.refresh(expense)
    return _expense_out(expense)
