#!/usr/bin/env python3
from __future__ import annotations
"""
End-to-end smoke test for the CodexBar attention hook pipeline.

Starts the companion server on a temporary port, fires hook payloads through
attention_hook.py as a subprocess (the same code path real Claude/Codex hooks
use), and asserts that /status reflects the expected attention state.

Usage:
    python3 companion/smoke_test_hooks.py

Exit 0 on success, non-zero on any failure.
"""

import json
import os
import subprocess
import sys
import threading
import time
import urllib.request
from http.server import HTTPServer

# Make sure the project root is on sys.path so `companion` is importable
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import companion.server as server


# ── Helpers ───────────────────────────────────────────────────────────────

def start_server() -> tuple[HTTPServer, str]:
    server.NTFY_TOPIC = ""
    with server._attention_lock:
        server._attention_records.clear()
        server._attention_seq = 0
    server._invalidate_usage_cache()

    httpd = HTTPServer(("127.0.0.1", 0), server.Handler)
    port = httpd.server_address[1]
    t = threading.Thread(target=httpd.serve_forever, daemon=True)
    t.start()
    return httpd, f"http://127.0.0.1:{port}"


def fire_hook(provider: str, payload: dict, base_url: str) -> subprocess.CompletedProcess:
    env = os.environ.copy()
    env["CODEXBAR_ATTENTION_URL"] = f"{base_url}/attention"
    return subprocess.run(
        [sys.executable, "companion/attention_hook.py", provider],
        input=json.dumps(payload),
        text=True,
        capture_output=True,
        timeout=5,
        env=env,
    )


def get_status(base_url: str) -> dict:
    with urllib.request.urlopen(f"{base_url}/status", timeout=5) as resp:
        return json.loads(resp.read())


def provider_attention(status: dict, name: str) -> dict:
    for p in status.get("providers", []):
        if p["name"] == name:
            return p["attention"]
    return {}


# ── Smoke cases ───────────────────────────────────────────────────────────

CASES: list[tuple[str, str, dict, str, str]] = [
    # (label, provider, hook_payload, expected_state, expected_reason)
    (
        "PermissionRequest → needs_user",
        "Claude",
        {"hook_event_name": "PermissionRequest", "session_id": "smoke-1", "cwd": "/tmp/proj-a"},
        "needs_user",
        "permission_request",
    ),
    (
        "UserPromptSubmit → running",
        "Claude",
        {"hook_event_name": "UserPromptSubmit", "session_id": "smoke-1", "cwd": "/tmp/proj-a"},
        "running",
        "user_prompt",
    ),
    (
        "Stop → done",
        "Claude",
        {"hook_event_name": "Stop", "session_id": "smoke-1", "cwd": "/tmp/proj-a"},
        "done",
        "stop",
    ),
    (
        "Codex PermissionRequest → needs_user",
        "Codex",
        {"hook_event_name": "PermissionRequest", "session_id": "smoke-codex", "cwd": "/tmp/codex-proj"},
        "needs_user",
        "permission_request",
    ),
    (
        "Codex SessionEnd → idle",
        "Codex",
        {"hook_event_name": "SessionEnd", "session_id": "smoke-codex", "cwd": "/tmp/codex-proj"},
        "idle",
        "",
    ),
]


def run_smoke_tests() -> bool:
    httpd, base_url = start_server()
    passed = 0
    failed = 0

    try:
        for label, provider, payload, exp_state, exp_reason in CASES:
            result = fire_hook(provider, payload, base_url)
            if result.returncode != 0:
                print(f"  FAIL  {label}")
                print(f"        hook script exited {result.returncode}: {result.stderr.strip()}")
                failed += 1
                continue

            time.sleep(0.05)  # let the server finish handling the POST
            status = get_status(base_url)
            attn = provider_attention(status, provider)
            state = attn.get("state", "")
            reason = attn.get("reason", "")

            state_ok = state == exp_state
            reason_ok = (exp_reason == "") or (reason == exp_reason)

            if state_ok and reason_ok:
                print(f"  PASS  {label}")
                passed += 1
            else:
                print(f"  FAIL  {label}")
                if not state_ok:
                    print(f"        state: got {state!r}, want {exp_state!r}")
                if not reason_ok:
                    print(f"        reason: got {reason!r}, want {exp_reason!r}")
                failed += 1
    finally:
        httpd.shutdown()
        httpd.server_close()

    print()
    print(f"Results: {passed} passed, {failed} failed")
    return failed == 0


if __name__ == "__main__":
    print("CodexBar attention hook smoke test")
    print("===================================")
    ok = run_smoke_tests()
    sys.exit(0 if ok else 1)
