"""Plugins management command for HexDAG CLI."""

import contextlib
from enum import StrEnum
from typing import Annotated, Any

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

app = typer.Typer()
console = Console()


class OutputFormat(StrEnum):
    """Output format options."""

    TABLE = "table"
    JSON = "json"
    YAML = "yaml"


@app.command("list")
def list_plugins(
    format: Annotated[
        OutputFormat | None,
        typer.Option(
            "--format",
            "-f",
            help="Output format",
        ),
    ] = None,
) -> None:
    """List all available plugins and adapters."""
    if format is None:
        format = OutputFormat.TABLE

    # Check available extras
    available_plugins = _get_available_plugins()

    if format == OutputFormat.JSON:
        console.print_json(data=available_plugins)
    elif format == OutputFormat.YAML:
        import yaml

        console.print(yaml.dump(available_plugins, default_flow_style=False))
    else:
        # Table format
        table = Table(title="Available Plugins", show_header=True, header_style="bold magenta")
        table.add_column("Plugin", style="cyan", no_wrap=True)
        table.add_column("Namespace", style="green")
        table.add_column("Status", style="yellow")
        table.add_column("Capabilities", style="white")

        for plugin in available_plugins:
            status = "✓ Installed" if plugin["installed"] else "✗ Not installed"
            table.add_row(
                str(plugin["name"]),
                str(plugin["namespace"]),
                status,
                ", ".join(str(c) for c in plugin["capabilities"]),
            )

        console.print(table)


@app.command("check")
def check_plugins() -> None:
    """Check plugin dependencies and suggest installation commands."""
    console.print("[bold]Checking plugin dependencies...[/bold]\n")

    checks = _check_dependencies()
    has_missing = False

    for check in checks:
        if check["status"] == "ok":
            console.print(f"✓ [green]{check['name']}[/green] - OK")
        elif check["status"] == "missing":
            has_missing = True
            console.print(f"✗ [red]{check['name']}[/red] - Missing")
            if check.get("install_hint"):
                console.print(f"  → Install with: [yellow]{check['install_hint']}[/yellow]")
        elif check["status"] == "optional":
            console.print(f"○ [yellow]{check['name']}[/yellow] - Optional")
            if check.get("install_hint"):
                console.print(f"  → Install with: [dim]{check['install_hint']}[/dim]")

    if not has_missing:
        console.print("\n[green]All required dependencies are installed![/green]")
    else:
        console.print(
            "\n[yellow]Some dependencies are missing. See installation hints above.[/yellow]"
        )


@app.command("install")
def install_plugin(
    plugin_name: str = typer.Argument(
        ...,
        help="Plugin name or extra to install (e.g., 'openai', 'viz')",
    ),
    use_uv: bool = typer.Option(
        True,
        "--uv/--pip",
        help="Prefer uv when available; use --pip to force pip",
    ),
    dry_run: bool = typer.Option(
        False,
        "--dry-run",
        help="Show what would be installed without actually installing",
    ),
    editable: bool = typer.Option(
        False,
        "--editable",
        "-e",
        help="Install in editable/development mode",
    ),
) -> None:
    """Install a plugin or adapter (wrapper around package manager).

    Raises
    ------
    typer.Exit
        If installation fails or plugin not found
    """
    # Map plugin names to extras
    plugin_map = {
        "openai": "adapters-openai",
        "anthropic": "adapters-anthropic",
        "viz": "viz",
        "visualization": "viz",
        "cli": "cli",
        "all": "all",
    }

    extra = plugin_map.get(plugin_name, plugin_name)

    # Detect package manager
    import shutil
    import subprocess  # nosec B404 - Required for package installation
    from pathlib import Path

    # Determine which package manager to use
    has_uv = shutil.which("uv") is not None
    use_uv_final = use_uv and has_uv

    if use_uv and not has_uv:
        console.print("[yellow]Warning: uv requested but not found. Using pip instead.[/yellow]")
        use_uv_final = False

    if use_uv_final:
        if editable:
            # For editable install with uv, need to install from current directory
            if Path("pyproject.toml").exists():
                cmd_list = ["uv", "pip", "install", "-e", f".[{extra}]"]
                cmd_str = f"uv pip install -e .[{extra}]"
            else:
                console.print(
                    "[red]Error: Editable install requires pyproject.toml "
                    "in current directory[/red]"
                )
                raise typer.Exit(1)
        else:
            cmd_list = ["uv", "pip", "install", f"hexdag[{extra}]"]
            cmd_str = f"uv pip install hexdag[{extra}]"
    else:
        if editable:
            if Path("pyproject.toml").exists():
                cmd_list = ["pip", "install", "-e", f".[{extra}]"]
                cmd_str = f"pip install -e .[{extra}]"
            else:
                console.print(
                    "[red]Error: Editable install requires pyproject.toml "
                    "in current directory[/red]"
                )
                raise typer.Exit(1)
        else:
            cmd_list = ["pip", "install", f"hexdag[{extra}]"]
            cmd_str = f"pip install hexdag[{extra}]"

    if dry_run:
        # Use markup=False to avoid bracket interpretation
        console.print("[yellow]Would run:[/yellow] ", end="")
        console.print(cmd_str, markup=False)
        if use_uv_final:
            console.print("[dim]Using: uv package manager[/dim]")
        else:
            console.print("[dim]Using: pip package manager[/dim]")
    else:
        console.print(f"[cyan]Installing {plugin_name}...[/cyan]")
        # Use markup=False for the command
        console.print("Running: [bold]", end="")
        console.print(cmd_str, markup=False, style="bold")
        console.print("")  # Empty line

        # Run the installation - using list format without shell=True for security
        result = subprocess.run(cmd_list, capture_output=True, text=True)  # nosec B603

        if result.returncode == 0:
            console.print(f"[green]✓ Successfully installed {plugin_name}[/green]")

            # Show what was installed
            if "Successfully installed" in result.stdout:
                console.print("\n[dim]Installed packages:[/dim]")
                for line in result.stdout.split("\n"):
                    if "Successfully installed" in line:
                        packages = line.split("Successfully installed")[1].strip()
                        for pkg in packages.split():
                            console.print(f"  • {pkg}")
        else:
            console.print(f"[red]✗ Failed to install {plugin_name}[/red]")
            if result.stderr:
                console.print(f"[dim]{result.stderr}[/dim]")
            if result.stdout and "error" in result.stdout.lower():
                console.print(f"[dim]{result.stdout}[/dim]")


def _get_available_plugins() -> list[dict[str, Any]]:
    """Get list of available plugins by scanning known module paths."""
    import importlib

    plugins = []

    # Built-in adapters (always available)
    builtin_adapters = {
        "mock": {
            "module": "hexdag.builtin.adapters.mock",
            "capabilities": ["LLM", "Database", "ToolRouter"],
            "namespace": "core",
        },
        "memory": {
            "module": "hexdag.builtin.adapters.memory",
            "capabilities": ["Memory"],
            "namespace": "core",
        },
    }

    for name, info in builtin_adapters.items():
        installed = False
        with contextlib.suppress(ImportError):
            importlib.import_module(info["module"])
            installed = True

        plugins.append({
            "name": name,
            "namespace": info["namespace"],
            "installed": installed,
            "capabilities": info["capabilities"],
        })

    # Optional adapters (extras)
    optional_adapters = {
        "openai": {
            "module": "hexdag.adapters.openai",
            "capabilities": ["LLM", "Embeddings"],
            "namespace": "plugin",
        },
        "anthropic": {
            "module": "hexdag.adapters.anthropic",
            "capabilities": ["LLM"],
            "namespace": "plugin",
        },
        "visualization": {
            "module": "hexdag.visualization",
            "capabilities": ["DAG Visualization"],
            "namespace": "core",
        },
    }

    for name, info in optional_adapters.items():
        installed = False
        with contextlib.suppress(ImportError):
            importlib.import_module(info["module"])
            installed = True

        plugins.append({
            "name": name,
            "namespace": info["namespace"],
            "installed": installed,
            "capabilities": info["capabilities"],
        })

    # Check for plugins in hexdag_plugins directory
    from pathlib import Path

    # Try to find hexdag_plugins
    current = Path.cwd()
    plugin_dir = None
    while current != current.parent:
        if (current / "hexdag_plugins").exists():
            plugin_dir = current / "hexdag_plugins"
            break
        current = current.parent

    if plugin_dir:
        for plugin_path in plugin_dir.iterdir():
            if plugin_path.is_dir() and not plugin_path.name.startswith((".", "_")):
                plugin_name = plugin_path.name
                # Skip if already in list
                if plugin_name in [p["name"] for p in plugins]:
                    continue

                # Try to determine capabilities from pyproject.toml
                capabilities = ["Adapter"]
                pyproject = plugin_path / "pyproject.toml"
                if pyproject.exists():
                    import tomllib

                    with pyproject.open("rb") as f:
                        data = tomllib.load(f)
                        # Check for port hints in keywords
                        keywords = data.get("project", {}).get("keywords", [])
                        for kw in keywords:
                            if kw in ("llm", "database", "memory", "api"):
                                capabilities = [kw.upper() if kw != "llm" else "LLM"]

                # Check if installable
                installed = False
                try:
                    importlib.import_module(f"hexdag_plugins.{plugin_name}")
                    installed = True
                except ImportError:
                    pass

                plugins.append({
                    "name": plugin_name,
                    "namespace": "plugin",
                    "installed": installed,
                    "capabilities": capabilities,
                })

    # Special check for visualization graphviz support
    try:
        from hexdag.visualization import GRAPHVIZ_AVAILABLE

        for plugin in plugins:
            if plugin["name"] == "visualization":
                plugin["installed"] = GRAPHVIZ_AVAILABLE
                break
    except ImportError:
        pass

    return plugins


def _check_dependencies() -> list[dict]:
    """Check plugin dependencies dynamically."""
    import importlib
    import shutil

    # Detect if uv is available for better hints
    has_uv = shutil.which("uv") is not None
    prefix = "uv pip install" if has_uv else "pip install"

    checks = []

    # Define dependency checks
    dependency_checks = {
        "pydantic": {
            "name": "pydantic (core)",
            "required": True,
            "extra": None,
        },
        "yaml": {
            "name": "PyYAML (CLI)",
            "required": False,
            "extra": "cli",
        },
        "graphviz": {
            "name": "graphviz (visualization)",
            "required": False,
            "extra": "viz",
        },
    }

    # Check each dependency
    for module_name, info in dependency_checks.items():
        try:
            importlib.import_module(module_name)
            checks.append({"name": info["name"], "status": "ok"})
        except ImportError:
            status = "missing" if info["required"] else "optional"
            hint = f"{prefix} hexdag"
            if info["extra"]:
                hint = f"{prefix} hexdag[{info['extra']}]"

            checks.append({
                "name": info["name"],
                "status": status,
                "install_hint": hint,
            })

    # Check optional adapter packages dynamically
    adapter_packages: dict[str, Any] = {
        "openai": {
            "display": "OpenAI",
            "adapter_module": "hexdag.adapters.openai",
            "extra": "adapters-openai",
        },
        "anthropic": {
            "display": "Anthropic",
            "adapter_module": "hexdag.adapters.anthropic",
            "extra": "adapters-anthropic",
        },
    }

    for sdk_name, info in adapter_packages.items():
        sdk_ok = False
        adapter_ok = False

        # Check SDK
        try:
            importlib.import_module(sdk_name)
            sdk_ok = True
        except ImportError:
            pass

        # Check adapter if SDK exists
        if sdk_ok:
            try:
                importlib.import_module(info["adapter_module"])
                adapter_ok = True
            except ImportError:
                pass

        if sdk_ok and adapter_ok:
            checks.append({"name": f"{info['display']} (SDK + Adapter)", "status": "ok"})
        elif sdk_ok:
            checks.append({
                "name": f"{info['display']} (SDK only, adapter missing)",
                "status": "optional",
                "install_hint": f"{prefix} hexdag[{info['extra']}]",
            })
        else:
            checks.append({
                "name": f"{info['display']} (not installed)",
                "status": "optional",
                "install_hint": f"{prefix} hexdag[{info['extra']}]",
            })

    return checks
