#!/usr/bin/env python3
"""Mock parallel agent scenario — multiple agents emitting prompts concurrently.

In real overnight-loop usage, an orchestrator spawns several AI agents.  Each
agent works independently but all I/O flows through the *same* terminal PTY.
Prompts from different agents appear interleaved in the stream.

Real agents serialize their stdin reads (they are separate processes with their
own stdin pipes, not sharing a single fd).  This mock simulates that correctly:
threads represent independent agents but stdin reads are serialized through a
shared lock so that one auto-yes injection is consumed by exactly one agent.

Usage:
    python mock_parallel_agents.py [--agents N] [--ops-per-agent M]

Exit codes:
    0  all prompts received correct responses
    1  wrong response or timeout
"""

import sys
import threading
import time

TIMEOUT = 10  # seconds per prompt


def _read_line(timeout=TIMEOUT):
    """Read one response from stdin with timeout."""
    import select

    ready, _, _ = select.select([sys.stdin], [], [], timeout)
    if not ready:
        return None
    line = sys.stdin.readline()
    return None if not line else line.rstrip("\r\n")


class AgentThread(threading.Thread):
    """One simulated AI agent: prints output, asks for approvals N times."""

    def __init__(self, agent_id: int, n_ops: int, stdin_lock: threading.Lock):
        super().__init__(daemon=True)
        self.agent_id = agent_id
        self.n_ops = n_ops
        self._lock = stdin_lock
        self.success = True

    def run(self):
        for op in range(1, self.n_ops + 1):
            # Emit "working" output (not protected — interleaving is fine here)
            sys.stdout.write(
                f"[agent-{self.agent_id}] op {op}/{self.n_ops}: processing...\n"
            )
            sys.stdout.flush()
            time.sleep(0.02)

            # Show approval prompt and read response — stdin must be serialized
            with self._lock:
                sys.stdout.write(
                    f"[agent-{self.agent_id}]"
                    f" │  → Write output-{self.agent_id}-{op}.txt (y)  │\n"
                )
                sys.stdout.flush()

                resp = _read_line()
                if resp not in ("", "y", "Y"):
                    sys.stdout.write(
                        f"[agent-{self.agent_id}] FAIL op {op}: got {resp!r}\n"
                    )
                    sys.stdout.flush()
                    self.success = False

            time.sleep(0.02)
            sys.stdout.write(f"[agent-{self.agent_id}] op {op} done.\n")
            sys.stdout.flush()


def main():
    import argparse

    p = argparse.ArgumentParser(description="Parallel agents mock")
    p.add_argument("--agents", type=int, default=3, help="number of agents")
    p.add_argument("--ops-per-agent", type=int, default=2, help="operations per agent")
    args = p.parse_args()

    n_agents = args.agents
    n_ops = args.ops_per_agent

    sys.stdout.write(
        f"\n[coordinator] Starting {n_agents} parallel agents "
        f"({n_ops} ops each, {n_agents * n_ops} total approvals)...\n\n"
    )
    sys.stdout.flush()

    # Shared stdin lock ensures only one agent reads a response at a time
    stdin_lock = threading.Lock()

    threads = [AgentThread(i + 1, n_ops, stdin_lock) for i in range(n_agents)]
    for t in threads:
        t.start()
    for t in threads:
        t.join(timeout=TIMEOUT * n_ops + 10)

    all_ok = all(t.success for t in threads)

    if all_ok:
        sys.stdout.write(
            f"\n[coordinator] All {n_agents} agents completed successfully.\n"
            f"[coordinator] Total approvals processed: {n_agents * n_ops}\n"
        )
        sys.stdout.flush()
        sys.exit(0)
    else:
        sys.stdout.write("[coordinator] Some agents failed.\n")
        sys.stdout.flush()
        sys.exit(1)


if __name__ == "__main__":
    main()
