"""Strands tools exposed to the adjudicator agents.

Both tools are **read-only** — they wrap the pure matching helpers. There is no
writer tool: agents may inspect candidates and variance but never mutate state.
``strands`` is imported lazily so this module is importable without it (the raw
callables remain usable directly, e.g. in tests).
"""

from __future__ import annotations

from datetime import date
from typing import Any, Callable

from app.matching.candidates import get_candidates as _get_candidates
from app.matching.models import Invoice, PlannedLine
from app.matching.variance import variance as _variance


def _invoice_from_payload(raw: dict[str, Any]) -> Invoice:
    return Invoice.from_dict(raw)


def candidates_for(
    client_name: str,
    planned_amount: float,
    invoices: list[dict[str, Any]],
    odoo_customer_id: int | None = None,
) -> list[dict[str, Any]]:
    """Read-only tool: rank invoice candidates for a planned line.

    Args:
        client_name: Planning-sheet client name.
        planned_amount: Planned ex-VAT amount for the month.
        invoices: Invoice payloads (shape of ``invoices_sample.json``).
        odoo_customer_id: Resolved Odoo customer id, if known.

    Returns:
        Ranked candidate dicts (best first), each with amount_delta, date
        proximity, description similarity and score.
    """
    line = PlannedLine(
        source_row=0,
        client_name=client_name,
        planned_amount=planned_amount,
        odoo_customer_id=odoo_customer_id,
    )
    invoice_objs = [_invoice_from_payload(i) for i in invoices]
    return [c.to_dict() for c in _get_candidates(line, invoice_objs)]


def variance_between(planned: float, invoiced: float, rounding: int = 2) -> float:
    """Read-only tool: ``round(planned - invoiced, rounding)`` (ex-VAT)."""
    return _variance(planned, invoiced, rounding)


def build_strands_tools() -> list[Callable[..., Any]]:
    """Wrap the read-only callables as Strands ``@tool`` objects (lazy import).

    Returns the decorated tools if ``strands`` is available, otherwise the plain
    callables so the caller still has something usable.
    """
    try:
        from strands import tool
    except Exception:  # pragma: no cover - exercised only without strands
        return [candidates_for, variance_between]

    return [tool(candidates_for), tool(variance_between)]
