"""House Monkey CLI — chaos test your AI app."""

import argparse
import os
import sys
import time

from housemonkey.personas import PERSONAS, list_personas
from housemonkey.runner import run_persona_against_target
from housemonkey.judge import judge_conversation


# Terminal colors
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BOLD = "\033[1m"
DIM = "\033[2m"
RESET = "\033[0m"


def print_banner():
    print(f"""{BOLD}
  ╔══════════════════════════════════════╗
  ║  House Monkey — Chaos Testing for AI    ║
  ║  100 extreme personas. 0 mercy.     ║
  ╚══════════════════════════════════════╝{RESET}
""")


def print_result(persona_name: str, persona: dict, result, judgment: dict):
    verdict = judgment.get("verdict", "error")
    score = judgment.get("score", 0)
    summary = judgment.get("summary", "")

    if verdict == "pass":
        icon = f"{GREEN}✓ PASS{RESET}"
    elif verdict == "fail":
        icon = f"{RED}✗ FAIL{RESET}"
    else:
        icon = f"{YELLOW}? ERROR{RESET}"

    severity_color = {"critical": RED, "high": YELLOW, "medium": DIM}.get(persona["severity"], "")
    owasp_tag = f" {DIM}OWASP:{persona.get('owasp', '—')}{RESET}" if persona.get("owasp") else ""

    print(f"  {icon}  {BOLD}{persona['name']}{RESET}  {severity_color}[{persona['severity']}]{RESET}{owasp_tag}  score:{score}  {DIM}{result.duration_ms}ms{RESET}")

    if verdict == "fail":
        for c in judgment.get("criteria", []):
            if c.get("result", "").lower() == "fail":
                print(f"       {RED}└ {c['name']}: {c['reason']}{RESET}")

    if verdict == "error":
        print(f"       {YELLOW}└ {summary}{RESET}")


def run_all(args):
    print_banner()
    print(f"  Target: {BOLD}{args.target}{RESET}")
    print(f"  Personas: {len(PERSONAS)}")
    print(f"  API Key: {'set' if args.api_key or os.environ.get('OPENAI_API_KEY') else 'missing'}")
    print()

    headers = {}
    if args.auth:
        headers["Authorization"] = f"Bearer {args.auth}"

    results = []
    passed = 0
    failed = 0
    errors = 0
    total_start = time.monotonic()

    for name, persona in PERSONAS.items():
        if args.persona and name not in args.persona:
            continue
        if args.severity and persona["severity"] != args.severity:
            continue
        if getattr(args, 'owasp', False) and "owasp" not in persona:
            continue

        print(f"  {DIM}Running {persona['name']}...{RESET}", end="", flush=True)
        print("\r" + " " * 60 + "\r", end="", flush=True)

        result = run_persona_against_target(
            persona_name=name,
            target_url=args.target,
            target_headers=headers,
            api_key=args.api_key,
            verbose=args.verbose,
            payload_template=getattr(args, 'payload_template', None),
        )

        if result.verdict == "error":
            judgment = {"verdict": "error", "score": 0, "summary": "; ".join(result.failures), "criteria": []}
        else:
            judgment = judge_conversation(persona, result.turns, api_key=args.api_key)

        print_result(name, persona, result, judgment)

        if judgment["verdict"] == "pass":
            passed += 1
        elif judgment["verdict"] == "fail":
            failed += 1
        else:
            errors += 1

        results.append({"persona": name, "judgment": judgment, "turns": result.turns})

    total_ms = int((time.monotonic() - total_start) * 1000)
    total = passed + failed + errors

    # Severity breakdown
    severity_stats = {"critical": {"pass": 0, "fail": 0, "error": 0}, "high": {"pass": 0, "fail": 0, "error": 0}, "medium": {"pass": 0, "fail": 0, "error": 0}}
    for r in results:
        p = PERSONAS.get(r["persona"], {})
        sev = p.get("severity", "medium")
        v = r["judgment"].get("verdict", "error")
        if v in severity_stats.get(sev, {}):
            severity_stats[sev][v] += 1

    print()
    print(f"  {BOLD}{'═' * 50}{RESET}")
    print(f"  {GREEN}Passed: {passed}{RESET}  {RED}Failed: {failed}{RESET}  {YELLOW}Errors: {errors}{RESET}  Total: {total}  {DIM}{total_ms}ms{RESET}")

    # Severity summary
    print(f"\n  {BOLD}By severity:{RESET}")
    for sev, color in [("critical", RED), ("high", YELLOW), ("medium", DIM)]:
        s = severity_stats[sev]
        if s["pass"] + s["fail"] + s["error"] > 0:
            print(f"    {color}[{sev:8s}]{RESET}  {GREEN}{s['pass']} pass{RESET}  {RED}{s['fail']} fail{RESET}  {YELLOW}{s['error']} err{RESET}")

    # OWASP coverage
    owasp_tested = set()
    owasp_failed = set()
    for r in results:
        p = PERSONAS.get(r["persona"], {})
        if "owasp" in p:
            owasp_tested.add(p["owasp"])
            if r["judgment"].get("verdict") == "fail":
                owasp_failed.add(p["owasp"])
    if owasp_tested:
        print(f"\n  {BOLD}OWASP LLM Top 10:{RESET} {len(owasp_tested)} tested, {RED}{len(owasp_failed)} failed{RESET}")

    if failed > 0:
        print(f"\n  {RED}{BOLD}Your AI failed {failed} persona test(s).{RESET}")
        print(f"  {DIM}Fix these before your users find them.{RESET}")
    elif errors == 0:
        print(f"\n  {GREEN}{BOLD}All personas handled correctly.{RESET}")

    # Save report
    if args.output:
        import json
        with open(args.output, "w") as f:
            json.dump({"passed": passed, "failed": failed, "errors": errors, "results": results}, f, indent=2)
        print(f"\n  Report saved to {args.output}")

    return 1 if failed > 0 else 0


def run_list(args):
    print(f"\n  {BOLD}Available Personas ({len(PERSONAS)}):{RESET}\n")
    for name, p in PERSONAS.items():
        severity_color = {"critical": RED, "high": YELLOW, "medium": DIM}.get(p["severity"], "")
        print(f"  {severity_color}[{p['severity']:8s}]{RESET}  {BOLD}{p['name']:28s}{RESET}  {DIM}{p['description']}{RESET}")
    print()


def main():
    parser = argparse.ArgumentParser(
        prog="housemonkey",
        description="Chaos testing for AI apps — 100 extreme personas, 0 mercy.",
    )
    sub = parser.add_subparsers(dest="command")

    # Run command
    run_parser = sub.add_parser("run", help="Run chaos tests against a target API")
    run_parser.add_argument("--target", "-t", required=True, help="Target API URL (OpenAI-compatible)")
    run_parser.add_argument("--auth", help="Bearer token for target API")
    run_parser.add_argument("--api-key", help="OpenAI/Anthropic API key for persona generation + judging")
    run_parser.add_argument("--persona", "-p", nargs="+", help="Run specific personas only")
    run_parser.add_argument("--severity", "-s", choices=["critical", "high", "medium"], help="Filter by severity")
    run_parser.add_argument("--owasp", action="store_true", help="Run only OWASP LLM Top 10 mapped personas")
    run_parser.add_argument("--output", "-o", help="Save JSON report to file")
    run_parser.add_argument("--verbose", "-v", action="store_true", help="Show conversation turns")
    run_parser.add_argument("--payload-template", help='Custom JSON payload template. Use {{message}} for persona text. E.g. \'{"input": "{{message}}"}\'')

    # List command
    sub.add_parser("list", help="List all available personas")

    args = parser.parse_args()

    if args.command == "run":
        sys.exit(run_all(args))
    elif args.command == "list":
        run_list(args)
    else:
        parser.print_help()


if __name__ == "__main__":
    main()
