"""/api/expenses — the PlusROI Expenses write form (phase-3 slice 3).
Thin HTTP layer over ``services/money/expenses.py`` (owner gate,
sheet-owned write-block, tenant scope, validation, audit all live in the
service). The LIST stays ``/api/books/expenses`` — this surface is the
writes + the form-options read."""

from __future__ import annotations

import uuid

from fastapi import APIRouter
from pydantic import BaseModel

from app.routers.deps import ActorDep, ApiError, SessionDep
from app.services.errors import ServiceError
from app.services.money import expenses as expenses_svc

router = APIRouter()


def _run(fn, *args, **kwargs):
    try:
        return fn(*args, **kwargs)
    except ServiceError as exc:
        raise ApiError(exc.status, exc.code, exc.summary, exc.detail) from exc


class ExpenseCreate(BaseModel):
    vendor_party_id: str | None = None
    project_id: str | None = None
    amount: str | None = None
    currency: str | None = None
    category: str | None = None
    paid_by_party_id: str | None = None
    expense_date: str | None = None
    settlement_month: str | None = None
    notes: str | None = None


class ExpenseBatchLine(BaseModel):
    project_id: str | None = None
    amount: str | None = None
    category: str | None = None
    notes: str | None = None


class ExpenseBatchCreate(BaseModel):
    vendor_party_id: str | None = None
    expense_date: str | None = None
    currency: str | None = None
    paid_by_party_id: str | None = None
    settlement_month: str | None = None
    lines: list[ExpenseBatchLine] = []


class ExpensePatch(BaseModel):
    """PATCH semantics: only the SET fields reach the service."""

    vendor_party_id: str | None = None
    project_id: str | None = None
    amount: str | None = None
    currency: str | None = None
    category: str | None = None
    paid_by_party_id: str | None = None
    expense_date: str | None = None
    settlement_month: str | None = None
    notes: str | None = None


@router.get("/api/expenses/options")
def expense_options(session: SessionDep, actor: ActorDep) -> dict:
    return _run(expenses_svc.expense_options, session, actor)


@router.post("/api/expenses")
def create_expense(session: SessionDep, actor: ActorDep, body: ExpenseCreate) -> dict:
    return _run(expenses_svc.create_expense, session, actor, body.model_dump())


@router.post("/api/expenses/batch")
def create_expense_batch(
    session: SessionDep, actor: ActorDep, body: ExpenseBatchCreate
) -> dict:
    payload = body.model_dump()
    payload["lines"] = [line.model_dump() for line in body.lines]
    return _run(expenses_svc.create_expense_batch, session, actor, payload)


@router.patch("/api/expenses/{expense_id}")
def update_expense(
    session: SessionDep, actor: ActorDep, expense_id: uuid.UUID, body: ExpensePatch
) -> dict:
    patch = {k: getattr(body, k) for k in body.model_fields_set}
    return _run(expenses_svc.update_expense, session, actor, expense_id, patch)


@router.delete("/api/expenses/{expense_id}")
def delete_expense(
    session: SessionDep, actor: ActorDep, expense_id: uuid.UUID
) -> dict:
    return _run(expenses_svc.delete_expense, session, actor, expense_id)
