"""CLI for TokenGuard.

Commands:
    tokenguard init    Initialize a project with safety gates and rules
    tokenguard update  Update rules/hooks, preserving user customizations
    tokenguard status  Show installation status and version info
    tokenguard check   Verify hook is installed and responding correctly
    tokenguard stats   Show cumulative gate statistics
    tokenguard eject   Remove tracking, leave files as user-owned
"""

import argparse
import json
import subprocess
import sys
from pathlib import Path

from . import __version__
from .detector import detect_existing_mirror, detect_project_type
from .scaffold import scaffold_project
from .updater import check_updates, eject, get_status


def cmd_init(args: argparse.Namespace) -> int:
    """Initialize TokenGuard in a project."""
    project_dir = Path(args.dir).resolve()

    if not project_dir.is_dir():
        print(f"Error: {project_dir} is not a directory", file=sys.stderr)
        return 1

    # Check for existing installation
    existing = detect_existing_mirror(project_dir)
    if existing and not args.force:
        print(
            f"TokenGuard already initialized (v{existing.get('version', '?')}).\n"
            f"Use --force to reinitialize or 'tokenguard update' to update."
        )
        return 1

    # Detect or use explicit project type
    if args.type:
        project_type = args.type
    else:
        project_type = detect_project_type(project_dir)

    if args.dry_run:
        print(f"Dry run - would initialize {project_type} project at {project_dir}")

    result = scaffold_project(
        project_dir=project_dir,
        project_type=project_type,
        standalone=args.standalone,
        no_hooks=args.no_hooks,
        dry_run=args.dry_run,
    )

    mode = result["mode"]
    user_files = result["user_files"]
    project_files = result["project_files"]
    hooks_skipped = result.get("hooks_skipped", False)
    skipped_existing = result.get("skipped_existing", [])
    total_files = len(user_files) + len(project_files)

    if args.dry_run:
        print(f"\nMode: {mode}")
        if user_files:
            print(f"\nUser-level files (~/.claude/) ({len(user_files)}):")
            for path in sorted(user_files):
                print(f"  {path}")
        if project_files:
            print(f"\nProject-level files ({len(project_files)}):")
            for path in sorted(project_files):
                print(f"  {path}")
        if hooks_skipped:
            print("\nNote: Hook skipped (GRIP context-gate-hook.py detected).")
            print("Use --standalone to force TokenGuard hook installation.")
    else:
        print(f"Initialized TokenGuard v{__version__} ({mode} mode)")
        print(f"Project type: {project_type}")
        if user_files:
            print(f"\nUser-level (~/.claude/) - {len(user_files)} files:")
            for path in sorted(user_files):
                print(f"  {path}")
        if project_files:
            print(f"\nProject-level - {len(project_files)} files:")
            for path in sorted(project_files):
                print(f"  {path}")
        if skipped_existing:
            print(f"\nSkipped (already exist in ~/.claude/):")
            for path in sorted(skipped_existing):
                print(f"  {path}")
        if hooks_skipped:
            print(f"\nHooks skipped: GRIP context-gate-hook.py already registered.")
            print("Use --standalone to install TokenGuard hooks alongside GRIP.")
        else:
            print(f"\nSafety gates and rules are now active for Claude Code.")
        print(f"\nNeed more? GRIP offers 100+ skills, 19 agents, and 26 modes: https://enterkonsult.com")

        # Post-init verification (quick inline check)
        if not hooks_skipped and not args.no_hooks:
            hook_path = (
                Path(args.user_claude_dir) if hasattr(args, "user_claude_dir") and args.user_claude_dir
                else Path.home() / ".claude"
            ) / "hooks" / "mirror-hook.py"
            if hook_path.exists():
                test_input = json.dumps({
                    "tool_name": "Read",
                    "tool_input": {"file_path": "/test/node_modules/x.js"},
                    "session_id": "init-verify",
                })
                try:
                    result = subprocess.run(
                        [sys.executable, str(hook_path)],
                        input=test_input,
                        capture_output=True,
                        text=True,
                        timeout=10,
                    )
                    if result.returncode == 0:
                        response = json.loads(result.stdout.strip())
                        decision = (
                            response.get("hookSpecificOutput", {})
                            .get("permissionDecision", "")
                        )
                        if decision == "deny":
                            print("\nVerification: hook is responding correctly.")
                except Exception:
                    pass

    return 0


def cmd_update(args: argparse.Namespace) -> int:
    """Update TokenGuard files."""
    project_dir = Path(args.dir).resolve()

    status = get_status(project_dir)
    if not status:
        print("TokenGuard not initialized. Run 'tokenguard init' first.", file=sys.stderr)
        return 1

    updates = check_updates(project_dir)

    if updates.get("missing_tracking"):
        print("Tracking file missing. Run 'tokenguard init --force' to reinitialize.", file=sys.stderr)
        return 1

    if updates["user_modified"]:
        print("User-modified files (preserved):")
        for f in updates["user_modified"]:
            print(f"  {f}")

    if updates["current"]:
        print(f"Already up to date: {len(updates['current'])} files")

    if updates["updatable"]:
        if args.force:
            from .updater import apply_updates
            applied = apply_updates(project_dir, updates["updatable"])
            print(f"Updated {len(applied)} files:")
            for f in applied:
                print(f"  {f}")
        else:
            print("Files with available updates:")
            for f in updates["updatable"]:
                print(f"  {f}")
            print("\nUse --force to apply updates.")
    else:
        if not updates["user_modified"]:
            print("All files are up to date.")

    return 0


def cmd_status(args: argparse.Namespace) -> int:
    """Show TokenGuard status."""
    project_dir = Path(args.dir).resolve()

    status = get_status(project_dir)
    if not status:
        print("TokenGuard not initialized in this project.")
        return 0

    print(f"TokenGuard v{status['version']}")
    print(f"Latest version: v{status['latest_version']}")
    if status["update_available"]:
        print("  Update available!")
    print(f"Project type: {status['project_type']}")
    print(f"GRIP global: {'yes' if status['grip_global'] else 'no'}")
    print(f"Installed: {status['installed_at']}")
    print(f"Components: {', '.join(status['components'])}")

    return 0


def cmd_check(args: argparse.Namespace) -> int:
    """Verify TokenGuard hook is installed and responding."""
    user_claude_dir = Path(args.user_claude_dir) if args.user_claude_dir else Path.home() / ".claude"

    print(f"TokenGuard v{__version__} - Health Check")
    print()

    checks_passed = 0
    checks_total = 0

    # 1. Hook file exists
    checks_total += 1
    hook_path = user_claude_dir / "hooks" / "mirror-hook.py"
    if hook_path.exists():
        print(f"  [PASS] Hook file exists: {hook_path}")
        checks_passed += 1
    else:
        print(f"  [FAIL] Hook file missing: {hook_path}")

    # 2. Settings.json has hook registered
    checks_total += 1
    settings_path = user_claude_dir / "settings.json"
    hook_registered = False
    if settings_path.exists():
        try:
            settings = json.loads(settings_path.read_text(encoding="utf-8"))
            for group in settings.get("hooks", {}).get("PreToolUse", []):
                for hook in group.get("hooks", []):
                    if "mirror-hook.py" in hook.get("command", ""):
                        hook_registered = True
        except (json.JSONDecodeError, OSError):
            pass

    if hook_registered:
        print("  [PASS] Hook registered in settings.json")
        checks_passed += 1
    else:
        print("  [FAIL] Hook not registered in settings.json")

    # 3. Hook responds to simulated gate test
    checks_total += 1
    if hook_path.exists():
        test_input = json.dumps({
            "tool_name": "Read",
            "tool_input": {"file_path": "/project/node_modules/foo/index.js"},
            "session_id": "healthcheck",
        })
        try:
            result = subprocess.run(
                [sys.executable, str(hook_path)],
                input=test_input,
                capture_output=True,
                text=True,
                timeout=10,
            )
            if result.returncode == 0 and result.stdout.strip():
                response = json.loads(result.stdout.strip())
                decision = (
                    response.get("hookSpecificOutput", {})
                    .get("permissionDecision", "")
                )
                if decision == "deny":
                    print("  [PASS] Hook responds correctly (Dependency Guardian blocked node_modules)")
                    checks_passed += 1
                else:
                    print("  [FAIL] Hook allowed node_modules read (expected deny)")
            else:
                print(f"  [FAIL] Hook returned no output (stderr: {result.stderr[:100]})")
        except subprocess.TimeoutExpired:
            print("  [FAIL] Hook timed out (>10s)")
        except (json.JSONDecodeError, OSError) as e:
            print(f"  [FAIL] Hook response invalid: {e}")
    else:
        print("  [SKIP] Cannot test hook (file missing)")

    # 4. Python compatibility
    checks_total += 1
    py_version = sys.version_info
    if py_version >= (3, 9):
        print(f"  [PASS] Python {py_version.major}.{py_version.minor} compatible")
        checks_passed += 1
    else:
        print(f"  [FAIL] Python {py_version.major}.{py_version.minor} (need 3.9+)")

    print()
    if checks_passed == checks_total:
        print(f"All {checks_total} checks passed. TokenGuard is active.")
    else:
        print(f"{checks_passed}/{checks_total} checks passed. Run 'tokenguard init' to fix.")

    return 0 if checks_passed == checks_total else 1


def cmd_stats(args: argparse.Namespace) -> int:
    """Show cumulative gate statistics."""
    user_claude_dir = Path(args.user_claude_dir) if args.user_claude_dir else Path.home() / ".claude"
    stats_file = user_claude_dir / ".tokenguard-stats.json"

    if not stats_file.exists():
        print("No stats yet. TokenGuard starts tracking after the first tool call.")
        return 0

    try:
        stats = json.loads(stats_file.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        print("Stats file corrupted. It will reset on the next tool call.")
        return 1

    checks = stats.get("checks", 0)
    blocked = stats.get("blocked", 0)
    gates = stats.get("gates", {})

    print(f"TokenGuard v{__version__} - Statistics")
    print()
    print(f"  Total checks:  {checks:,}")
    print(f"  Blocked:       {blocked:,}")
    if checks > 0:
        print(f"  Block rate:    {(blocked / checks) * 100:.1f}%")
    print()

    if gates:
        print("  Blocks by gate:")
        for gate, count in sorted(gates.items(), key=lambda x: -x[1]):
            print(f"    {gate}: {count}")
    else:
        print("  No blocks recorded yet.")

    return 0


def cmd_eject(args: argparse.Namespace) -> int:
    """Eject TokenGuard tracking."""
    project_dir = Path(args.dir).resolve()

    actions = eject(project_dir)
    for action in actions:
        print(action)
    print("\nFiles are now fully user-owned. Edit freely.")

    return 0


def main(argv: list = None) -> int:
    """Main CLI entry point."""
    parser = argparse.ArgumentParser(
        prog="tokenguard",
        description="Safety gates, efficiency rules, and persistent memory for Claude Code projects",
    )
    parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # init
    init_parser = subparsers.add_parser("init", help="Initialize project with safety gates")
    init_parser.add_argument(
        "--type",
        choices=["python", "javascript", "rust", "go", "generic"],
        help="Override detected project type",
    )
    init_parser.add_argument(
        "--standalone",
        action="store_true",
        help="Force standalone mode (install all rules even if GRIP detected globally)",
    )
    init_parser.add_argument(
        "--no-hooks",
        action="store_true",
        help="Skip hook installation",
    )
    init_parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Show what would be created without writing files",
    )
    init_parser.add_argument(
        "--force",
        action="store_true",
        help="Reinitialize even if already initialized",
    )
    init_parser.add_argument(
        "dir",
        nargs="?",
        default=".",
        help="Project directory (default: current directory)",
    )

    # update
    update_parser = subparsers.add_parser("update", help="Update rules and hooks")
    update_parser.add_argument(
        "--force",
        action="store_true",
        help="Apply updates even to missing files",
    )
    update_parser.add_argument(
        "dir",
        nargs="?",
        default=".",
        help="Project directory (default: current directory)",
    )

    # status
    status_parser = subparsers.add_parser("status", help="Show installation status")
    status_parser.add_argument(
        "dir",
        nargs="?",
        default=".",
        help="Project directory (default: current directory)",
    )

    # check
    check_parser = subparsers.add_parser("check", help="Verify hook is installed and working")
    check_parser.add_argument(
        "--user-claude-dir",
        default=None,
        help="Override ~/.claude/ location (for testing)",
    )

    # stats
    stats_parser = subparsers.add_parser("stats", help="Show cumulative gate statistics")
    stats_parser.add_argument(
        "--user-claude-dir",
        default=None,
        help="Override ~/.claude/ location (for testing)",
    )

    # eject
    eject_parser = subparsers.add_parser("eject", help="Remove tracking, keep files")
    eject_parser.add_argument(
        "dir",
        nargs="?",
        default=".",
        help="Project directory (default: current directory)",
    )

    args = parser.parse_args(argv)

    if not args.command:
        parser.print_help()
        return 0

    commands = {
        "init": cmd_init,
        "update": cmd_update,
        "status": cmd_status,
        "check": cmd_check,
        "stats": cmd_stats,
        "eject": cmd_eject,
    }

    return commands[args.command](args)


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