"""
vr setup command - Set up the VoiceRun CLI environment.
"""

import json
import platform
import re
import shutil
import subprocess
import sys
from pathlib import Path

import questionary
import typer

from vr_cli.utils.utils import (
    console,
    print_success,
    print_error,
    print_info,
    print_warning,
)
from vr_cli.utils.config import get_context_urls, save_setup_targets
from vr_cli.utils.helm import is_helm_available

TARGET_PATHS = {
    "claude-code": Path.home() / ".claude" / "skills",
    "codex": Path.home() / ".agents" / "skills",
    "openclaw": Path.home() / ".openclaw" / "workspace" / "skills",
}


def is_uv_available() -> bool:
    """Check if uv is available in PATH."""
    return shutil.which("uv") is not None


def get_uv_install_command() -> tuple[list[str], str]:
    """Get the appropriate uv install command for the current platform.

    Returns:
        Tuple of (command_list, description) or (None, error_message)
    """
    system = platform.system().lower()

    if system == "darwin":
        if shutil.which("brew"):
            return (["brew", "install", "uv"], "Installing uv via Homebrew...")
        else:
            return (
                ["bash", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"],
                "Installing uv via official script..."
            )

    elif system == "linux":
        return (
            ["bash", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"],
            "Installing uv via official script..."
        )

    elif system == "windows":
        if shutil.which("choco"):
            return (["choco", "install", "uv", "-y"], "Installing uv via Chocolatey...")
        else:
            return (
                ["powershell", "-c", "irm https://astral.sh/uv/install.ps1 | iex"],
                "Installing uv via official script..."
            )

    return (None, f"Unsupported platform: {system}")


def install_uv() -> bool:
    """Install uv package manager.

    Returns:
        True if installation succeeded, False otherwise.
    """
    if is_uv_available():
        print_success("uv is already installed")
        try:
            result = subprocess.run(
                ["uv", "--version"],
                capture_output=True,
                text=True,
                check=True
            )
            print_info(f"Version: {result.stdout.strip()}")
        except subprocess.CalledProcessError:
            pass
        return True

    command, message = get_uv_install_command()

    if command is None:
        print_error(message)
        return False

    print_info(message)

    try:
        subprocess.run(command, check=True, text=True)

        if is_uv_available():
            print_success("uv installed successfully!")
            try:
                version_result = subprocess.run(
                    ["uv", "--version"],
                    capture_output=True,
                    text=True,
                    check=True
                )
                print_info(f"Version: {version_result.stdout.strip()}")
            except subprocess.CalledProcessError:
                pass
            return True
        else:
            print_error("uv installation completed but 'uv' command not found in PATH")
            print_info("You may need to restart your terminal or add uv to your PATH")
            return False

    except subprocess.CalledProcessError as e:
        print_error(f"Failed to install uv: {e}")
        return False
    except FileNotFoundError as e:
        print_error(f"Command not found: {e}")
        return False


def get_helm_install_command() -> tuple[list[str], str]:
    """Get the appropriate Helm install command for the current platform.

    Returns:
        Tuple of (command_list, description) or (None, error_message)
    """
    system = platform.system().lower()

    if system == "darwin":
        # macOS - prefer Homebrew
        if shutil.which("brew"):
            return (["brew", "install", "helm"], "Installing Helm via Homebrew...")
        else:
            return (None, "Homebrew not found. Please install Homebrew first: https://brew.sh")

    elif system == "linux":
        # Linux - try snap, apt, or script
        if shutil.which("snap"):
            return (["sudo", "snap", "install", "helm", "--classic"], "Installing Helm via Snap...")
        elif shutil.which("apt-get"):
            # For Debian/Ubuntu, use the official Helm install script
            return (
                ["bash", "-c", "curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"],
                "Installing Helm via official script..."
            )
        elif shutil.which("yum"):
            return (
                ["bash", "-c", "curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"],
                "Installing Helm via official script..."
            )
        else:
            return (
                ["bash", "-c", "curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"],
                "Installing Helm via official script..."
            )

    elif system == "windows":
        # Windows - prefer Chocolatey or Scoop
        if shutil.which("choco"):
            return (["choco", "install", "kubernetes-helm", "-y"], "Installing Helm via Chocolatey...")
        elif shutil.which("scoop"):
            return (["scoop", "install", "helm"], "Installing Helm via Scoop...")
        else:
            return (None, "Please install Chocolatey or Scoop first, or download Helm manually from https://helm.sh")

    return (None, f"Unsupported platform: {system}")


def install_helm() -> bool:
    """Install Helm CLI.

    Returns:
        True if installation succeeded, False otherwise.
    """
    if is_helm_available():
        print_success("Helm is already installed")
        # Show version
        try:
            result = subprocess.run(
                ["helm", "version", "--short"],
                capture_output=True,
                text=True,
                check=True
            )
            print_info(f"Version: {result.stdout.strip()}")
        except subprocess.CalledProcessError:
            pass
        return True

    command, message = get_helm_install_command()

    if command is None:
        print_error(message)
        return False

    print_info(message)

    try:
        # Run the installation command
        result = subprocess.run(
            command,
            check=True,
            text=True,
        )

        # Verify installation
        if is_helm_available():
            print_success("Helm installed successfully!")
            # Show version
            try:
                version_result = subprocess.run(
                    ["helm", "version", "--short"],
                    capture_output=True,
                    text=True,
                    check=True
                )
                print_info(f"Version: {version_result.stdout.strip()}")
            except subprocess.CalledProcessError:
                pass
            return True
        else:
            print_error("Helm installation completed but 'helm' command not found in PATH")
            print_info("You may need to restart your terminal or add Helm to your PATH")
            return False

    except subprocess.CalledProcessError as e:
        print_error(f"Failed to install Helm: {e}")
        return False
    except FileNotFoundError as e:
        print_error(f"Command not found: {e}")
        return False


def install_skills(targets: list[str]) -> bool:
    """Install skills to the specified targets.

    Each skill is a subdirectory containing a SKILL.md file. The entire
    directory is copied into each target path.

    Args:
        targets: List of target keys from TARGET_PATHS (e.g. ["claude-code", "codex"]).

    Returns:
        True if at least one skill was installed, False otherwise.
    """
    skills_src = Path(__file__).parent.parent / "skills"

    if not skills_src.exists():
        print_warning("Skills directory not found, skipping")
        return False

    # Discover skill directories (those containing SKILL.md)
    skill_dirs = [d for d in skills_src.iterdir() if d.is_dir() and (d / "SKILL.md").exists()]
    if not skill_dirs:
        print_warning("No skill directories found, skipping")
        return False

    valid_targets = {t: TARGET_PATHS[t] for t in targets if t in TARGET_PATHS}
    if not valid_targets:
        print_warning(f"No valid targets specified: {targets}")
        return False

    for target_name, target_path in valid_targets.items():
        target_path.mkdir(parents=True, exist_ok=True)
        for skill_dir in skill_dirs:
            dest = target_path / skill_dir.name
            if dest.exists():
                shutil.rmtree(dest)
            shutil.copytree(skill_dir, dest)
            print_success(f"Installed skill: /{skill_dir.name} → {target_name}")

    return True


MCP_TARGETS = {
    "claude-code": Path.home() / ".claude.json",
    "cursor": Path.home() / ".cursor" / "mcp.json",
    "codex": Path.home() / ".codex" / "config.toml",
    "windsurf": Path.home() / ".codeium" / "windsurf" / "mcp_config.json",
}


def _build_mcp_entry(target_name: str, mcp_url: str) -> dict:
    """Build the MCP server entry for the given target."""
    if target_name == "claude-code":
        return {"type": "http", "url": mcp_url}
    elif target_name == "cursor":
        return {"url": mcp_url}
    elif target_name == "windsurf":
        return {"serverUrl": mcp_url}
    # codex is handled separately (TOML format)
    return {"url": mcp_url}


def _install_mcp_codex(config_path: Path, mcp_url: str) -> None:
    """Install MCP config for Codex (TOML format)."""
    config_path.parent.mkdir(parents=True, exist_ok=True)

    existing_content = ""
    if config_path.exists():
        existing_content = config_path.read_text()

    # Check if voicerun MCP entry already exists
    if "[mcp_servers.voicerun]" in existing_content:
        # Replace existing entry
        pattern = r'\[mcp_servers\.voicerun\]\s*\nurl\s*=\s*"[^"]*"'
        replacement = f'[mcp_servers.voicerun]\nurl = "{mcp_url}"'
        updated = re.sub(pattern, replacement, existing_content)
        config_path.write_text(updated)
    else:
        # Append new entry
        separator = "\n" if existing_content and not existing_content.endswith("\n") else ""
        extra_newline = "\n" if existing_content.strip() else ""
        config_path.write_text(
            existing_content + separator + extra_newline
            + f'[mcp_servers.voicerun]\nurl = "{mcp_url}"\n'
        )


def install_mcp_server(targets: list[str]) -> bool:
    """Install MCP server configuration to the specified targets.

    Args:
        targets: List of target keys from MCP_TARGETS (e.g. ["claude-code"]).

    Returns:
        True if at least one target was configured, False otherwise.
    """
    api_url, _ = get_context_urls()
    mcp_url = f"{api_url}/v1/mcp"

    valid_targets = {t: MCP_TARGETS[t] for t in targets if t in MCP_TARGETS}
    if not valid_targets:
        print_warning(f"No valid MCP targets specified: {targets}")
        return False

    success = False
    for target_name, settings_path in valid_targets.items():
        try:
            if target_name == "codex":
                _install_mcp_codex(settings_path, mcp_url)
            else:
                # JSON-based config files
                existing = {}
                if settings_path.exists():
                    existing = json.loads(settings_path.read_text())

                if "mcpServers" not in existing:
                    existing["mcpServers"] = {}
                existing["mcpServers"]["voicerun"] = _build_mcp_entry(target_name, mcp_url)

                settings_path.parent.mkdir(parents=True, exist_ok=True)
                settings_path.write_text(json.dumps(existing, indent=2) + "\n")

            print_success(f"MCP server configured for {target_name} → {settings_path}")
            success = True
        except (json.JSONDecodeError, IOError) as e:
            print_error(f"Failed to configure MCP for {target_name}: {e}")

    return success


def setup(
    skip_uv: bool = typer.Option(
        False,
        "--skip-uv",
        help="Skip uv installation.",
    ),
    skip_helm: bool = typer.Option(
        False,
        "--skip-helm",
        help="Skip Helm installation.",
    ),
    skip_skills: bool = typer.Option(
        False,
        "--skip-skills",
        help="Skip skills installation.",
    ),
    skip_mcp: bool = typer.Option(
        False,
        "--skip-mcp",
        help="Skip MCP server configuration.",
    ),
    claude_code: bool = typer.Option(
        False,
        "--claude-code",
        help="Install skills to Claude Code only.",
    ),
    codex: bool = typer.Option(
        False,
        "--codex",
        help="Install skills to Codex CLI only.",
    ),
    openclaw: bool = typer.Option(
        False,
        "--openclaw",
        help="Install skills to OpenClaw only.",
    ),
    cursor: bool = typer.Option(
        False,
        "--cursor",
        help="Configure MCP for Cursor only.",
    ),
    windsurf: bool = typer.Option(
        False,
        "--windsurf",
        help="Configure MCP for Windsurf only.",
    ),
):
    """Set up the VoiceRun CLI environment.

    This command installs required dependencies:
    - uv (for Python package management)
    - Helm CLI (for template rendering)
    - Skills (for AI-assisted workflows)
    """
    console.print("[bold]Setting up VoiceRun CLI...[/bold]")
    console.print()

    all_success = True
    installed_skill_targets = []
    installed_mcp_targets = []

    # Install uv
    if not skip_uv:
        console.print("[bold]1. uv (Python package manager)[/bold]")
        if not install_uv():
            all_success = False
            print_error("uv is required for running tests")
    else:
        print_info("Skipping uv installation (--skip-uv)")

    console.print()

    # Install Helm
    if not skip_helm:
        console.print("[bold]2. Helm CLI[/bold]")
        if not install_helm():
            all_success = False
            print_warning("Helm is optional but required for template rendering")
    else:
        print_info("Skipping Helm installation (--skip-helm)")

    console.print()

    # Install skills
    if not skip_skills:
        console.print("[bold]3. Skills[/bold]")

        # Determine targets from flags
        targets = []
        if claude_code:
            targets.append("claude-code")
        if codex:
            targets.append("codex")
        if openclaw:
            targets.append("openclaw")

        # If no flags, prompt interactively or use default
        if not targets:
            if sys.stdin.isatty():
                try:
                    selected = questionary.checkbox(
                        "Select skill install targets:",
                        choices=[
                            questionary.Choice("Claude Code (~/.claude/skills/)", value="claude-code", checked=True),
                            questionary.Choice("Codex CLI (~/.agents/skills/)", value="codex"),
                            questionary.Choice("OpenClaw (~/.openclaw/workspace/skills/)", value="openclaw"),
                        ],
                    ).ask()
                    if selected is None:
                        # User pressed Ctrl+C
                        print_info("Skipping skills installation")
                        targets = []
                    else:
                        targets = selected
                except KeyboardInterrupt:
                    print_info("Skipping skills installation")
                    targets = []
            else:
                targets = ["claude-code"]

        if targets:
            if not install_skills(targets):
                print_warning("Skills installation had issues")
            installed_skill_targets = targets
        else:
            print_info("No targets selected, skipping skills installation")
    else:
        print_info("Skipping skills installation (--skip-skills)")

    console.print()

    # Configure MCP server
    if not skip_mcp:
        console.print("[bold]4. MCP Server[/bold]")

        # Determine MCP targets from flags
        mcp_targets = []
        if claude_code:
            mcp_targets.append("claude-code")
        if cursor:
            mcp_targets.append("cursor")
        if codex:
            mcp_targets.append("codex")
        if windsurf:
            mcp_targets.append("windsurf")

        # If no flags, prompt interactively or use default
        if not mcp_targets:
            if sys.stdin.isatty():
                try:
                    selected = questionary.checkbox(
                        "Select MCP server install targets:",
                        choices=[
                            questionary.Choice("Claude Code (~/.claude.json)", value="claude-code", checked=True),
                            questionary.Choice("Cursor (~/.cursor/mcp.json)", value="cursor"),
                            questionary.Choice("Codex (~/.codex/config.toml)", value="codex"),
                            questionary.Choice("Windsurf (~/.codeium/windsurf/mcp_config.json)", value="windsurf"),
                        ],
                    ).ask()
                    if selected is None:
                        print_info("Skipping MCP server configuration")
                        mcp_targets = []
                    else:
                        mcp_targets = selected
                except KeyboardInterrupt:
                    print_info("Skipping MCP server configuration")
                    mcp_targets = []
            else:
                mcp_targets = ["claude-code"]

        if mcp_targets:
            if not install_mcp_server(mcp_targets):
                print_warning("MCP server configuration had issues")
            installed_mcp_targets = mcp_targets
        else:
            print_info("No targets selected, skipping MCP server configuration")
    else:
        print_info("Skipping MCP server configuration (--skip-mcp)")

    console.print()

    # Persist selected targets for vr --update
    if installed_skill_targets or installed_mcp_targets:
        save_setup_targets(installed_skill_targets, installed_mcp_targets)

    if all_success:
        print_success("Setup complete!")
    else:
        print_warning("Setup completed with warnings")

    console.print()
    console.print("[dim]Next steps:[/dim]")
    console.print("  1. Run [cyan]vr signin[/cyan] to authenticate")
    console.print("  2. Run [cyan]vr init <project-name>[/cyan] to create a new project")
