"""Command-line interface for PyWry configuration management."""

from __future__ import annotations

import argparse
import sys

from pathlib import Path
from typing import TYPE_CHECKING


if TYPE_CHECKING:
    from .config import PyWrySettings


def main() -> int:
    """Run the main CLI entry point.

    Returns
    -------
    int
        Exit code.
    """
    parser = argparse.ArgumentParser(
        prog="pywry",
        description="PyWry configuration and development tools",
    )

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # config command
    config_parser = subparsers.add_parser(
        "config",
        help="Show or export configuration",
    )
    config_group = config_parser.add_mutually_exclusive_group()
    config_group.add_argument(
        "--show",
        action="store_true",
        help="Show current configuration with sources",
    )
    config_group.add_argument(
        "--toml",
        action="store_true",
        help="Export configuration as TOML",
    )
    config_group.add_argument(
        "--env",
        action="store_true",
        help="Export configuration as environment variables",
    )
    config_group.add_argument(
        "--sources",
        action="store_true",
        help="Show configuration file sources",
    )
    config_parser.add_argument(
        "--output",
        "-o",
        type=str,
        help="Output file path (default: stdout)",
    )

    # init command
    init_parser = subparsers.add_parser(
        "init",
        help="Initialize a pywry.toml configuration file",
    )
    init_parser.add_argument(
        "--force",
        "-f",
        action="store_true",
        help="Overwrite existing configuration file",
    )
    init_parser.add_argument(
        "--path",
        "-p",
        type=str,
        default="pywry.toml",
        help="Path for configuration file (default: pywry.toml)",
    )

    # mcp command
    mcp_parser = subparsers.add_parser(
        "mcp",
        help="Run the MCP (Model Context Protocol) server for AI agent integration",
    )
    mcp_parser.add_argument(
        "--transport",
        "-t",
        type=str,
        choices=["stdio", "sse"],
        default=None,
        help="Transport type: 'stdio' for CLI/Claude Desktop, 'sse' for HTTP (uses config default)",
    )
    mcp_parser.add_argument(
        "--port",
        "-p",
        type=int,
        default=None,
        help="Port for SSE transport (uses config default)",
    )
    mcp_parser.add_argument(
        "--host",
        type=str,
        default=None,
        help="Host for SSE transport (uses config default)",
    )
    mcp_parser.add_argument(
        "--name",
        type=str,
        default=None,
        help="MCP server name (uses config default)",
    )
    mcp_parser.add_argument(
        "--headless",
        action="store_true",
        default=None,
        help="Run in headless mode (inline widgets via browser)",
    )
    mcp_parser.add_argument(
        "--native",
        action="store_true",
        default=None,
        help="Run in native mode (desktop windows)",
    )

    # install-skills command
    install_skills_parser = subparsers.add_parser(
        "install-skills",
        help="Install bundled PyWry skills into AI vendor skill directories",
    )
    install_skills_parser.add_argument(
        "--target",
        "-t",
        nargs="+",
        default=[],
        metavar="VENDOR",
        help=(
            "Vendor(s) to install into: claude, cursor, vscode, copilot, codex, "
            "gemini, goose, opencode, or 'all' (default: all)"
        ),
    )
    install_skills_parser.add_argument(
        "--skills",
        nargs="+",
        default=None,
        metavar="SKILL",
        help="Specific skill names to install (default: all bundled skills)",
    )
    install_skills_parser.add_argument(
        "--overwrite",
        action="store_true",
        default=False,
        help="Overwrite existing skill directories (default: skip existing)",
    )
    install_skills_parser.add_argument(
        "--custom-dir",
        default=None,
        dest="custom_dir",
        metavar="PATH",
        help="Also install into this custom directory path",
    )
    install_skills_parser.add_argument(
        "--dry-run",
        action="store_true",
        default=False,
        dest="dry_run",
        help="Show what would be installed without writing any files",
    )
    install_skills_parser.add_argument(
        "--list",
        action="store_true",
        default=False,
        help="List all bundled skill names and exit",
    )
    install_skills_parser.add_argument(
        "--list-targets",
        action="store_true",
        default=False,
        dest="list_targets",
        help="List all supported vendor targets and exit",
    )
    install_skills_parser.add_argument(
        "--verbose",
        "-v",
        action="store_true",
        default=False,
        help="Print per-skill status in results",
    )

    args = parser.parse_args()

    if args.command == "config":
        return handle_config(args)
    if args.command == "init":
        return handle_init(args)
    if args.command == "mcp":
        return handle_mcp(args)
    if args.command == "install-skills":
        return handle_install_skills(args)
    parser.print_help()
    return 0


def handle_config(args: argparse.Namespace) -> int:
    """Handle the config command.

    Parameters
    ----------
    args : argparse.Namespace
        Parsed command line arguments.

    Returns
    -------
    int
        Exit code.
    """
    from .config import PyWrySettings

    settings = PyWrySettings()

    if args.sources:
        return show_config_sources()

    if args.toml:
        output = settings.to_toml()
    elif args.env:
        output = settings.to_env()
    elif args.show:
        output = format_config_show(settings)
    else:
        output = format_config_show(settings)

    if args.output:
        Path(args.output).write_text(output, encoding="utf-8")
        print(f"Configuration written to {args.output}")
    else:
        print(output)

    return 0


def handle_init(args: argparse.Namespace) -> int:
    """Handle the init command.

    Parameters
    ----------
    args : argparse.Namespace
        Parsed command line arguments.

    Returns
    -------
    int
        Exit code.
    """
    from .config import PyWrySettings

    path = Path(args.path)

    if path.exists() and not args.force:
        print(f"Error: {path} already exists. Use --force to overwrite.", file=sys.stderr)
        return 1

    settings = PyWrySettings()
    toml_content = settings.to_toml()

    # Add header comment
    header = """# PyWry Configuration File
# Documentation: https://github.com/openbb/pywry
#
# Environment variables can override any setting:
#   PYWRY_CSP__DEFAULT_SRC="'self'"
#   PYWRY_THEME__DARK_BG="#000000"
#   PYWRY_TIMEOUT__STARTUP=30.0
#   PYWRY_WINDOW__TOOLBAR_POSITION="left"
#   PYWRY_HOT_RELOAD__ENABLED=true
#
# Use nested keys with __ (double underscore) delimiter.

"""
    path.write_text(header + toml_content, encoding="utf-8")
    print(f"Created {path}")

    return 0


def handle_mcp(args: argparse.Namespace) -> int:
    """Handle the mcp command.

    Parameters
    ----------
    args : argparse.Namespace
        Parsed command line arguments.

    Returns
    -------
    int
        Exit code.
    """
    try:
        from .mcp import run_server
    except ImportError as e:
        print(
            f"Error: MCP SDK not installed. Install with: pip install mcp\n{e}",
            file=sys.stderr,
        )
        return 1

    from .config import get_settings

    settings = get_settings()
    mcp_config = settings.mcp

    # CLI args override config (if provided)
    transport = args.transport if args.transport is not None else mcp_config.transport
    port = args.port if args.port is not None else mcp_config.port
    host = args.host if args.host is not None else mcp_config.host

    # Apply name override to the config object
    if args.name is not None:
        mcp_config = mcp_config.model_copy(update={"name": args.name})

    # Handle headless mode: CLI flags override config, which overrides PYWRY_HEADLESS env
    import os

    if args.headless:
        headless = True
    elif args.native:
        headless = False
    elif os.environ.get("PYWRY_HEADLESS") is not None:
        # Env var takes precedence over config for backwards compatibility
        headless = os.environ.get("PYWRY_HEADLESS", "0") == "1"
    else:
        headless = mcp_config.headless

    try:
        run_server(
            transport=transport,
            port=port,
            host=host,
            headless=headless,
            settings=mcp_config,
        )
    except KeyboardInterrupt:
        print("\nMCP server stopped.")
    except Exception as e:
        print(f"Error running MCP server: {e}", file=sys.stderr)
        return 1

    return 0


def show_config_sources() -> int:
    """Show configuration file sources and their status.

    Returns
    -------
    int
        Exit code.
    """
    sources = [
        ("Built-in defaults", "Always loaded", True),
        (
            "~/.config/pywry/config.toml",
            str(Path.home() / ".config" / "pywry" / "config.toml"),
            None,
        ),
        ("pyproject.toml [tool.pywry]", "pyproject.toml", None),
        ("./pywry.toml", "pywry.toml", None),
        ("Environment variables", "PYWRY_* vars", None),
    ]

    print("Configuration Sources (in order of precedence):\n")
    print(f"{'Source':<40} {'Status':<15} {'Path'}")
    print("-" * 80)

    for name, path_str, forced_status in sources:
        if forced_status is True:
            status = "✓ Active"
            path_display = ""
        elif forced_status is False:
            status = "✗ Not found"
            path_display = path_str
        # Check if file exists
        elif name == "Environment variables":
            import os

            pywry_vars = [k for k in os.environ if k.startswith("PYWRY_")]
            if pywry_vars:
                status = f"✓ {len(pywry_vars)} vars"
                path_display = ", ".join(pywry_vars[:3])
                if len(pywry_vars) > 3:
                    path_display += "..."
            else:
                status = "✗ No vars"
                path_display = ""
        else:
            path = Path(path_str).expanduser()
            if path.exists():
                status = "✓ Found"
                path_display = str(path)
            else:
                status = "✗ Not found"
                path_display = str(path)

        print(f"{name:<40} {status:<15} {path_display}")

    print("\nNote: Later sources override earlier ones.")
    return 0


def format_config_show(settings: PyWrySettings) -> str:
    """Format configuration for display.

    Parameters
    ----------
    settings : PyWrySettings
        The settings object to format.

    Returns
    -------
    str
        Formatted configuration string.
    """
    from .config import _REDACTED, _SENSITIVE_FIELDS

    lines = []
    lines.append("PyWry Configuration\n" + "=" * 40 + "\n")

    section_names = [
        "csp",
        "theme",
        "timeout",
        "asset",
        "log",
        "window",
        "hot_reload",
        "server",
        "deploy",
        "mcp",
    ]
    all_data = settings.model_dump(
        exclude=dict.fromkeys(section_names, _SENSITIVE_FIELDS),
    )

    for section_name in section_names:
        section_data = all_data.get(section_name, {})
        if lines[-1] != "":
            lines.append("")
        lines.append(f"[{section_name}]")
        for field_name, field_value in section_data.items():
            lines.append(f"  {field_name} = {field_value!r}")
        # Append redacted placeholders for any sensitive fields defined
        # on this section's model class.
        section_cls = type(getattr(settings, section_name))
        lines.extend(
            f"  {rn} = '{_REDACTED}'"
            for rn in sorted(_SENSITIVE_FIELDS & section_cls.model_fields.keys())
        )

    return "\n".join(lines)


def handle_install_skills(args: argparse.Namespace) -> int:
    """Handle the install-skills command.

    Parameters
    ----------
    args : argparse.Namespace
        Parsed command line arguments.

    Returns
    -------
    int
        Exit code.
    """
    try:
        from .mcp.install import (
            ALL_TARGETS,
            install_skills,
            list_bundled_skills,
            print_install_results,
        )
    except ImportError as exc:
        print(
            f"Error: MCP module not available. Install with: pip install pywry[mcp]\n{exc}",
            file=sys.stderr,
        )
        return 1

    if args.list:
        print("Bundled PyWry skills:")
        for name in list_bundled_skills():
            print(f"  {name}")
        return 0

    if args.list_targets:
        print("Supported vendor targets:")
        for name in ALL_TARGETS:
            print(f"  {name}")
        return 0

    targets: list[str] = args.target if args.target else ["all"]
    skill_names: list[str] | None = args.skills if args.skills else None
    custom_dir = getattr(args, "custom_dir", None)

    try:
        results = install_skills(
            targets=targets,
            overwrite=args.overwrite,
            skill_names=skill_names,
            custom_dir=custom_dir,
            dry_run=args.dry_run,
        )
    except (ValueError, FileNotFoundError) as exc:
        print(f"Error: {exc}", file=sys.stderr)
        return 1

    prefix = "[DRY RUN] " if args.dry_run else ""
    print(f"{prefix}PyWry skill install results:")
    print_install_results(results, verbose=args.verbose)
    return 0


if __name__ == "__main__":
    sys.exit(main())
