"""The PlusROI paste block (invoices.md B49–B57) — the byte-format IS
the contract: one line per (client × project) breakdown row, 7 columns
joined with tabs, lines joined with `\\n`, empty string for an empty
breakdown. Column layout (user direction 2026-05-25):

  1. ``{client} : {project}``  — colon WITH spaces (ADR #029 vocabulary);
                                 NULLs render the literals
                                 ``(no client)`` / ``(no project)``.
  2. team label                — the destination sheet's vendor string.
  3. ``{amount}``              — 2dp, no currency symbol, no separators.
  4. ``{invoice_date}``        — YYYY-MM-DD.
  5. *(empty)*                 — intentional; destination column is blank.
  6. operator label            — the operator string.
  7. category label            — the category string.

The three business literals ('Bowden Works Team' / 'Bowden Works' /
'Labour') are TENANT SETTINGS (04 "no hard-coded business literals"),
read from ``tenants.settings["paste_block"]``; the defaults below are
the Bowden Works tenant's values, so R1 renders byte-identically to
the legacy formatPasteBlock without a settings write. Amounts are the
per-row ENTRY sums — manual_total never reaches the paste block (B51).

Cross-validated byte-for-byte against the legacy
/srv/apps/work/lib/invoices.ts via scripts/ts_paste_block.mjs
(tests/invoices/test_paste_block_gate.py — the M6 gate).
"""

from __future__ import annotations

import uuid
from dataclasses import dataclass
from decimal import Decimal

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.models import Tenant
from app.services.money.invoices import (
    _get_invoice,
    default_invoice_date,
    invoice_breakdown,
)

NO_CLIENT = "(no client)"
NO_PROJECT = "(no project)"


@dataclass(frozen=True)
class PasteBlockConfig:
    """Per-tenant column literals. Defaults = the Bowden Works values."""

    team_label: str = "Bowden Works Team"
    operator_label: str = "Bowden Works"
    category_label: str = "Labour"


def paste_block_config(tenant_settings: dict | None) -> PasteBlockConfig:
    raw = (tenant_settings or {}).get("paste_block", {})
    defaults = PasteBlockConfig()
    return PasteBlockConfig(
        team_label=raw.get("team_label", defaults.team_label),
        operator_label=raw.get("operator_label", defaults.operator_label),
        category_label=raw.get("category_label", defaults.category_label),
    )


def _amount_2dp(raw: str | dict | Decimal | None) -> str:
    """`toFixed(2)`-equivalent: exactly two decimals, no symbol. The
    inputs are exact 2dp decimal strings (Money amounts), so this is
    formatting, not rounding."""
    if raw is None:
        value = Decimal(0)
    elif isinstance(raw, dict):  # a Money dict from the breakdown
        value = Decimal(raw["amount"])
    else:
        value = Decimal(str(raw))
    return f"{value:.2f}"


def format_paste_block(
    breakdown: list[dict], invoice_date: str, config: PasteBlockConfig | None = None
) -> str:
    """Render breakdown rows in the CALLER's order (no sort here —
    T-INV-P-004; the service breakdown is already B44-sorted). Each row
    needs `client`, `project`, and a billout amount under
    `summed_billout` (Money dict) or `billout_amount` (string)."""
    cfg = config or PasteBlockConfig()
    lines = []
    for row in breakdown:
        client = row.get("client") or NO_CLIENT
        project = row.get("project") or NO_PROJECT
        amount = _amount_2dp(row.get("summed_billout", row.get("billout_amount")))
        lines.append(
            "\t".join(
                [
                    f"{client} : {project}",
                    cfg.team_label,
                    amount,
                    invoice_date,
                    "",
                    cfg.operator_label,
                    cfg.category_label,
                ]
            )
        )
    return "\n".join(lines)


def paste_block_for_invoice(session: Session, actor, invoice_id: uuid.UUID) -> dict:
    """The API payload: text + line count. A NULL-dated invoice uses
    today's last-day-of-previous-month default as column 4 (B49)."""
    invoice = _get_invoice(session, actor, invoice_id)
    breakdown = invoice_breakdown(session, actor, invoice_id)
    date_text = (invoice.invoice_date or default_invoice_date()).isoformat()
    settings = session.scalar(
        select(Tenant.settings).where(Tenant.id == actor.tenant_id)
    )
    text = format_paste_block(breakdown, date_text, paste_block_config(settings))
    return {"text": text, "line_count": len(breakdown), "invoice_date": date_text}
