"""obol setup - one-command setup for Obol monitoring.

Usage:
    obol setup ob_live_xxxxxxxxxxxx

Does everything: saves config, scans for AI agents on your machine,
registers them, installs monitoring hooks. One command, done.
"""
import hashlib
import json
import os
import shutil
import sys


def setup_parser(subparsers):
    p = subparsers.add_parser("setup", help="Set up Obol monitoring (one command)")
    p.add_argument("api_key", nargs="?", help="Your API key (ob_live_...)")
    p.set_defaults(func=cmd_setup)


def cmd_setup(args):
    api_key = args.api_key or os.environ.get("OBOL_API_KEY", "")

    print()
    print("  OBOL SETUP")
    print("  Trust infrastructure for AI agents")
    print()

    # --- Step 1: Get API key ---
    if not api_key:
        print("  Your API key is on your dashboard at:")
        print("  https://obol.grayowlresearch.com/dashboard")
        print()
        try:
            api_key = input("  Paste your API key here: ").strip()
        except (EOFError, KeyboardInterrupt):
            print("\n  Cancelled.")
            sys.exit(1)

    if not api_key.startswith("ob_live_"):
        print("  That doesn't look right. Your API key starts with ob_live_")
        print("  You can find it at: https://obol.grayowlresearch.com/dashboard")
        sys.exit(1)

    # --- Step 2: Save config ---
    config_dir = os.path.expanduser("~/.obol")
    os.makedirs(config_dir, exist_ok=True)
    config_path = os.path.join(config_dir, "config.json")

    config = {}
    if os.path.exists(config_path):
        try:
            with open(config_path) as f:
                config = json.load(f)
        except Exception:
            pass

    api_url = config.get("api_url", "https://obol.grayowlresearch.com/v1")
    config["api_key"] = api_key
    config["api_url"] = api_url
    if "enforcement_mode" not in config:
        config["enforcement_mode"] = "audit"

    with open(config_path, "w") as f:
        json.dump(config, f, indent=2)
    from obol.cli._base import secure_file
    secure_file(config_path)
    print("  Saved your API key.")

    # --- Step 3: Verify connection ---
    from obol.api import ObolAPI, ObolAPIError

    api = ObolAPI(api_key, api_url)
    try:
        api._request("GET", "/agents")
        print("  Connected to Obol.")
    except ObolAPIError as e:
        print(f"\n  Could not connect. The API said: {e.detail}")
        print("  Double-check your API key and try again.")
        sys.exit(1)
    except Exception as e:
        print(f"\n  Could not reach the Obol server: {e}")
        print("  Check your internet connection and try again.")
        sys.exit(1)

    # --- Step 4: Scan for AI agents ---
    print()
    print("  Scanning for AI agents on your machine...")
    print()

    from obol.registry import detect_installed

    found = detect_installed()
    configured = 0

    if not found:
        print("  No known AI agents detected.")
        print()
        print("  You can still monitor any command with:")
        print("    obol wrap -- python my_agent.py")
        print("    obol wrap -- aider")
        print("    obol wrap -- <any command>")
    else:
        for agent_info in found:
            name = agent_info["name"]
            slug = agent_info["slug"]
            desc = agent_info["description"]
            method = agent_info["wrap_method"]

            print(f"  Found: {name}")
            print(f"         {desc}")

            if method == "hooks" and slug == "claude-code":
                agent_id = _setup_claude_code(api, config, config_path)
                if agent_id:
                    print(f"         Registered and hooks installed. (agent #{agent_id})")
                    configured += 1
                else:
                    print("         Could not configure (see error above).")
            elif method == "hooks" and slug == "cursor":
                agent_id = _setup_cursor(api, config, config_path)
                if agent_id:
                    print(f"         Registered and hooks installed. (agent #{agent_id})")
                    configured += 1
                else:
                    print("         Could not configure (see error above).")
            elif method == "hooks" and slug == "windsurf":
                agent_id = _setup_windsurf(api, config, config_path)
                if agent_id:
                    print(f"         Registered and hooks installed. (agent #{agent_id})")
                    configured += 1
                else:
                    print("         Could not configure (see error above).")
            elif method == "hooks":
                # Future: other hook-based agents
                agent_id = _register_agent(api, config, config_path, agent_info)
                if agent_id:
                    print(f"         Registered. (agent #{agent_id})")
                    print(f"         Hook support coming soon. Use: obol wrap -- {slug}")
                    configured += 1
            elif method == "interceptors":
                agent_id = _register_agent(api, config, config_path, agent_info)
                if agent_id:
                    print(f"         Registered. (agent #{agent_id})")
                    print(f"         To monitor: obol wrap -- python your_script.py")
                    configured += 1
            elif method == "wrap":
                agent_id = _register_agent(api, config, config_path, agent_info)
                if agent_id:
                    print(f"         Registered. (agent #{agent_id})")
                    print(f"         To monitor: obol wrap -- {slug}")
                    configured += 1
            print()

    # --- Step 5: Ensure archive key exists ---
    from obol.local_log import _get_or_create_archive_key
    archive_key = _get_or_create_archive_key()

    # --- Done ---
    print("  " + "=" * 48)
    print()
    if configured > 0:
        print(f"  Setup complete! Obol is monitoring in audit mode.")
    else:
        print("  Obol is ready (audit mode).")
    if archive_key:
        print()
        print("  Archive key stored at ~/.obol/archive_key - back this up to protect local audit logs")
    print()
    print("  Modes:")
    print("    audit   — Log all actions, detect anomalies (default)")
    print("    alert   — Audit + desktop notifications on threats")
    print("    enforce — Alert + auto-lockdown on critical events")
    print()
    # Determine agent_id for display
    _display_agent_id = config.get("claude_code_agent_id", "")
    print("  Next steps:")
    print("    obol config set enforcement_mode alert   Change mode")
    if _display_agent_id:
        print(f"    obol tail --agent {_display_agent_id}                  View events")
    else:
        print("    obol events                             View events")
    print("    obol status                             Terminal dashboard")
    print("    obol wrap -- <cmd>                      Monitor any command")
    print()
    print("  Dashboard: https://obol.grayowlresearch.com/dashboard")
    print()
    if any(a["slug"] == "claude-code" for a in found):
        print("  If your agent gets locked down (enforce mode only):")
        print("    obol unlock")
        print()


def _register_agent(api, config, config_path, agent_info):
    """Register an agent with Obol (or find existing)."""
    from obol.api import ObolAPIError

    slug = agent_info["slug"]
    config_key = f"{slug.replace('-', '_')}_agent_id"
    agent_id = config.get(config_key)

    if agent_id:
        try:
            api._request("GET", f"/agents/{agent_id}")
            return agent_id
        except ObolAPIError:
            agent_id = None

    if not agent_id:
        try:
            result = api._request("GET", "/agents")
            agents = result.get("agents", [])
            for a in agents:
                if a.get("name") == slug:
                    agent_id = a["id"]
                    break
        except Exception:
            pass

    if not agent_id:
        try:
            result = api.register_agent(
                name=slug,
                platform=agent_info.get("platform", "custom"),
                model=agent_info.get("model"),
                capabilities=agent_info.get("capabilities", []),
            )
            agent_id = result.get("id")
        except ObolAPIError as e:
            print(f"         Warning: {e.detail}")
            return None

    config[config_key] = agent_id
    with open(config_path, "w") as f:
        json.dump(config, f, indent=2)

    return agent_id


def _setup_claude_code(api, config, config_path):
    """Register Claude Code agent and install hooks."""
    from obol.registry import get_agent

    agent_info = get_agent("claude-code")
    agent_id = _register_agent(api, config, config_path, agent_info)
    if not agent_id:
        return None

    # Save with the specific key the hook handler expects
    config["claude_code_agent_id"] = agent_id
    with open(config_path, "w") as f:
        json.dump(config, f, indent=2)

    # Install read-only copy of the hook handler to ~/.obol/handler.py
    config_dir = os.path.expanduser("~/.obol")
    handler_copy_path = os.path.join(config_dir, "handler.py")

    try:
        import obol.hooks.claude_code as hook_module
        handler_source = os.path.abspath(hook_module.__file__)

        # Remove old copy if it exists (may be read-only)
        if os.path.exists(handler_copy_path):
            os.chmod(handler_copy_path, 0o644)
            os.unlink(handler_copy_path)

        shutil.copy2(handler_source, handler_copy_path)
        os.chmod(handler_copy_path, 0o444)
        print("         Hook handler installed (read-only) at ~/.obol/handler.py")
    except Exception as e:
        print(f"         Warning: could not install handler copy: {e}")
        # Fall back to module invocation if copy fails
        handler_copy_path = None

    # Install read-only copy of the gate handler to ~/.obol/gate.py
    gate_copy_path = os.path.join(config_dir, "gate.py")

    try:
        import obol.hooks.gate as gate_module
        gate_source = os.path.abspath(gate_module.__file__)

        if os.path.exists(gate_copy_path):
            os.chmod(gate_copy_path, 0o644)
            os.unlink(gate_copy_path)

        shutil.copy2(gate_source, gate_copy_path)
        os.chmod(gate_copy_path, 0o444)
        print("         Gate handler installed (read-only) at ~/.obol/gate.py")
    except Exception as e:
        print(f"         Warning: could not install gate copy: {e}")
        gate_copy_path = None

    # Install hooks
    settings_path = os.path.expanduser("~/.claude/settings.json")
    from obol.cli._base import quote_path

    if handler_copy_path:
        hook_cmd = f"{quote_path(sys.executable)} {quote_path(handler_copy_path)}"
    else:
        hook_cmd = f"{quote_path(sys.executable)} -m obol.hooks.claude_code"

    if gate_copy_path:
        gate_cmd = f"{quote_path(sys.executable)} {quote_path(gate_copy_path)}"
    else:
        gate_cmd = f"{quote_path(sys.executable)} -m obol.hooks.gate"

    settings = {}
    if os.path.exists(settings_path):
        try:
            with open(settings_path) as f:
                settings = json.load(f)
        except Exception:
            pass

    if "hooks" not in settings:
        settings["hooks"] = {}

    hooks = settings["hooks"]

    obol_hook = {
        "type": "command",
        "command": hook_cmd,
        "timeout": 10,
    }

    obol_gate = {
        "type": "command",
        "command": gate_cmd,
        "timeout": 3,
    }

    hook_events = {
        "PreToolUse": {"matcher": ".*", "hooks": [obol_gate]},
        "PostToolUse": {"matcher": ".*", "hooks": [{**obol_hook, "async": True}]},
        "PostToolUseFailure": {"matcher": ".*", "hooks": [{**obol_hook, "async": True}]},
        "SessionStart": {"hooks": [{**obol_hook, "async": True}]},
        "SessionEnd": {"hooks": [{**obol_hook, "async": True}]},
    }

    # Detect existing obol hooks (old module-based or new handler.py/gate.py-based)
    def _has_obol_hook(hook_list):
        existing_json = json.dumps(hook_list)
        return "obol" in existing_json and (
            "claude_code" in existing_json
            or "handler.py" in existing_json
            or "gate.py" in existing_json
        )

    for event, handler in hook_events.items():
        if event not in hooks:
            hooks[event] = [handler]
        else:
            if not _has_obol_hook(hooks[event]):
                hooks[event].append(handler)
            else:
                # Replace existing obol hook entry with updated command
                for i, entry in enumerate(hooks[event]):
                    entry_json = json.dumps(entry)
                    if "obol" in entry_json and (
                        "claude_code" in entry_json
                        or "handler.py" in entry_json
                        or "gate.py" in entry_json
                    ):
                        hooks[event][i] = handler
                        break

    with open(settings_path, "w") as f:
        json.dump(settings, f, indent=2)

    # Store integrity hash of the installed handler copy for tamper detection
    hash_target = handler_copy_path or None
    if not hash_target:
        # Fallback: hash the source module if copy failed
        try:
            import obol.hooks.claude_code as hook_mod
            hash_target = os.path.abspath(hook_mod.__file__)
        except Exception:
            pass

    if hash_target:
        try:
            with open(hash_target, "rb") as f:
                handler_hash = hashlib.sha256(f.read()).hexdigest()
            config["hook_handler_hash"] = handler_hash
            with open(config_path, "w") as f:
                json.dump(config, f, indent=2)
        except Exception:
            pass  # Non-fatal: integrity check just won't activate

    return agent_id


def _setup_cursor(api, config, config_path):
    """Register Cursor agent and install hooks."""
    from obol.registry import get_agent

    agent_info = get_agent("cursor")
    if not agent_info:
        return None

    agent_id = _register_agent(api, config, config_path, agent_info)
    if not agent_id:
        return None

    # Save with the specific key
    config["cursor_agent_id"] = agent_id
    with open(config_path, "w") as f:
        json.dump(config, f, indent=2)

    # Install hook handler to ~/.obol/cursor_handler.py
    config_dir = os.path.expanduser("~/.obol")
    handler_copy_path = os.path.join(config_dir, "cursor_handler.py")

    try:
        import obol.hooks.cursor as hook_module
        handler_source = os.path.abspath(hook_module.__file__)

        if os.path.exists(handler_copy_path):
            os.chmod(handler_copy_path, 0o644)
            os.unlink(handler_copy_path)

        shutil.copy2(handler_source, handler_copy_path)
        os.chmod(handler_copy_path, 0o444)
        print("         Hook handler installed at ~/.obol/cursor_handler.py")
    except Exception as e:
        print(f"         Warning: could not install handler copy: {e}")
        handler_copy_path = None

    # Install hooks to ~/.cursor/hooks.json
    hooks_path = os.path.expanduser("~/.cursor/hooks.json")
    hooks_dir = os.path.dirname(hooks_path)
    os.makedirs(hooks_dir, exist_ok=True)

    from obol.cli._base import quote_path

    if handler_copy_path:
        hook_cmd = f"{quote_path(sys.executable)} {quote_path(handler_copy_path)}"
    else:
        hook_cmd = f"{quote_path(sys.executable)} -m obol.hooks.cursor"

    hooks = {}
    if os.path.exists(hooks_path):
        try:
            with open(hooks_path) as f:
                hooks = json.load(f)
        except Exception:
            pass

    # Cursor hooks format: {"hookName": [{"command": "...", "timeout": N}]}
    obol_hook = {"command": hook_cmd, "timeout": 10}

    cursor_events = [
        "afterShellExecution",
        "afterFileEdit",
        "afterMCPExecution",
        "beforeShellExecution",
        "beforeFileEdit",
        "beforeMCPExecution",
        "onAgentStart",
        "onAgentEnd",
    ]

    for event in cursor_events:
        if event not in hooks:
            hooks[event] = [obol_hook]
        else:
            # Check if obol hook already exists
            has_obol = any("obol" in json.dumps(h) for h in hooks[event])
            if not has_obol:
                hooks[event].append(obol_hook)
            else:
                # Replace existing obol hook
                for i, h in enumerate(hooks[event]):
                    if "obol" in json.dumps(h):
                        hooks[event][i] = obol_hook
                        break

    with open(hooks_path, "w") as f:
        json.dump(hooks, f, indent=2)

    print("         Hooks installed to ~/.cursor/hooks.json")
    return agent_id


def _setup_windsurf(api, config, config_path):
    """Register Windsurf agent and install hooks."""
    from obol.registry import get_agent

    agent_info = get_agent("windsurf")
    if not agent_info:
        return None

    agent_id = _register_agent(api, config, config_path, agent_info)
    if not agent_id:
        return None

    # Save with the specific key
    config["windsurf_agent_id"] = agent_id
    with open(config_path, "w") as f:
        json.dump(config, f, indent=2)

    # Install hook handler to ~/.obol/windsurf_handler.py
    config_dir = os.path.expanduser("~/.obol")
    handler_copy_path = os.path.join(config_dir, "windsurf_handler.py")

    try:
        import obol.hooks.windsurf as hook_module
        handler_source = os.path.abspath(hook_module.__file__)

        if os.path.exists(handler_copy_path):
            os.chmod(handler_copy_path, 0o644)
            os.unlink(handler_copy_path)

        shutil.copy2(handler_source, handler_copy_path)
        os.chmod(handler_copy_path, 0o444)
        print("         Hook handler installed at ~/.obol/windsurf_handler.py")
    except Exception as e:
        print(f"         Warning: could not install handler copy: {e}")
        handler_copy_path = None

    # Install hooks to ~/.codeium/windsurf/hooks.json
    hooks_path = os.path.expanduser("~/.codeium/windsurf/hooks.json")
    hooks_dir = os.path.dirname(hooks_path)
    os.makedirs(hooks_dir, exist_ok=True)

    from obol.cli._base import quote_path

    if handler_copy_path:
        hook_cmd = f"{quote_path(sys.executable)} {quote_path(handler_copy_path)}"
    else:
        hook_cmd = f"{quote_path(sys.executable)} -m obol.hooks.windsurf"

    hooks = {}
    if os.path.exists(hooks_path):
        try:
            with open(hooks_path) as f:
                hooks = json.load(f)
        except Exception:
            pass

    # Windsurf hooks format: {"hookName": [{"command": "...", "timeout": N}]}
    obol_hook = {"command": hook_cmd, "timeout": 10}

    windsurf_events = [
        "post_run_command",
        "post_write_code",
        "post_mcp_tool_use",
        "pre_run_command",
        "pre_write_code",
        "pre_mcp_tool_use",
        "cascade_start",
        "cascade_end",
    ]

    for event in windsurf_events:
        if event not in hooks:
            hooks[event] = [obol_hook]
        else:
            has_obol = any("obol" in json.dumps(h) for h in hooks[event])
            if not has_obol:
                hooks[event].append(obol_hook)
            else:
                for i, h in enumerate(hooks[event]):
                    if "obol" in json.dumps(h):
                        hooks[event][i] = obol_hook
                        break

    with open(hooks_path, "w") as f:
        json.dump(hooks, f, indent=2)

    print("         Hooks installed to ~/.codeium/windsurf/hooks.json")
    return agent_id
