#!/usr/bin/env python3
"""Parameterizable fake `claude` binary for SessionRunner tests.

Behaviour is selected via env vars so tests can drive each branch:
  FAKE_CLAUDE_MODE = ready | no_marker | crash | trust_error | slow | stall
                     | resume | stderr_error                              (default ready)
  - resume:       like ready but omits "Created initial session" (a reconnecting
                  bridge doesn't re-log it) -> exercises pointer session backfill
  - stderr_error: writes a failure reason to STDERR (not --debug-file) and exits 1
                  -> exercises clauster's stderr capture into error_detail
  FAKE_CLAUDE_SLOW = seconds to delay before writing markers (mode=slow)
  FAKE_CLAUDE_AGENTS = JSON string returned by `agents --json`        (default [])

`remote-control` writes the real bridge-log marker sequence to --debug-file,
then (mode=ready) idles until SIGINT, on which it logs the shutdown marker and
exits 0 — mimicking the empirically-confirmed clean SIGINT path.
"""
import os
import signal
import sys
import time

ENV_ID = "env_01TESTENVAAAAAAAAAAAAAAAA"
STARTER = "session_01TESTSTARTERAAAAAAAAAA"
BRIDGE_ID = "11111111-2222-3333-4444-555555555555"
# Flag-form (`claude --remote-control`) connect URL session id (pty mode).
PTY_SESSION = "session_01TESTPTYSESSIONAAAAA"


def _idle_until_signal():
    stopped = {"v": False}

    def handler(_signum, _frame):
        stopped["v"] = True

    signal.signal(signal.SIGINT, handler)
    signal.signal(signal.SIGTERM, handler)
    if hasattr(signal, "SIGBREAK"):
        signal.signal(signal.SIGBREAK, handler)
    for _ in range(2400):  # idle up to ~600s waiting for a signal
        if stopped["v"]:
            break
        time.sleep(0.25)
    return 0


def run_flag_bridge(args):
    """The `claude --remote-control <name> [--continue]` flag form (pty mode).

    Single interactive session: prints its connect URL to STDOUT (the PTY keeper
    scrapes it off the master) then idles until SIGINT — unlike the subcommand,
    it writes no env-register markers to --debug-file.
    """
    debug_file = _arg(args, "--debug-file")
    mode = os.environ.get("FAKE_CLAUDE_MODE", "ready")
    if debug_file:
        import json

        with open(debug_file + ".argv.json", "w") as fh:
            json.dump(args, fh)
    if mode == "crash":
        return 1
    if mode == "pty_no_url":
        # Alive but never prints a connect URL -> exercises the STARTING/ERROR path.
        return _idle_until_signal()
    if mode == "pty_slow":
        # Print the URL only after a delay so the bridge is STARTING past the
        # synchronous readiness wait -> the background startup-watch promotes it.
        time.sleep(float(os.environ.get("FAKE_CLAUDE_SLOW", "0.8")))
    # Mimic the real TUI line: "Continue here, on your phone, or at <url>".
    sys.stdout.write(f"\r\n/remote-control is active · Continue at https://claude.ai/code/{PTY_SESSION}\r\n")
    sys.stdout.flush()
    return _idle_until_signal()


def _arg(args, flag):
    return args[args.index(flag) + 1] if flag in args and args.index(flag) + 1 < len(args) else None


def run_bridge(args):
    debug_file = _arg(args, "--debug-file")
    mode = os.environ.get("FAKE_CLAUDE_MODE", "ready")

    # Record the full argv next to the debug file so tests can assert the flags
    # Clauster passed (--spawn / --permission-mode, etc.).
    if debug_file:
        import json

        with open(debug_file + ".argv.json", "w") as fh:
            json.dump(args, fh)

    def log(line):
        if debug_file:
            with open(debug_file, "a") as fh:
                fh.write(line + "\n")

    if mode == "crash":
        return 1
    if mode == "trust_error":
        log("2026-01-01T00:00:00.000Z [ERROR] Workspace not trusted")
        return 1
    if mode == "stderr_error":
        # Failure reason on stderr only (NOT --debug-file), e.g. a controller-auth
        # rejection. clauster routes stdout+stderr to a file and surfaces this.
        sys.stderr.write("Error: failed to authenticate bridge to controller (HTTP 401)\n")
        sys.stderr.flush()
        return 1
    if mode == "stall":
        # Alive but never registers an environment: mimic a bridge that launched
        # yet could not authenticate to the controller (the real "running but
        # unusable" case). Emit only a non-marker debug line, then idle until
        # signalled — no bridge:init/api/work markers are ever written.
        log("2026-01-01T00:00:00.000Z [DEBUG] Failed to read OAuth token: permission denied")
        stalled = {"v": False}

        def _stop(_signum, _frame):
            stalled["v"] = True

        signal.signal(signal.SIGINT, _stop)
        signal.signal(signal.SIGTERM, _stop)
        if hasattr(signal, "SIGBREAK"):  # Windows CTRL_BREAK_EVENT
            signal.signal(signal.SIGBREAK, _stop)
        for _ in range(2400):  # idle up to ~600s waiting for a signal
            if stalled["v"]:
                break
            time.sleep(0.25)
        return 0
    if mode == "slow":
        time.sleep(float(os.environ.get("FAKE_CLAUDE_SLOW", "1.0")))

    log(f"2026-01-01T00:00:00.000Z [DEBUG] [bridge:init] bridgeId={BRIDGE_ID} dir=/x branch=HEAD")
    log(f"2026-01-01T00:00:00.100Z [DEBUG] [bridge:api] POST /v1/environments/bridge -> 200 environment_id={ENV_ID}")
    if mode != "resume":
        # A reconnecting bridge re-attaches to an existing session and does NOT
        # re-log this line — so 'resume' mode omits it and clauster must recover
        # the session id from the bridge-pointer instead.
        log(f"2026-01-01T00:00:00.200Z [DEBUG] [bridge:init] Created initial session {STARTER}")

    if mode == "no_marker":
        return 0  # exits before the poll loop -> never becomes ready

    log(f"2026-01-01T00:00:00.300Z [DEBUG] [bridge:work] Starting poll loop spawnMode=same-dir maxSessions=32 environmentId={ENV_ID}")

    stopped = {"v": False}

    def handler(_signum, _frame):
        log("2026-01-01T00:00:05.000Z [DEBUG] [bridge:shutdown] SIGINT received, shutting down")
        stopped["v"] = True

    signal.signal(signal.SIGINT, handler)
    signal.signal(signal.SIGTERM, handler)
    if hasattr(signal, "SIGBREAK"):  # Windows CTRL_BREAK_EVENT (graceful stop)
        signal.signal(signal.SIGBREAK, handler)
    for _ in range(2400):  # idle up to ~600s waiting for a signal
        if stopped["v"]:
            break
        time.sleep(0.25)
    return 0


def main():
    args = sys.argv[1:]
    if args and args[0] == "--version":
        print("2.1.156 (Claude Code)")
        return 0
    if args and args[0] == "agents" and "--json" in args:
        print(os.environ.get("FAKE_CLAUDE_AGENTS", "[]"))
        return 0
    if args and args[0] == "remote-control":
        return run_bridge(args)
    if args and args[0] == "--remote-control":
        return run_flag_bridge(args)
    sys.stderr.write(f"fake-claude: unknown args {args}\n")
    return 2


if __name__ == "__main__":
    raise SystemExit(main())
