"""Obol hook handler for Windsurf (Codeium) editor.

Receives tool use events via stdin JSON from Windsurf Cascade hooks,
sends them to Obol API as audit events. Zero external dependencies.

Windsurf hooks format: JSON in, exit code out (0=allow, 2=block).
Config: ~/.codeium/windsurf/hooks.json

Usage in Windsurf hooks config:
    {
      "hooks": {
        "post_run_command": [{"command": "python3 ~/.obol/windsurf_handler.py"}],
        "post_write_code": [{"command": "python3 ~/.obol/windsurf_handler.py"}],
        "post_mcp_tool_use": [{"command": "python3 ~/.obol/windsurf_handler.py"}]
      }
    }
"""
import datetime
import hashlib
import json
import os
import re
import sys
import urllib.request
import urllib.error


def _load_config():
    """Load API key and agent_id from ~/.obol/config.json."""
    config_path = os.path.expanduser("~/.obol/config.json")
    try:
        with open(config_path) as f:
            return json.load(f)
    except Exception:
        return {}


def _scrub_secrets(text):
    """Redact sensitive patterns from text."""
    if not text:
        return text
    text = re.sub(r'Bearer\s+\S+', 'Bearer [REDACTED]', text)
    text = re.sub(r'ob_(live|test)_[a-f0-9]+', r'ob_\1_[REDACTED]', text)
    text = re.sub(
        r'(api[_-]?key|token|secret|password|authorization)\s*[=:]\s*\S+',
        r'\1=[REDACTED]',
        text,
        flags=re.IGNORECASE,
    )
    return text


def _content_hash(data):
    """Hash content for audit trail without storing content."""
    if not data:
        return None
    raw = json.dumps(data, sort_keys=True).encode()
    return hashlib.sha256(raw).hexdigest()[:16]


def _detect_obfuscation(command):
    """Check a command for obfuscation patterns."""
    if not command:
        return []
    patterns = []
    if re.search(r'\bbase64\s+(-d|--decode)', command):
        patterns.append("base64")
    if re.search(r'(?:^|[;&|])\s*eval\s', command, re.MULTILINE):
        patterns.append("eval")
    m = re.search(r'[A-Za-z0-9+/]{80,}={0,2}', command)
    if m and re.search(r'[a-z]', m.group()) and re.search(r'[A-Z]', m.group()) and re.search(r'[0-9]', m.group()):
        patterns.append("encoded_string")
    if len(re.findall(r'\$(?![(\s])', command)) >= 6:
        patterns.append("variable_expansion")
    return patterns


def _map_windsurf_event(data):
    """Map Windsurf Cascade hook event to Obol event format.

    Windsurf uses:
    - agent_action_name: event type
    - tool_info: event-specific data
    """
    action_name = data.get("agent_action_name", "")
    tool_info = data.get("tool_info", {})

    # Windsurf event types
    if action_name == "post_run_command":
        command = tool_info.get("command", "")
        return {
            "action": "Bash",
            "target": _scrub_secrets(command[:2000]) if command else None,
            "result": "success" if tool_info.get("exit_code", 0) == 0 else "error",
            "obfuscation": _detect_obfuscation(command),
            "metadata": {
                "exit_code": tool_info.get("exit_code"),
                "working_dir": tool_info.get("working_directory"),
            }
        }

    elif action_name == "pre_run_command":
        command = tool_info.get("command", "")
        return {
            "action": "Bash.pre",
            "target": _scrub_secrets(command[:2000]) if command else None,
            "result": "pending",
            "obfuscation": _detect_obfuscation(command),
        }

    elif action_name == "post_write_code":
        file_path = tool_info.get("file_path", "")
        return {
            "action": "Write",
            "target": file_path,
            "result": "success",
            "metadata": {
                "language": tool_info.get("language"),
            }
        }

    elif action_name == "pre_write_code":
        return {
            "action": "Write.pre",
            "target": tool_info.get("file_path", ""),
            "result": "pending",
        }

    elif action_name == "post_read_code":
        return {
            "action": "Read",
            "target": tool_info.get("file_path", ""),
            "result": "success",
        }

    elif action_name == "pre_read_code":
        return {
            "action": "Read.pre",
            "target": tool_info.get("file_path", ""),
            "result": "pending",
        }

    elif action_name == "post_mcp_tool_use":
        tool_name = tool_info.get("tool_name", "mcp_tool")
        return {
            "action": f"MCP.{tool_name}",
            "target": _content_hash(tool_info.get("tool_input")),
            "result": "success" if tool_info.get("success", True) else "error",
            "metadata": {
                "mcp_server": tool_info.get("server_name"),
                "mcp_tool": tool_name,
            }
        }

    elif action_name == "pre_mcp_tool_use":
        tool_name = tool_info.get("tool_name", "mcp_tool")
        return {
            "action": f"MCP.{tool_name}.pre",
            "target": _content_hash(tool_info.get("tool_input")),
            "result": "pending",
        }

    elif action_name == "post_cascade_response":
        return {
            "action": "agent.response",
            "target": None,
            "result": "success",
        }

    elif action_name == "post_setup_worktree":
        return {
            "action": "git.worktree",
            "target": tool_info.get("worktree_path"),
            "result": "success",
        }

    elif action_name == "pre_user_prompt":
        return {
            "action": "prompt.received",
            "target": None,  # Don't log prompt content
            "result": "success",
        }

    else:
        return {
            "action": action_name or "unknown",
            "target": None,
            "result": "success",
        }


def main():
    # Read event from stdin (Windsurf sends JSON)
    try:
        data = json.load(sys.stdin)
    except Exception:
        # Return success exit code so Windsurf continues
        sys.exit(0)

    config = _load_config()

    api_key = os.environ.get("OBOL_API_KEY") or config.get("api_key", "")
    api_url = os.environ.get("OBOL_API_URL") or config.get("api_url", "https://obol.grayowlresearch.com/v1")
    agent_id = config.get("windsurf_agent_id")

    if not api_key or not agent_id:
        # Not configured - return success so Windsurf continues
        sys.exit(0)

    # Map Windsurf event to Obol format
    mapped = _map_windsurf_event(data)

    session_id = data.get("trajectory_id") or data.get("execution_id") or "windsurf-session"

    event = {
        "agent_id": agent_id,
        "session_id": session_id[:200],
        "action": mapped["action"][:100],
        "target": mapped.get("target"),
        "target_hash": _content_hash(data),
        "result": mapped["result"],
        "detected_patterns": mapped.get("obfuscation", []),
        "metadata": {
            "platform": "windsurf",
            "hook_event": data.get("agent_action_name", ""),
            "timestamp": data.get("timestamp", ""),
            **(mapped.get("metadata") or {}),
        },
    }

    # Send to Obol API
    body = json.dumps(event).encode()
    req = urllib.request.Request(
        f"{api_url.rstrip('/')}/events",
        data=body,
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
            "User-Agent": "obol-sdk/0.1.0",
        },
        method="POST",
    )

    try:
        urllib.request.urlopen(req, timeout=10)
    except Exception:
        # Log to local fallback
        try:
            failed_path = os.path.expanduser("~/.obol/failed_events.jsonl")
            event["attempted_at"] = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None).isoformat() + "Z"
            with open(failed_path, "a") as f:
                f.write(json.dumps(event) + "\n")
        except Exception:
            pass

    # Return success exit code so Windsurf continues
    sys.exit(0)


if __name__ == "__main__":
    main()
