"""Skill installer — copy SKILL.md + references/ to AI-tool skill directories."""

from __future__ import annotations

import re
import shutil
from pathlib import Path

import click
from rich.console import Console
from rich.table import Table

console = Console()

TOOL_CONFIGS = {
    "claude-code": {
        "user": Path.home() / ".claude" / "skills" / "gsearch-mcp",
        "project": Path(".claude/skills/gsearch-mcp"),
        "description": "Claude Code CLI and Desktop",
    },
    "cursor": {
        "user": Path.home() / ".cursor" / "skills" / "gsearch-mcp",
        "project": Path(".cursor/skills/gsearch-mcp"),
        "description": "Cursor AI editor",
    },
    "agents": {
        "user": Path.home() / ".agents" / "skills" / "gsearch-mcp",
        "project": Path(".agents/skills/gsearch-mcp"),
        "description": "Generic agent skill (Gemini CLI, Codex, and others)",
    },
    "codex": {
        "user": Path.home() / ".agents" / "skills" / "gsearch-mcp",
        "project": Path(".agents/skills/gsearch-mcp"),
        "description": "OpenAI Codex CLI agent",
    },
    "gemini-cli": {
        "user": Path.home() / ".agents" / "skills" / "gsearch-mcp",
        "project": Path(".agents/skills/gsearch-mcp"),
        "description": "Google Gemini CLI",
    },
    "opencode": {
        "user": Path.home() / ".config" / "opencode" / "skills" / "gsearch-mcp",
        "project": Path(".opencode/skills/gsearch-mcp"),
        "description": "OpenCode AI assistant",
    },
    "antigravity": {
        "user": Path.home() / ".gemini" / "antigravity" / "skills" / "gsearch-mcp",
        "project": Path(".agents/skills/gsearch-mcp"),
        "description": "Google Antigravity AI IDE",
    },
    "cline": {
        "user": Path.home() / ".cline" / "skills" / "gsearch-mcp",
        "project": Path(".cline/skills/gsearch-mcp"),
        "description": "Cline CLI terminal agent",
    },
    "openclaw": {
        "user": Path.home() / ".openclaw" / "workspace" / "skills" / "gsearch-mcp",
        "project": Path(".openclaw/workspace/skills/gsearch-mcp"),
        "description": "OpenClaw AI agent framework",
    },
    "cc-claw": {
        "user": Path.home() / ".cc-claw" / "workspace" / "skills" / "gsearch-mcp",
        "project": Path(".cc-claw/workspace/skills/gsearch-mcp"),
        "description": "CC-Claw AI agent framework",
    },
    "other": {
        "project": Path("./gsearch-mcp-skill-export"),
        "description": "Export all formats for manual installation",
    },
}


def _data_dir() -> Path:
    return Path(__file__).resolve().parent.parent / "data"


def _extract_version(skill_md_path: Path) -> str | None:
    """Extract version from SKILL.md YAML frontmatter."""
    if not skill_md_path.is_file():
        return None
    try:
        text = skill_md_path.read_text(encoding="utf-8")
        m = re.search(r'^version:\s*["\']?([^"\'\s]+)', text, re.MULTILINE)
        return m.group(1) if m else None
    except OSError:
        return None


def _get_bundled_version() -> str | None:
    """Get the version of the SKILL.md bundled with this package."""
    return _extract_version(_data_dir() / "SKILL.md")


def _installed_files(target: Path) -> list[str]:
    """Return relative file names installed under target."""
    files = []
    if (target / "SKILL.md").is_file():
        files.append("SKILL.md")
    refs = target / "references"
    if refs.is_dir():
        for f in sorted(refs.iterdir()):
            if f.is_file():
                files.append(f"references/{f.name}")
    return files


def _is_installed(tool_id: str) -> bool:
    cfg = TOOL_CONFIGS[tool_id]
    user_path = cfg.get("user")
    if user_path and (user_path / "SKILL.md").is_file():
        return True
    return False


def _install_to(target: Path) -> None:
    data = _data_dir()
    target.mkdir(parents=True, exist_ok=True)

    shutil.copy2(data / "SKILL.md", target / "SKILL.md")

    refs_src = data / "references"
    if refs_src.is_dir():
        refs_dst = target / "references"
        if refs_dst.exists():
            shutil.rmtree(refs_dst)
        shutil.copytree(refs_src, refs_dst)


@click.group("skill")
def skill_cmd():
    """Manage gsearch skill installation for AI tools."""


@skill_cmd.command("list")
def skill_list():
    """Show available install targets and their status."""
    table = Table(title="Skill Install Targets")
    table.add_column("Tool", style="bold")
    table.add_column("Status")
    table.add_column("Path", style="dim")
    table.add_column("Description", style="dim")

    for tool_id, cfg in TOOL_CONFIGS.items():
        if tool_id == "other":
            table.add_row(tool_id, "-", str(cfg.get("project", "")), cfg["description"])
            continue
        installed = _is_installed(tool_id)
        status = "[green]installed[/green]" if installed else "[dim]not installed[/dim]"
        user_path = cfg.get("user", "")
        table.add_row(tool_id, status, str(user_path), cfg["description"])

    console.print(table)


def _parse_skill_target(value: str) -> list[str]:
    """Parse skill install target supporting comma-separated values."""
    raw_items = [t.strip() for t in value.split(",") if t.strip()]
    if not raw_items:
        raise click.BadParameter("No target specified.")
    valid_keys = list(TOOL_CONFIGS.keys())
    if len(raw_items) == 1 and raw_items[0] == "all":
        return ["all"]
    result: list[str] = []
    for item in raw_items:
        if item in valid_keys:
            result.append(item)
        elif item == "all":
            raise click.BadParameter("'all' cannot be combined with other targets.")
        else:
            raise click.BadParameter(
                f"Unknown target '{item}'. Choose from: {', '.join(valid_keys + ['all'])}"
            )
    return result


@skill_cmd.command("install")
@click.argument("target")
@click.option("--project", is_flag=True, help="Install to project-local path instead of user path.")
def skill_install(target: str, project: bool) -> None:
    """Install gsearch skill to TARGET tool directory.

    Supports individual tools, comma-separated lists, or 'all'.
    Example: gsearch skill install claude-code,cursor,cline
    """
    targets = _parse_skill_target(target)

    if targets == ["all"]:
        if project:
            console.print("[red]--project is not supported with 'all'[/red]")
            raise SystemExit(1)
        _install_all()
        return

    for tool_id in targets:
        cfg = TOOL_CONFIGS[tool_id]

        if project:
            install_path = cfg.get("project")
            if not install_path:
                console.print(f"[red]No project path defined for {tool_id}[/red]")
                raise SystemExit(1)
        else:
            install_path = cfg.get("user")
            if not install_path:
                install_path = cfg.get("project")

        _install_to(install_path)
        new_ver = _get_bundled_version()
        ver_tag = f" (v{new_ver})" if new_ver else ""
        console.print(f"[green]✓ Installed SKILL.md{ver_tag} to {install_path}[/green]")
        for fname in _installed_files(install_path):
            console.print(f"    • {fname}")


@skill_cmd.command("update")
def skill_update():
    """Update all installed skills with the latest version."""
    new_ver = _get_bundled_version()
    updated = []
    for tool_id, cfg in TOOL_CONFIGS.items():
        if tool_id == "other":
            continue
        user_path = cfg.get("user")
        if not user_path or not _is_installed(tool_id):
            continue

        old_ver = _extract_version(user_path / "SKILL.md")
        _install_to(user_path)
        updated.append(tool_id)

        # Version transition header
        if old_ver and new_ver and old_ver != new_ver:
            console.print(f"\n[bold]Updating {tool_id} (user): v{old_ver} → v{new_ver}[/bold]")
        elif new_ver:
            console.print(f"\n[bold]Updating {tool_id} (user): v{new_ver}[/bold]")
        else:
            console.print(f"\n[bold]Updating {tool_id} (user)[/bold]")

        ver_tag = f" (v{new_ver})" if new_ver else ""
        console.print(f"[green]✓ Installed SKILL.md{ver_tag} to {user_path}[/green]")
        for fname in _installed_files(user_path):
            console.print(f"    • {fname}")

    if not updated:
        console.print("[dim]No installed skills found. Run 'gsearch skill install <target>' first.[/dim]")


def _install_all() -> None:
    """Install gsearch skill to all supported tools (user scope)."""
    new_ver = _get_bundled_version()
    console.print("[bold]Installing gsearch skill to all tools...[/bold]\n")
    installed_count = 0
    seen_paths: set[str] = set()

    for tool_id, cfg in TOOL_CONFIGS.items():
        if tool_id == "other":
            continue
        user_path = cfg.get("user")
        if not user_path:
            continue
        # Avoid installing twice to the same path (e.g. agents/codex/gemini-cli share a path)
        path_key = str(user_path)
        if path_key in seen_paths:
            continue
        seen_paths.add(path_key)

        _install_to(user_path)
        installed_count += 1

        ver_tag = f" (v{new_ver})" if new_ver else ""
        console.print(f"[green]✓ Installed SKILL.md{ver_tag} to {user_path}[/green]")
        for fname in _installed_files(user_path):
            console.print(f"    • {fname}")

    console.print(f"\n[bold]Summary:[/bold] installed to {installed_count} locations")
