#!/usr/bin/env python3
"""
Signet Identity Functional Test

Tests that all harnesses are correctly configured to share identity.
Verifies config files, symlinks, and (optionally) live harness responses.

Run: python ~/.agents/tests/test_identity.py [--live]
"""

import subprocess
import sys
import re
import os
import signal
from pathlib import Path

EXPECTED_NAME = "Mr. Claude"
EXPECTED_OPERATOR = "Nicholai"
LIVE_MODE = "--live" in sys.argv


def test_agents_md_content():
    """Test AGENTS.md contains correct identity."""
    print("1. Testing AGENTS.md content...")
    
    agents_md = Path.home() / ".agents/AGENTS.md"
    
    if not agents_md.exists():
        print(f"   ✗ AGENTS.md not found: {agents_md}")
        return False
    
    content = agents_md.read_text()
    
    has_name = EXPECTED_NAME in content
    has_operator = EXPECTED_OPERATOR in content
    
    if has_name and has_operator:
        print(f"   ✓ AGENTS.md contains '{EXPECTED_NAME}' and '{EXPECTED_OPERATOR}'")
        return True
    else:
        print(f"   ✗ Identity not found in AGENTS.md")
        return False


def test_claude_code_config():
    """Test Claude Code config is generated from AGENTS.md."""
    print("\n2. Testing Claude Code config...")
    
    agents_md = Path.home() / ".agents/AGENTS.md"
    claude_md = Path.home() / ".claude/CLAUDE.md"
    
    if not claude_md.exists():
        print(f"   ✗ Claude Code config missing: {claude_md}")
        return False
    
    agents_content = agents_md.read_text()
    claude_content = claude_md.read_text()
    
    # Check if AGENTS.md content is present in generated file
    # (generated files have a header, so we check for content inclusion)
    if agents_content in claude_content:
        print(f"   ✓ Claude Code config contains AGENTS.md")
        return True
    elif EXPECTED_NAME in claude_content and EXPECTED_OPERATOR in claude_content:
        print(f"   ~ Claude Code config has identity (different format)")
        return True
    else:
        print(f"   ✗ Claude Code config missing identity")
        return False


def test_opencode_config():
    """Test OpenCode config is generated from AGENTS.md."""
    print("\n3. Testing OpenCode config...")
    
    agents_md = Path.home() / ".agents/AGENTS.md"
    opencode_md = Path.home() / ".config/opencode/AGENTS.md"
    
    if not opencode_md.exists():
        print(f"   ✗ OpenCode config missing: {opencode_md}")
        return False
    
    agents_content = agents_md.read_text()
    opencode_content = opencode_md.read_text()
    
    # Check if AGENTS.md content is present in generated file
    if agents_content in opencode_content:
        print(f"   ✓ OpenCode config contains AGENTS.md")
        return True
    elif EXPECTED_NAME in opencode_content and EXPECTED_OPERATOR in opencode_content:
        print(f"   ~ OpenCode config has identity (different format)")
        return True
    else:
        print(f"   ✗ OpenCode config missing identity")
        return False


def test_openclaw_config():
    """Test OpenClaw reads AGENTS.md from workspace."""
    print("\n4. Testing OpenClaw config...")
    
    agents_md = Path.home() / ".agents/AGENTS.md"
    
    # OpenClaw uses workspace context, which includes AGENTS.md
    # Just verify the file exists and is readable
    if agents_md.exists() and agents_md.is_file():
        content = agents_md.read_text()
        if EXPECTED_NAME in content and EXPECTED_OPERATOR in content:
            print(f"   ✓ OpenClaw workspace has AGENTS.md")
            return True
    
    print(f"   ✗ OpenClaw workspace missing AGENTS.md")
    return False


def test_live_claude_code():
    """Test Claude Code responds with correct identity (requires API)."""
    print("\n5. Testing Claude Code identity (live)...")
    
    if not LIVE_MODE:
        print("   ⊘ Skipped (use --live to enable)")
        return True
    
    try:
        proc = subprocess.Popen(
            ["claude", "--print", "--dangerously-skip-permissions", 
             "-p", "What is your name and who is your operator? Answer in one sentence."],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            start_new_session=True
        )
        
        try:
            stdout, stderr = proc.communicate(timeout=300)
            output = stdout + stderr
        except subprocess.TimeoutExpired:
            os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
            proc.wait()
            print("   ✗ Claude Code timed out (5m)")
            return False
        
        output_lower = output.lower()
        
        has_name = "claude" in output_lower or "mr. claude" in output_lower
        has_operator = "nicholai" in output_lower
        
        if has_name and has_operator:
            print(f"   ✓ Claude Code identifies correctly")
            return True
        else:
            print(f"   ✗ Claude Code identity not confirmed")
            return False
            
    except FileNotFoundError:
        print("   ⊘ Claude Code not installed (skipped)")
        return True
    except Exception as e:
        print(f"   ✗ Error: {e}")
        return False


def test_live_opencode():
    """Test OpenCode responds with correct identity (requires API)."""
    print("\n6. Testing OpenCode identity (live)...")
    
    if not LIVE_MODE:
        print("   ⊘ Skipped (use --live to enable)")
        return True
    
    try:
        proc = subprocess.Popen(
            ["opencode", "run", 
             "What is your name and who is your operator? Answer in one sentence."],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            start_new_session=True
        )
        
        try:
            stdout, stderr = proc.communicate(timeout=300)
            output = stdout + stderr
        except subprocess.TimeoutExpired:
            os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
            proc.wait()
            print("   ✗ OpenCode timed out (5m)")
            return False
        
        # Strip ANSI codes
        ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
        clean_output = ansi_escape.sub('', output)
        output_lower = clean_output.lower()
        
        has_name = "claude" in output_lower or "mr. claude" in output_lower
        has_operator = "nicholai" in output_lower
        
        if has_name and has_operator:
            print(f"   ✓ OpenCode identifies correctly")
            return True
        else:
            print(f"   ✗ OpenCode identity not confirmed")
            return False
            
    except FileNotFoundError:
        print("   ⊘ OpenCode not installed (skipped)")
        return True
    except Exception as e:
        print(f"   ✗ Error: {e}")
        return False


def main():
    print("=" * 60)
    print("SIGNET IDENTITY TEST")
    print("=" * 60)
    if LIVE_MODE:
        print("Mode: LIVE (includes API calls, may take several minutes)")
    else:
        print("Mode: FAST (config/symlink verification only)")
        print("Use --live to include API response tests")
    print()
    
    tests = [
        ("AGENTS.md content", test_agents_md_content),
        ("Claude Code config", test_claude_code_config),
        ("OpenCode config", test_opencode_config),
        ("OpenClaw config", test_openclaw_config),
    ]
    
    if LIVE_MODE:
        tests.extend([
            ("Claude Code live", test_live_claude_code),
            ("OpenCode live", test_live_opencode),
        ])
    
    results = []
    for name, test_fn in tests:
        try:
            results.append(test_fn())
        except Exception as e:
            print(f"   ✗ Error: {e}")
            results.append(False)
    
    print()
    print("=" * 60)
    passed = sum(results)
    total = len(results)
    
    if all(results):
        print(f"RESULT: ALL TESTS PASSED ({passed}/{total})")
        print("=" * 60)
        return 0
    else:
        print(f"RESULT: {passed}/{total} TESTS PASSED")
        print("=" * 60)
        return 1


if __name__ == "__main__":
    sys.exit(main())
