"""/api/projects — the per-(operator, client, project) rollup
(projects-and-entities.md parity, FK grouping) + the lazy
per-description expansion. Income/margin/billout exist in the payload
only for can_see_income holders (T-PRJ-023 at the data layer)."""

from __future__ import annotations

import uuid

from fastapi import APIRouter
from pydantic import BaseModel

from app.routers.deps import (
    ActorDep,
    ApiError,
    FiltersDep,
    SessionDep,
    contain_view_as,
    tenant_currency,
)
from app.services import projects_admin
from app.services.errors import ServiceError
from app.services.reporting import projects as projects_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


@router.get("/api/projects")
def project_rollup(session: SessionDep, actor: ActorDep, f: FiltersDep) -> dict:
    f = contain_view_as(actor, f)
    return projects_svc.project_summary(
        session, actor, f, zero_currency=tenant_currency(actor)
    )


@router.get("/api/projects/{group}/descriptions")
def project_descriptions(
    group: str,
    session: SessionDep,
    actor: ActorDep,
    f: FiltersDep,
    raw_operator: str | None = None,
    raw_client: str | None = None,
    raw_project: str | None = None,
) -> dict:
    """Expand one rollup group. `group` is a project id, or the literal
    `unassigned` with the raw_* pin params (absent = NULL — distinct
    names fix the legacy __null__ collision, B20)."""
    f = contain_view_as(actor, f)
    if group == "unassigned":
        project_id = None
    else:
        try:
            project_id = uuid.UUID(group)
        except ValueError as exc:
            raise ApiError(
                422,
                "BAD_GROUP",
                "Unknown project group",
                f"{group!r} is neither a project id nor 'unassigned'.",
            ) from exc
    rows = projects_svc.entries_by_description(
        session,
        actor,
        f,
        project_id=project_id,
        raw_operator=raw_operator,
        raw_client=raw_client,
        raw_project=raw_project,
        zero_currency=tenant_currency(actor),
    )
    return {"rows": rows}


class FinancialsBody(BaseModel):
    """Owner-only project financials. Each field is optional; an omitted or
    blank field CLEARS its column (parity: one save writes all three). pct
    and amount are independent (they COMBINE at read time — not XOR)."""

    income: str | None = None
    billout_adjustment_pct: str | None = None
    billout_adjustment_amount: str | None = None
    currency: str | None = None


@router.patch("/api/projects/{project_id}/financials")
def update_financials(
    project_id: uuid.UUID,
    session: SessionDep,
    actor: ActorDep,
    body: FinancialsBody,
) -> dict:
    """Set a project's income + display-only billout adjustments. Owner-only
    (can_transfer), audited, re-stamps nothing (ADR #040)."""
    return _run(
        projects_admin.update_project_financials,
        session,
        actor,
        project_id,
        body.model_dump(),
    )
