"""MCP stdio transport — plug an edge into any MCP host (Claude, IDE tools) in one line.

Stdlib only, like the rest of the edge: newline-delimited JSON-RPC 2.0 on stdin/stdout,
which is what the stdio transport is. No SDK, so connecting an edge costs a config entry
rather than a dependency tree.

    python3 -m neoedge.mcp.stdio --demo         # a testbed edge with one disputed trade
    python3 -m neoedge.mcp.stdio --book book.jsonl

Claude Desktop / Claude Code config:

    {"mcpServers": {"my-edge": {
        "command": "python3",
        "args": ["-m", "neoedge.mcp.stdio", "--demo"],
        "env": {"PYTHONPATH": "/path/to/neoedgex/src"}}}}

Then ask your own model: *"review my edge against these counterparty records"* — and the L0
rung stops being a hope. Everything is READ; there is no tool here that records, seals or
sends.

⚠️ stdout is the protocol. Anything printed there that is not a JSON-RPC message breaks the
session, so every diagnostic in this file goes to stderr. That is the one rule a stdio server
cannot bend.
"""
from __future__ import annotations

import argparse
import json
import sys

from .rpc import PROTOCOL, make_rpc_handler, serve_lines  # noqa: F401 — PROTOCOL re-exported


def _demo_edge():
    """A testbed edge carrying one trade with an injected SHORTFALL, plus the counterparty's
    records — so a host can call `edge_review` immediately and see a real disagreement rather
    than an empty book."""
    from ..testbed import build_network, run_trade

    network = build_network(2)
    buyer, seller = network[0], network[1]
    result = run_trade(buyer, seller, "HS-2026-001", fault="partial_delivery")
    buyer.edge.seal("2026-07")
    seller.edge.seal("2026-07")
    return buyer.edge, seller.edge.records(result.seller_relation)


class BookReader:
    """A read-only edge view over a book file — what `--book` serves.

    It quacks exactly as much of `NEOedge` as the read tools need: `member_id`, `book`,
    `sealed`, `records()`, `review_against()`. It has NO seal and NO disclosure — a book
    file alone carries no sealed snapshots, and pretending otherwise would let this
    surface promise proofs it cannot produce. `edge_disclose` on a book view fails
    loudly with that exact reason.
    """

    def __init__(self, book) -> None:
        if book.height == 0:
            raise ValueError("the book is empty — nothing to serve")
        self.book = book
        self.member_id = book.entries()[0].record["member_id"]
        self.sealed: list = []

    def records(self, relation=None) -> list[dict]:
        out = [entry.record for entry in self.book.entries()]
        if relation is not None:
            out = [r for r in out if r["relation_ref"] == relation.ref]
        return out

    def review_against(self, counterparty_records: list[dict], relation):
        from ..matching import review

        return review(self.records(relation), counterparty_records)

    def disclose(self, event_key: str):
        raise KeyError(
            "a book-only view holds no sealed snapshots, so there is no proof to hand "
            "over — disclosure needs the edge that sealed the period"
        )


def _load_edge(path: str):
    from ..book import Book

    probe = json.loads(open(path, encoding="utf-8").readline())
    member_id = probe["record"]["member_id"]
    return BookReader(Book(member_id, path))


def serve(edge, counterparty_hint: list[dict] | None = None) -> int:
    """The stdio wire: stdin lines in, stdout lines out. The handler — and therefore
    every refusal — is shared with the TCP wire (`rpc.py`), so the two transports
    cannot drift into two behaviours."""

    def write_line(text: str) -> None:
        sys.stdout.write(text + "\n")
        sys.stdout.flush()

    return serve_lines(sys.stdin, write_line, make_rpc_handler(edge, counterparty_hint))


def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(prog="python3 -m neoedge.mcp.stdio")
    ap.add_argument("--demo", action="store_true", help="serve a testbed edge with one dispute")
    ap.add_argument("--book", help="serve a live book file, read-only")
    args = ap.parse_args(argv)

    if args.book:
        reader = _load_edge(args.book)
        print(f"neoedge MCP: serving book of {reader.member_id} · "
              f"{reader.book.height} records (read-only, no seals)", file=sys.stderr)
        return serve(reader)
    if not args.demo:
        print("need --demo or --book", file=sys.stderr)
        return 2
    edge, counterparty = _demo_edge()
    print(f"neoedge MCP: serving {edge.member_id} · {edge.book.height} records "
          f"· {len(edge.sealed)} sealed period(s)", file=sys.stderr)
    return serve(edge, counterparty)


if __name__ == "__main__":
    sys.exit(main())
