"""CLI entry point for Staso tooling.

Usage:
    staso setup --target claude-code --api-key ak_... [options]
    staso setup-claude-code --api-key ak_... [options]  # deprecated alias
"""
from __future__ import annotations

import argparse
import json
import os
import sys
import warnings
from dataclasses import dataclass
from pathlib import Path


# -- agent registry ------------------------------------------------------------


@dataclass(frozen=True)
class AgentConfig:
    """Configuration for a CLI AI agent's hook integration."""

    name: str  # CLI flag value (e.g. "claude-code")
    module: str  # Python module under staso.* (e.g. "claude_code")
    display_name: str
    hook_events: tuple[str, ...]
    events_with_matcher: tuple[str, ...]
    global_settings: str  # path relative to $HOME
    project_settings: str  # path relative to cwd


CLAUDE_CODE = AgentConfig(
    name="claude-code",
    module="claude_code",
    display_name="Claude Code",
    hook_events=(
        "SessionStart",
        "UserPromptSubmit",
        "PreToolUse",
        "PostToolUse",
        "PostToolUseFailure",
        "Stop",
        "StopFailure",
        "SubagentStart",
        "SubagentStop",
        "SessionEnd",
    ),
    events_with_matcher=("PreToolUse", "PostToolUse", "PostToolUseFailure"),
    global_settings=".claude/settings.json",
    project_settings=".claude/settings.local.json",
)

_AGENTS: dict[str, AgentConfig] = {
    "claude-code": CLAUDE_CODE,
}


def get_agent(name: str) -> AgentConfig | None:
    return _AGENTS.get(name)


# -- kept for backward compat (imported by hook.py auto-repair) ---------------

_HOOK_EVENTS = CLAUDE_CODE.hook_events
_HOOK_EVENTS_WITH_MATCHER = CLAUDE_CODE.events_with_matcher


# -- hooks config builder -----------------------------------------------------


def _hook_entry(command: str, with_matcher: bool = False) -> dict:
    hook: dict = {"type": "command", "command": command}
    if with_matcher:
        return {"matcher": ".*", "hooks": [hook]}
    return {"hooks": [hook]}


def _build_hooks_config(command: str, agent: AgentConfig | None = None) -> dict:
    cfg = agent or CLAUDE_CODE
    hooks: dict = {}
    for event in cfg.hook_events:
        hooks[event] = [_hook_entry(command, with_matcher=event in cfg.events_with_matcher)]
    return hooks


def _merge_settings(existing: dict, new_hooks: dict, new_env: dict) -> dict:
    """Merge hook and env entries into existing settings, preserving unrelated keys."""
    result = dict(existing)

    existing_hooks = result.get("hooks", {})
    for event, entries in new_hooks.items():
        current = [
            e for e in existing_hooks.get(event, [])
            if not _is_staso_hook(e)
        ]
        current.extend(entries)
        existing_hooks[event] = current
    result["hooks"] = existing_hooks

    existing_env = result.get("env", {})
    existing_env.update(new_env)
    result["env"] = existing_env

    return result


def _is_staso_hook(entry: dict) -> bool:
    """Return True if this hook entry was written by Staso setup."""
    if "hooks" in entry:
        return any("staso." in h.get("command", "") for h in entry.get("hooks", []))
    return "staso." in entry.get("command", "")


# -- setup command -------------------------------------------------------------


def _resolve_settings_path(agent: AgentConfig, scope: str) -> Path:
    if scope == "global":
        return Path.home() / agent.global_settings
    return Path(agent.project_settings)


def cmd_setup(args: argparse.Namespace) -> int:
    """Write hook configuration for a CLI agent to the chosen settings file."""
    agent_name = getattr(args, "target", None) or "claude-code"
    agent = get_agent(agent_name)
    if agent is None:
        available = ", ".join(sorted(_AGENTS.keys()))
        print(f"error: unknown agent '{agent_name}'. Available: {available}", file=sys.stderr)
        return 1

    api_key = args.api_key or os.environ.get("STASO_API_KEY", "")
    if not api_key:
        print(
            "error: --api-key is required (or set STASO_API_KEY env var)",
            file=sys.stderr,
        )
        return 1

    python = sys.executable
    command = f"{python} -m staso.{agent.module}"

    hooks = _build_hooks_config(command, agent)
    env_vars: dict[str, str] = {"STASO_API_KEY": api_key}
    if args.base_url:
        env_vars["STASO_BASE_URL"] = args.base_url
    if args.workspace:
        env_vars["STASO_WORKSPACE_SLUG"] = args.workspace
    if args.environment:
        env_vars["STASO_ENVIRONMENT"] = args.environment
    if args.agent_id:
        env_vars["STASO_AGENT_ID"] = args.agent_id

    settings_path = _resolve_settings_path(agent, args.scope)

    settings_path.parent.mkdir(parents=True, exist_ok=True)
    existing: dict = {}
    if settings_path.exists():
        try:
            existing = json.loads(settings_path.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError):
            print(
                f"warning: could not read existing {settings_path}, starting fresh",
                file=sys.stderr,
            )

    merged = _merge_settings(existing, hooks, env_vars)
    settings_path.write_text(json.dumps(merged, indent=2), encoding="utf-8")

    scope_label = f"~/{agent.global_settings}" if args.scope == "global" else agent.project_settings
    print(f"Staso {agent.display_name} hooks configured in {scope_label}")
    print(f"  Python: {python}")
    print(f"  API key: {api_key[:8]}...")
    if args.base_url:
        print(f"  Backend: {args.base_url}")
    print()
    print(f"Start a {agent.display_name} conversation to begin tracing.")
    return 0


# kept for backward compat
def cmd_setup_claude_code(args: argparse.Namespace) -> int:
    """Deprecated: use `staso setup --target claude-code` instead."""
    args.target = "claude-code"
    return cmd_setup(args)


# -- parser --------------------------------------------------------------------


def _add_setup_args(parser: argparse.ArgumentParser) -> None:
    """Add common setup arguments to a parser."""
    parser.add_argument("--api-key", metavar="KEY", help="Staso API key (ak_...)")
    parser.add_argument("--base-url", metavar="URL", help="Staso backend URL")
    parser.add_argument("--workspace", metavar="SLUG", help="Workspace slug")
    parser.add_argument("--environment", metavar="ENV", default="production", help="Environment name (default: production)")
    parser.add_argument("--agent-id", metavar="ID", help="Agent ID registered in Staso")
    parser.add_argument(
        "--scope",
        choices=["global", "project"],
        default="global",
        help="Write to global or project settings",
    )


def _build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="staso",
        description="Staso CLI",
    )
    sub = parser.add_subparsers(dest="command")

    # Primary: staso setup --target <name>
    setup = sub.add_parser(
        "setup",
        help="Configure hooks for a CLI AI agent to send traces to Staso",
    )
    setup.add_argument(
        "--target",
        choices=sorted(_AGENTS.keys()),
        default="claude-code",
        help="Which CLI tool to configure (default: claude-code)",
    )
    _add_setup_args(setup)

    # Deprecated alias: staso setup-claude-code
    setup_cc = sub.add_parser(
        "setup-claude-code",
        help="(deprecated) Use 'staso setup --target claude-code' instead",
    )
    _add_setup_args(setup_cc)

    return parser


def main() -> None:
    parser = _build_parser()
    args = parser.parse_args()

    if args.command == "setup":
        sys.exit(cmd_setup(args))
    elif args.command == "setup-claude-code":
        warnings.warn(
            "setup-claude-code is deprecated, use: staso setup --target claude-code",
            DeprecationWarning,
            stacklevel=1,
        )
        sys.exit(cmd_setup_claude_code(args))
    else:
        parser.print_help()
        sys.exit(0)
