"""/api/plus-projects/{id}/generate-invoice + /api/plus-invoices — the
PlusROI invoice generation + lifecycle surface (phase-3 slice 2b). Thin
HTTP layer over ``services/money/invoices_plus.py`` (owner gate,
sheet-owned write-block, tenant scope, transition guards, realized-FX
rules, audit all live in the service). Distinct from ``/api/invoices``
(the Bowden Works time-invoice CRUD) — additive; that surface is
untouched."""

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 invoices_plus as 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 GenerateBody(BaseModel):
    month: str = ""


class GenerateBatchBody(BaseModel):
    month: str = ""
    timing: str = ""


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

    status: str | None = None
    external_id: str | None = None
    amount: str | None = None
    collected_amount: str | None = None
    collected_currency: str | None = None


@router.post("/api/plus-projects/{project_id}/generate-invoice")
def generate_invoice(
    session: SessionDep,
    actor: ActorDep,
    project_id: uuid.UUID,
    body: GenerateBody,
) -> dict:
    return _run(
        svc.generate_for_project, session, actor, project_id, body.model_dump()
    )


@router.post("/api/plus-invoices/generate-batch")
def generate_batch(
    session: SessionDep, actor: ActorDep, body: GenerateBatchBody
) -> dict:
    return _run(svc.generate_batch, session, actor, body.model_dump())


@router.patch("/api/plus-invoices/{invoice_id}")
def update_invoice(
    session: SessionDep,
    actor: ActorDep,
    invoice_id: uuid.UUID,
    body: PlusInvoicePatch,
) -> dict:
    patch = {k: getattr(body, k) for k in body.model_fields_set}
    return _run(svc.update_plus_invoice, session, actor, invoice_id, patch)
