"""The agent container's entrypoint — connect to the core's MCP wire, ask, answer.

    python3 -m neoedge.agents.runner --endpoint edge:9137 --agents structured-source

This process is what runs in the SEPARATE agent container of the compose topology. Its
whole world is one TCP endpoint: no book volume, no keys, no egress network. The
connect-retry loop lives here and not in any core module on purpose — the core models
the ideal path; waiting out a neighbour's startup is the organ's job (containers race,
and `--wait` is the honest answer to that race).

Output is JSON lines on stdout — one per (agent, question) — because the consumer of an
agent's answers is a machine (our consultant, a pipeline, the owner's jq), not a log
reader. Diagnostics go to stderr.
"""

from __future__ import annotations

import argparse
import json
import sys
import time

from ..mcp.tcp import MCPClient
from .base import AgentContext
from .remote import RemoteEdge
from .roster import AgentRegistry

DEFAULT_QUESTIONS = ("process_shape", "counterparty_profile", "automation_candidates")


def connect(host: str, port: int, wait_s: float) -> MCPClient:
    """Retry until the core answers or the wait is spent. Bounded, dumb, and out here in
    the organ where retries belong."""
    deadline = time.monotonic() + wait_s
    last: Exception | None = None
    while True:
        try:
            return MCPClient(host, port)
        except OSError as error:
            last = error
            if time.monotonic() >= deadline:
                raise ConnectionError(
                    f"the core at {host}:{port} did not answer within {wait_s:.0f}s: {last}"
                ) from last
            time.sleep(0.5)


def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(prog="python3 -m neoedge.agents.runner")
    ap.add_argument("--endpoint", required=True, help="host:port of the core's MCP wire")
    ap.add_argument("--agents", default="structured-source", help="comma list from the roster")
    ap.add_argument("--questions", default=",".join(DEFAULT_QUESTIONS))
    ap.add_argument("--wait", type=float, default=30.0,
                    help="seconds to wait for the core to come up (containers race)")
    args = ap.parse_args(argv)

    host, _, port = args.endpoint.rpartition(":")
    client = connect(host or "127.0.0.1", int(port), args.wait)
    edge = RemoteEdge(client)
    print(f"runner: connected to {edge.member_id} · book {edge.book.height} · "
          f"{len(edge.sealed)} sealed period(s)", file=sys.stderr)

    registry = AgentRegistry()
    context = AgentContext(edge=edge)
    failures = 0
    for name in [a.strip() for a in args.agents.split(",") if a.strip()]:
        try:
            agent = registry.start(name, context)
        except Exception as error:  # noqa: BLE001 — one bad name must not silence the rest
            print(f"runner: agent {name!r}: {error}", file=sys.stderr)
            failures += 1
            continue
        for question in [q.strip() for q in args.questions.split(",") if q.strip()]:
            answer = agent.ask(question)
            print(json.dumps({"agent": agent.name, "question": question, **answer},
                             default=str), flush=True)

    client.close()
    return 1 if failures else 0


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