"""StructuredSourceAgent — company A's own agent, answering our consultant.

The owner's design: a company-side agent that is a **source of structured information about
the company**, feeding our consultant agent so the consultant can learn how to set this system
up for them and how to structure their processes for automation.

Everything below is derived from the edge's own book. There is no ERP connection, no document
access and no credential — so the honest answer to *"what did you give them?"* is: the shape
of how this company trades, and nothing that names a price or reproduces a document.

The refusals are the interesting part of this file. An agent that will answer anything is not
a source, it is a leak.
"""

from __future__ import annotations

from collections import Counter
from typing import Any

from .base import EdgeAgent


class StructuredSourceAgent(EdgeAgent):
    name = "structured-source"
    description = ("Answers how THIS company actually trades — counterparties, cadence, "
                   "record kinds, recurring disagreement classes — so a consultant can "
                   "propose automation without seeing a single document.")

    # Questions we will not answer, with the reason attached rather than a silent empty result.
    REFUSED = {
        "amounts": "amounts are disclosed as bands by design; a figure is not ours to give",
        "documents": "documents never leave the company — the edge seals roots, not content",
        "counterparty_books": "this agent answers for its own edge only; the other side's "
                              "book is the other side's to disclose",
        "credentials": "no agent on this seam holds or forwards a credential",
    }

    def capabilities(self) -> list[dict]:
        return [
            {"question": "process_shape",
             "returns": "record kinds in use, how many relations, sealed periods — the "
                        "skeleton a consultant needs to propose automation"},
            {"question": "counterparty_profile",
             "returns": "per counterparty: role, how many events, which record kinds "
                        "(no amounts, no documents)"},
            {"question": "friction",
             "returns": "which disagreement CLASSES recur (SHORTFALL / DIFFERS / ABSENT) — "
                        "where this company loses time every week"},
            {"question": "automation_candidates",
             "returns": "steps that are repetitive and already machine-checkable, ranked by "
                        "how often they occur"},
        ]

    def ask(self, question: str, **kwargs: Any) -> dict:
        for topic, why in self.REFUSED.items():
            if topic in question.lower():
                return self._refuse(why)
        handler = {
            "process_shape": self._process_shape,
            "counterparty_profile": self._counterparty_profile,
            "friction": self._friction,
            "automation_candidates": self._automation_candidates,
        }.get(question)
        if handler is None:
            return self._refuse(f"not a question this agent answers; try "
                                f"{[c['question'] for c in self.capabilities()]}")
        return {"answered": True, "agent": self.name, "question": question,
                "answer": handler(**kwargs)}

    # ── answers ──────────────────────────────────────────────────────────────────────────

    def _records(self) -> list[dict]:
        return self.edge.records()

    def _process_shape(self) -> dict:
        records = self._records()
        kinds = Counter(r.get("doc_kind") for r in records if r.get("doc_kind"))
        relations = {r.get("relation_ref") for r in records if r.get("relation_ref")}
        sealed = list(getattr(self.edge, "sealed", []))
        return {
            "record_kinds_in_use": dict(kinds),
            "relations": len(relations),
            "book_height": self.edge.book.height,
            "sealed_periods": len(sealed),
            "reading": ("Deliveries flow but no INVOICE reaches the edge yet — usually the "
                        "first automation gap worth closing."
                        if kinds.get("DELIVERY_NOTE") and not kinds.get("INVOICE") else
                        "Orders, deliveries and invoices all flow — the loop is closed and "
                        "the next gain is in cadence, not coverage."
                        if kinds.get("INVOICE") else
                        "The edge sees only the top of the chain so far — orders without "
                        "the goods and money legs that make reconciliation worth automating."),
        }

    def _counterparty_profile(self) -> list[dict]:
        by_cp: dict[str, Counter] = {}
        roles: dict[str, str] = {}
        for record in self._records():
            cp = record.get("counterparty_id")
            if not cp:
                continue
            by_cp.setdefault(cp, Counter())[record.get("doc_kind", "?")] += 1
            roles.setdefault(cp, record.get("role", "?"))
        return [{"counterparty": cp, "role": roles.get(cp), "events": sum(kinds.values()),
                 "kinds": dict(kinds)} for cp, kinds in by_cp.items()]

    def _friction(self, reports: list | None = None) -> dict:
        """Needs the reports this edge has already produced — friction is a fact about
        history, and this agent will not manufacture one from a single snapshot."""
        if not reports:
            return {"classes": {}, "note": "no reports supplied — friction is measured over "
                                           "past reviews, and inventing it from one book "
                                           "would be a guess wearing a number"}
        classes = Counter(str(getattr(d, "status", d)) for report in reports
                          for d in getattr(report, "disagreements", []))
        return {"classes": dict(classes),
                "reading": "Recurring SHORTFALL points at picking, not billing; recurring "
                           "DIFFERS on the same field points at two systems disagreeing on "
                           "one definition; recurring ABSENT points at a step nobody owns."}

    def _automation_candidates(self) -> list[dict]:
        shape = self._process_shape()
        kinds = shape["record_kinds_in_use"]
        out = []
        if kinds.get("DELIVERY_NOTE", 0) and not kinds.get("INVOICE", 0):
            out.append({"candidate": "invoice capture from the ERP",
                        "why": "deliveries are recorded and invoices are not — the edge sees "
                               "half the loop, so nothing can be reconciled automatically"})
        if shape["relations"] and shape["sealed_periods"] == 0:
            out.append({"candidate": "period sealing on a schedule",
                        "why": "relations are live but nothing is sealed; without a seal "
                               "there is no root to attest and no disclosure to offer"})
        if shape["relations"] > 3:
            out.append({"candidate": "counterparty onboarding as a template",
                        "why": f"{shape['relations']} relations already exist; the handshake "
                               f"step repeats and is fully machine-checkable"})
        return out or [{"candidate": "none obvious",
                        "why": "the edge does not yet hold enough history to rank anything "
                               "honestly"}]
