#!/usr/bin/env python3
"""Read-only / gating self-test for the screenconnect skill.

Runs each CLI command as a subprocess and checks exit code + output markers. NO
state-changing API calls (writes are tested only in their --confirm-absent refusal
path). build-installer is a pure URL build (no network). Prints a PASS/FAIL report.
"""
from __future__ import annotations

import json
import os
import subprocess
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
SC = os.path.join(HERE, "sc.py")
results = []


def run(args):
    env = dict(os.environ)
    env.setdefault("CLAUDETOOLS_ROOT", "C:/claudetools")
    env["PYTHONIOENCODING"] = "utf-8"
    env["SC_SUPPRESS_ERRORLOG"] = "1"  # never let the self-test touch errorlog.md
    p = subprocess.run([sys.executable, SC] + args, capture_output=True,
                       text=True, env=env, timeout=120)
    return p.returncode, p.stdout, p.stderr


def check(name, args, *, want_rc=None, out_has=None, out_json_ok=False, out_json_min_len=None):
    rc, out, err = run(args)
    problems = []
    if want_rc is not None and rc != want_rc:
        problems.append(f"rc={rc} want {want_rc}")
    if out_has and out_has not in out:
        problems.append(f"stdout missing {out_has!r}")
    if out_json_ok or out_json_min_len is not None:
        try:
            parsed = json.loads(out)
            if out_json_min_len is not None and len(parsed) < out_json_min_len:
                problems.append(f"json len {len(parsed)} < {out_json_min_len}")
        except Exception as e:
            problems.append(f"stdout not JSON: {e}")
    results.append(("PASS" if not problems else "FAIL", name, "; ".join(problems)))


# --- reads: succeed (rc 0) [hit the live instance] ---
check("status", ["status"], want_rc=0, out_has="Authenticated")
check("status shows fleet", ["status"], want_rc=0, out_has="fleet:")
check("sessions (full fleet)", ["sessions"], want_rc=0, out_has="Sessions:")
check("sessions json", ["sessions", "--json"], want_rc=0, out_json_ok=True)
check("sessions --limit", ["sessions", "--limit", "5"], want_rc=0, out_has="Sessions:")
check("sessions --like", ["sessions", "--like", "DELL", "--json"], want_rc=0, out_json_ok=True)
check("sessions --type all", ["sessions", "--type", "all", "--json"], want_rc=0, out_json_ok=True)
check("sessions --filter", ["sessions", "--filter", "SessionType = 'Access'", "--json"],
      want_rc=0, out_json_ok=True)
check("sessions --company", ["sessions", "--company", "Safesite", "--json"],
      want_rc=0, out_json_ok=True)
# --limit must NOT truncate --json (documented "--json is always full" contract):
# Safesite has 62 machines; even with --limit 1 the JSON must return them all.
check("sessions --limit does not truncate json",
      ["sessions", "--company", "Safesite", "--limit", "1", "--json"],
      want_rc=0, out_json_min_len=10)

# --- build-installer: pure URL build (no network) ---
check("build-installer", ["build-installer", "--name", "HOST", "--company", "AZ Computer Guru",
      "--site", "Howard-VM", "--tag", "T1"], want_rc=0, out_has="e=Access")
check("build-installer encodes spaces", ["build-installer", "--company", "A B C"],
      want_rc=0, out_has="A%20B%20C")
check("build-installer json", ["build-installer", "--json", "--tag", "X"],
      want_rc=0, out_json_ok=True)

# --- gating: writes refuse without --confirm (rc 3, no API call) ---
check("send-command no confirm -> rc3", ["send-command", "--session", "x", "--command", "whoami"],
      want_rc=3, out_has="Would")
check("send-message no confirm -> rc3", ["send-message", "--session", "x", "--message", "hi"],
      want_rc=3)
check("set-properties no confirm -> rc3",
      ["set-properties", "--session", "x", "--props-json", '["A","B","C"]'], want_rc=3)
check("set-properties bad json -> rc2",
      ["set-properties", "--session", "x", "--props-json", "{bad", "--confirm"], want_rc=2)

# --- raw: read ok; destructive method refused without --confirm ---
check("raw read ok", ["raw", "--method", "GetSessionsByName", "--body", '{"sessionName":""}'],
      want_rc=0)
check("raw SendCommandToSession no confirm -> rc3",
      ["raw", "--method", "SendCommandToSession", "--body", "[]"], want_rc=3)

# --- report ---
print("\n==== screenconnect skill self-test ====")
npass = sum(1 for r in results if r[0] == "PASS")
for status, name, prob in results:
    print(f"[{status}] {name}" + (f"  -> {prob}" if prob else ""))
print(f"\n{npass}/{len(results)} passed, {len(results)-npass} failed")
sys.exit(0 if npass == len(results) else 1)
