#!/usr/bin/python3
"""
Harness test for recap_mcp.py (the connect/disconnect concierge) against a SCRATCH
config file, with the quit/relaunch steps stubbed so the real Claude Desktop is
never touched. This exercises the exact code paths that matter and asserts the
specific behaviours (the Hush lesson: a fail-safe test that bails on an earlier
guard proves nothing - stub past the guards).

The one thing this deliberately does NOT test is the live quit-and-relaunch of
Claude Desktop; that path is a direct port of Hush's, verified live 2026-07-16.

Run:  /usr/bin/python3 test_concierge.py
"""
from __future__ import annotations

import json
import os
import sys
import tempfile

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(HERE))  # engine/

tmp = tempfile.mkdtemp(prefix="recap-mcp-test-")
CFG = os.path.join(tmp, "claude_desktop_config.json")
os.environ["RECAP_MCP_CLAUDE_CFG"] = CFG

import recap_mcp  # noqa: E402  (env must be set before import)

# Stub the parts that would touch the real app: never quit, never relaunch.
recap_mcp._claude_running = lambda: False
recap_mcp._relaunch_claude = lambda: None

passed = failed = 0


def check(name: str, ok: bool, detail: str = ""):
    global passed, failed
    passed += ok
    failed += not ok
    print(f"  {'PASS' if ok else 'FAIL'}  {name}" + (f"  {detail}" if detail and not ok else ""))


# 1. connect() on a missing config creates it with exactly our entry.
r = recap_mcp.connect()
check("connect on empty config verifies", r.get("ok") is True and r.get("verified") is True, str(r))
cfg = json.load(open(CFG))
check("entry points at bundled server",
      cfg["mcpServers"]["recapmuse"]["args"] == [recap_mcp.SERVER_PATH]
      and cfg["mcpServers"]["recapmuse"]["command"] == "/usr/bin/python3")

# 2. status() now reports registered.
st = recap_mcp.status()
check("status registered after connect", st.get("registered") is True and not st.get("stale_entry"))

# 3. OTHER connectors survive a connect (the data-loss class from the Hush bug).
cfg["mcpServers"]["someone-else"] = {"command": "node", "args": ["/x.js"]}
cfg["preferences"] = {"theme": "dark"}
json.dump(cfg, open(CFG, "w"))
r = recap_mcp.connect()
cfg2 = json.load(open(CFG))
check("re-connect preserves other connectors",
      r.get("ok") is True and cfg2["mcpServers"].get("someone-else", {}).get("command") == "node")
check("re-connect preserves unrelated top-level keys", cfg2.get("preferences", {}).get("theme") == "dark")

# 4. A MALFORMED config is fatal, never treated as empty (the exact Hush data-loss bug).
open(CFG, "w").write("{ this is not json")
st = recap_mcp.status()
check("status says config unreadable", st.get("config_unreadable") is True and not st.get("registered"))
r = recap_mcp.connect()
check("connect refuses on unreadable config", r.get("ok") is False and "delete" in r.get("error", "").lower())
check("unreadable config was not overwritten", open(CFG).read() == "{ this is not json")
r = recap_mcp.disconnect()
check("disconnect refuses on unreadable config", r.get("ok") is False)

# 5. stale entry (old app path) -> status offers reconnect, connect() heals it.
json.dump({"mcpServers": {"recapmuse": {"command": "/usr/bin/python3",
                                        "args": ["/old/gone/server.py"], "env": {}}}}, open(CFG, "w"))
st = recap_mcp.status()
check("stale entry detected", st.get("stale_entry") is True and st.get("registered") is False)
r = recap_mcp.connect()
st = recap_mcp.status()
check("connect heals stale entry", r.get("ok") is True and st.get("registered") is True)

# 6. disconnect removes ONLY our key and verifies.
cfg = json.load(open(CFG))
cfg["mcpServers"]["someone-else"] = {"command": "node", "args": ["/x.js"]}
json.dump(cfg, open(CFG, "w"))
r = recap_mcp.disconnect()
cfg2 = json.load(open(CFG))
check("disconnect verifies", r.get("ok") is True and r.get("verified") is True, str(r))
check("disconnect removes only our key",
      "recapmuse" not in cfg2["mcpServers"] and "someone-else" in cfg2["mcpServers"])

# 7. every mutation left a backup beside the config.
baks = [f for f in os.listdir(tmp) if f.startswith("claude_desktop_config.json.bak-")]
check("backups written before mutations", len(baks) >= 1, str(baks))

print(f"\n{passed} passed, {failed} failed")
sys.exit(1 if failed else 0)
