#!/usr/bin/env python3
"""Interactive setup wizard for second-opinion-mcp."""
import sys
import platform
import os

try:
    import questionary
    from questionary import Style
except ImportError:
    print("Error: questionary not installed. Run: pip install questionary")
    sys.exit(1)

import keyring
from keyring.errors import NoKeyringError

# Provider configurations
PROVIDERS = {
    "deepseek": {
        "name": "DeepSeek V3.2",
        "description": "DeepSeek Reasoner with chain-of-thought reasoning",
        "keyring_service": "second-opinion",
        "keyring_name": "deepseek",
        "key_hint": "https://platform.deepseek.com",
        "key_prefix": "sk-",
    },
    "moonshot": {
        "name": "Kimi K2.5 (Moonshot)",
        "description": "Moonshot AI's Kimi with thinking mode",
        "keyring_service": "second-opinion",
        "keyring_name": "moonshot",
        "key_hint": "https://platform.moonshot.cn",
        "key_prefix": None,
    },
    "openrouter": {
        "name": "OpenRouter",
        "description": "Access 300+ models via unified API",
        "keyring_service": "second-opinion",
        "keyring_name": "openrouter",
        "key_hint": "https://openrouter.ai/keys",
        "key_prefix": "sk-or-",
    },
}

# Custom style for questionary
custom_style = Style([
    ("qmark", "fg:cyan bold"),
    ("question", "bold"),
    ("answer", "fg:cyan"),
    ("pointer", "fg:cyan bold"),
    ("highlighted", "fg:cyan bold"),
    ("selected", "fg:green"),
])


def detect_installation_method() -> tuple[str, str | None]:
    """Detect how second-opinion-mcp was installed.

    Returns:
        tuple: (method, python_path) where method is 'pipx', 'pip', or 'unknown'
               and python_path is the path to Python executable if found.
    """
    home = os.path.expanduser("~")
    system = platform.system()

    # Check for pipx installation
    if system == "Windows":
        pipx_paths = [
            os.path.join(home, ".local", "pipx", "venvs", "second-opinion-mcp", "Scripts", "python.exe"),
            os.path.join(home, "pipx", "venvs", "second-opinion-mcp", "Scripts", "python.exe"),
            os.path.join(os.environ.get("LOCALAPPDATA", ""), "pipx", "venvs", "second-opinion-mcp", "Scripts", "python.exe"),
        ]
    else:
        pipx_paths = [
            os.path.join(home, ".local", "share", "pipx", "venvs", "second-opinion-mcp", "bin", "python3"),
            os.path.join(home, ".local", "share", "pipx", "venvs", "second-opinion-mcp", "bin", "python"),
            os.path.join(home, ".local", "pipx", "venvs", "second-opinion-mcp", "bin", "python3"),
        ]

    for path in pipx_paths:
        if os.path.exists(path):
            return "pipx", path

    # Check if we're running from a venv (pip install -e . or pip install)
    if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
        return "pip", sys.executable

    # Unknown installation method
    return "unknown", sys.executable


def get_pipx_python_path() -> str | None:
    """Get the Python path from pipx venv if installed via pipx."""
    method, path = detect_installation_method()
    if method == "pipx":
        return path
    return None


def check_keyring_available() -> tuple[bool, str]:
    """Check if keyring backend is available and working."""
    try:
        # Try to access keyring - this will fail if no backend
        backend = keyring.get_keyring()
        backend_name = str(type(backend).__name__)

        # Check for null/fail backends
        if "Null" in backend_name or "Fail" in backend_name:
            return False, backend_name

        # Try a test write/read/delete to verify it works
        test_service = "second-opinion-test"
        test_key = "connectivity-check"
        try:
            keyring.set_password(test_service, test_key, "test")
            result = keyring.get_password(test_service, test_key)
            keyring.delete_password(test_service, test_key)
            if result != "test":
                return False, backend_name
        except Exception:
            return False, backend_name

        return True, backend_name
    except NoKeyringError:
        return False, "NoKeyringError"
    except Exception as e:
        return False, str(e)


def print_keyring_setup_instructions():
    """Print instructions for setting up keyring on different platforms."""
    print()
    print("=" * 60)
    print("  Keyring Backend Required")
    print("=" * 60)
    print()
    print("second-opinion-mcp stores API keys securely in your system keyring.")
    print("A keyring backend must be installed and configured first.")
    print()

    system = platform.system()
    install_method, _ = detect_installation_method()

    if system == "Linux":
        if install_method == "pipx":
            print("For Linux (pipx installation detected), run:")
            print()
            print("  # Install the encrypted file backend into the pipx environment")
            print("  pipx inject second-opinion-mcp keyrings.alt")
        else:
            print("For Linux, install the encrypted file backend:")
            print()
            print("  pip install keyrings.alt")
        print()
        print("  # Create the keyring config")
        print("  mkdir -p ~/.local/share/python_keyring")
        print("  cat > ~/.local/share/python_keyring/keyringrc.cfg << 'EOF'")
        print("  [backend]")
        print("  default-keyring=keyrings.alt.file.EncryptedKeyring")
        print("  EOF")
        print()
        print("Alternative for desktop Linux (GNOME/KDE):")
        print("  sudo apt install gnome-keyring  # or kde-wallet")
        if install_method == "pipx":
            print("  pipx inject second-opinion-mcp secretstorage")
        else:
            print("  pip install secretstorage")
        print()
        print("Then run 'second-opinion-mcp setup' again.")

    elif system == "Darwin":
        print("For macOS, keyring should work automatically with Keychain.")
        print("If you see this error, try:")
        print()
        if install_method == "pipx":
            print("  pipx reinstall second-opinion-mcp")
        else:
            print("  pip install --upgrade keyring")
        print()
        print("Then run 'second-opinion-mcp setup' again.")

    else:
        print("For Windows, keyring should work automatically with Credential Manager.")
        print("If you see this error, try:")
        print()
        if install_method == "pipx":
            print("  pipx reinstall second-opinion-mcp")
        else:
            print("  pip install --upgrade keyring")
        print()
        print("Then run 'second-opinion-mcp setup' again.")

    print()


def print_manual_setup_instructions(providers: list[str] | None = None, existing: dict[str, bool] | None = None):
    """Print instructions for manual keyring setup."""
    print()
    print("=" * 60)
    print("  Manual API Key Setup")
    print("=" * 60)
    print()

    # Detect installation method
    install_method, python_path = detect_installation_method()
    system = platform.system()

    if system == "Linux":
        print("Step 1: Install keyrings.alt backend (if not done):")
        print()
        if install_method == "pipx":
            print("  pipx inject second-opinion-mcp keyrings.alt")
        else:
            print("  pip install keyrings.alt")
        print("  mkdir -p ~/.local/share/python_keyring")
        print("  cat > ~/.local/share/python_keyring/keyringrc.cfg << 'EOF'")
        print("  [backend]")
        print("  default-keyring=keyrings.alt.file.EncryptedKeyring")
        print("  EOF")
        print()
        print("Step 2: Store API keys (at least 2 required):")
        print()
    else:
        print("IMPORTANT: You must configure at least 2 providers for the")
        print("'consensus' tool to work. Single-provider mode is limited.")
        print()
        print("Run these commands to store your API keys:")
        print()

    # Determine which Python to use based on installation method
    if install_method == "pipx" and python_path:
        python_cmd = python_path
        print(f"  # Using pipx environment: {python_path}")
        print()
    elif python_path:
        python_cmd = python_path
        print(f"  # Using Python: {python_path}")
        print()
    else:
        python_cmd = "python3"
        print("  # Note: If 'import keyring' fails, ensure keyring is installed:")
        if install_method == "pipx":
            print("  # pipx inject second-opinion-mcp keyring")
        else:
            print("  # pip install keyring")
        print()

    target_providers = providers if providers else list(PROVIDERS.keys())

    # Show status if we have existing info
    if existing:
        configured = [p for p in target_providers if existing.get(p)]
        not_configured = [p for p in target_providers if not existing.get(p)]
        if configured:
            print(f"  Already configured: {', '.join(configured)}")
        if not_configured:
            print(f"  Need to configure: {', '.join(not_configured)}")
        print()

    for provider in target_providers:
        config = PROVIDERS[provider]
        status = ""
        if existing and existing.get(provider):
            status = " (already configured)"
        print(f"  # {config['name']}{status} - Get key at {config['key_hint']}")
        print(f"  {python_cmd} -c \"import keyring; keyring.set_password('{config['keyring_service']}', '{config['keyring_name']}', 'YOUR_API_KEY')\"")
        print()

    print("To verify your keys are stored:")
    print()
    verify_providers = "', '".join([f"{p}:", "bool(keyring.get_password('second-opinion', '{p}'))".replace("{p}", p) for p in ["deepseek", "moonshot", "openrouter"]])
    print(f"  {python_cmd} -c \"import keyring; print('deepseek:', bool(keyring.get_password('second-opinion', 'deepseek'))); print('moonshot:', bool(keyring.get_password('second-opinion', 'moonshot'))); print('openrouter:', bool(keyring.get_password('second-opinion', 'openrouter')))\"")
    print()
    print("After configuring at least 2 keys, register with Claude Code:")
    print()

    if providers and len(providers) >= 1:
        providers_str = ",".join(providers)
    else:
        providers_str = "deepseek,moonshot"

    print(f"  claude mcp add -s user second-opinion -e SECOND_OPINION_PROVIDERS=\"{providers_str}\" -- second-opinion-mcp")
    print()


def check_existing_keys() -> dict[str, bool]:
    """Check which providers already have API keys configured."""
    existing = {}
    for provider, config in PROVIDERS.items():
        try:
            key = keyring.get_password(config["keyring_service"], config["keyring_name"])
            existing[provider] = bool(key)
        except Exception:
            existing[provider] = False
    return existing


def count_configured_providers() -> int:
    """Count how many providers have API keys configured."""
    return sum(1 for v in check_existing_keys().values() if v)


def store_api_key(provider: str, key: str) -> tuple[bool, str]:
    """Store API key in system keyring. Returns (success, error_message)."""
    config = PROVIDERS[provider]
    try:
        keyring.set_password(config["keyring_service"], config["keyring_name"], key)
        return True, ""
    except Exception as e:
        return False, str(e)


def validate_api_key(provider: str, key: str) -> tuple[bool, str]:
    """Basic validation of API key format."""
    if not key or not key.strip():
        return False, "API key cannot be empty"

    config = PROVIDERS[provider]
    key = key.strip()

    if config["key_prefix"] and not key.startswith(config["key_prefix"]):
        return False, f"Key should start with '{config['key_prefix']}'"

    if len(key) < 20:
        return False, "Key seems too short"

    return True, ""


def get_registration_command(selected_providers: list[str]) -> str:
    """Generate the registration command for Claude Code CLI."""
    providers_env = ",".join(selected_providers)
    return f'claude mcp add -s user second-opinion -e SECOND_OPINION_PROVIDERS="{providers_env}" -- second-opinion-mcp'


def get_desktop_config(selected_providers: list[str]) -> str:
    """Generate Claude Desktop configuration JSON."""
    providers_env = ",".join(selected_providers)

    return f'''{{
  "mcpServers": {{
    "second-opinion": {{
      "command": "second-opinion-mcp",
      "env": {{
        "SECOND_OPINION_PROVIDERS": "{providers_env}"
      }}
    }}
  }}
}}'''


def print_client_instructions(configured_providers: list[str]):
    """Print setup instructions for all supported MCP clients."""
    providers_env = ",".join(configured_providers)

    print()
    print("=" * 60)
    print("  Client Setup Instructions")
    print("=" * 60)

    # Claude Code CLI
    print()
    print("CLAUDE CODE CLI:")
    print(f"  claude mcp add -s user second-opinion -e SECOND_OPINION_PROVIDERS=\"{providers_env}\" -- second-opinion-mcp")

    # Claude Desktop
    print()
    print("CLAUDE DESKTOP APP:")
    print("  Add to claude_desktop_config.json:")
    if platform.system() == "Windows":
        print("  Location: %APPDATA%\\Claude\\claude_desktop_config.json")
    elif platform.system() == "Darwin":
        print("  Location: ~/Library/Application Support/Claude/claude_desktop_config.json")
    else:
        print("  Location: ~/.config/Claude/claude_desktop_config.json")
    print()
    print(get_desktop_config(configured_providers))

    # OpenClaw / Other MCP clients
    print()
    print("OPENCLAW / OTHER MCP CLIENTS:")
    print("  Server command: second-opinion-mcp")
    print(f"  Environment: SECOND_OPINION_PROVIDERS={providers_env}")
    print()
    print("  For OpenClaw, add in settings with:")
    print("    - Name: second-opinion")
    print("    - Command: second-opinion-mcp")
    print(f"    - Environment variables: SECOND_OPINION_PROVIDERS={providers_env}")

    print()


def setup_wizard():
    """Interactive setup wizard for second-opinion-mcp."""
    print()
    print("=" * 60)
    print("  Second Opinion MCP - Setup Wizard")
    print("=" * 60)
    print()

    # Check keyring availability first
    keyring_ok, backend_info = check_keyring_available()

    if not keyring_ok:
        print(f"Keyring backend not available: {backend_info}")
        print_keyring_setup_instructions()
        print()
        print("Alternatively, choose 'Manual setup' to get CLI commands.")
        print()

        choice = questionary.select(
            "How would you like to proceed?",
            choices=[
                questionary.Choice("Manual setup (show CLI commands)", value="manual"),
                questionary.Choice("Exit and fix keyring first", value="exit"),
            ],
            style=custom_style,
        ).ask()

        if choice == "manual" or choice is None:
            print_manual_setup_instructions()
        return

    print("This wizard will help you configure API keys for AI providers.")
    print("Keys are stored securely in your system keyring.")
    print()

    # Check existing configuration
    existing = check_existing_keys()
    configured_count = sum(1 for v in existing.values() if v)
    configured = [p for p, has_key in existing.items() if has_key]
    not_configured = [p for p, has_key in existing.items() if not has_key]

    # Show current status
    print("Current status:")
    for provider, config in PROVIDERS.items():
        status = "configured" if existing.get(provider) else "not configured"
        print(f"  {config['name']}: {status}")
    print()

    # If 2+ providers already configured, offer quick path
    if configured_count >= 2:
        print(f"You have {configured_count} providers configured (minimum 2 required).")
        print()

        action = questionary.select(
            "What would you like to do?",
            choices=[
                questionary.Choice("Show registration command (ready to use)", value="register"),
                questionary.Choice("Add or update API keys", value="configure"),
                questionary.Choice("Manual setup (show CLI commands)", value="manual"),
            ],
            style=custom_style,
        ).ask()

        if action == "register":
            client_choice = questionary.select(
                "Which client will you use?",
                choices=[
                    questionary.Choice("Claude Code CLI (Recommended)", value="claude-code"),
                    questionary.Choice("Claude Desktop App", value="claude-desktop"),
                    questionary.Choice("OpenClaw / Other MCP Client", value="other"),
                    questionary.Choice("Show all client instructions", value="all"),
                ],
                style=custom_style,
            ).ask()

            if client_choice == "all":
                print_client_instructions(configured)
            elif client_choice == "claude-code":
                print()
                print("Register with Claude Code CLI:")
                print()
                print(f"  {get_registration_command(configured)}")
                print()
                print("Then restart Claude Code.")
            elif client_choice == "claude-desktop":
                print()
                print("Add this to your claude_desktop_config.json:")
                print()
                print(get_desktop_config(configured))
                print()
                if platform.system() == "Windows":
                    print("Location: %APPDATA%\\Claude\\claude_desktop_config.json")
                elif platform.system() == "Darwin":
                    print("Location: ~/Library/Application Support/Claude/claude_desktop_config.json")
                else:
                    print("Location: ~/.config/Claude/claude_desktop_config.json")
                print()
                print("Then restart Claude Desktop.")
            else:
                providers_env = ",".join(configured)
                print()
                print("For OpenClaw or other MCP clients:")
                print()
                print("  Server command: second-opinion-mcp")
                print(f"  Environment: SECOND_OPINION_PROVIDERS={providers_env}")
                print()
                print("  Add the server in your client's MCP settings with these values.")
            return

        elif action == "manual":
            print_manual_setup_instructions(list(PROVIDERS.keys()), existing)
            return

        # Fall through to configure flow

    elif configured_count == 1:
        print(f"Only {configured_count} provider configured. You need at least 2.")
        print("The 'consensus' tool requires multiple models to debate.")
        print()
    else:
        print("No providers configured yet. You need at least 2.")
        print()

    # Setup method selection
    setup_method = questionary.select(
        "Setup method:",
        choices=[
            questionary.Choice("Interactive (enter keys now)", value="interactive"),
            questionary.Choice("Manual (show CLI commands)", value="manual"),
        ],
        style=custom_style,
    ).ask()

    if setup_method is None:
        print("Setup cancelled.")
        return

    if setup_method == "manual":
        # Ask which providers they want to configure
        choices = [
            questionary.Choice(
                f"{config['name']}" + (" (configured)" if existing.get(provider) else ""),
                value=provider,
                checked=not existing.get(provider)  # Pre-check unconfigured ones
            )
            for provider, config in PROVIDERS.items()
        ]

        selected = questionary.checkbox(
            "Select providers to configure:",
            choices=choices,
            style=custom_style,
        ).ask()

        if not selected:
            selected = list(PROVIDERS.keys())

        print_manual_setup_instructions(selected, existing)
        return

    # Interactive setup - determine which providers to configure
    if not_configured:
        # Default: configure unconfigured providers
        choices = []
        for provider, config in PROVIDERS.items():
            label = config["name"]
            if existing.get(provider):
                label += " (configured)"
            # Pre-check unconfigured ones, or all if less than 2 configured
            should_check = not existing.get(provider)
            choices.append(questionary.Choice(label, value=provider, checked=should_check))

        # Calculate how many more we need
        needed = max(0, 2 - configured_count)

        selected = questionary.checkbox(
            f"Select providers to configure (need {needed} more for minimum 2):",
            choices=choices,
            style=custom_style,
            validate=lambda x: len(x) >= 1 or "Select at least one provider",
        ).ask()
    else:
        # All configured, but user wants to update
        choices = [
            questionary.Choice(f"{config['name']} (configured)", value=provider)
            for provider, config in PROVIDERS.items()
        ]

        selected = questionary.checkbox(
            "Select providers to update:",
            choices=choices,
            style=custom_style,
        ).ask()

    if not selected:
        print("Setup cancelled.")
        return

    # Check if this will meet minimum requirement
    will_configure_new = len([p for p in selected if not existing.get(p)])
    total_after = configured_count + will_configure_new

    if total_after < 2 and will_configure_new > 0:
        print()
        print(f"WARNING: After configuring, you'll have {total_after} provider(s).")
        print("You need at least 2 providers for the 'consensus' tool.")
        print("With only 1 provider, only basic tools will be available.")
        proceed = questionary.confirm(
            "Continue anyway?",
            default=False,
            style=custom_style,
        ).ask()
        if not proceed:
            return setup_wizard()

    print()

    # Configure API keys
    keyring_error_shown = False
    for provider in selected:
        config = PROVIDERS[provider]

        if existing.get(provider):
            update = questionary.confirm(
                f"{config['name']} already configured. Update API key?",
                default=False,
                style=custom_style,
            ).ask()
            if not update:
                continue

        print(f"\n{config['name']}")
        print(f"  Get your key at: {config['key_hint']}")

        while True:
            key = questionary.password(
                "  Enter API key:",
                style=custom_style,
            ).ask()

            if key is None:
                print("  Skipped.")
                break

            valid, error = validate_api_key(provider, key)
            if not valid:
                print(f"  {error}")
                retry = questionary.confirm("Try again?", default=True, style=custom_style).ask()
                if not retry:
                    break
                continue

            success, store_error = store_api_key(provider, key.strip())
            if success:
                print("  Key stored successfully.")
                break
            else:
                print(f"  Error storing key: {store_error}")
                if not keyring_error_shown:
                    keyring_error_shown = True
                    print()
                    print("  Keyring storage failed. You may need to configure a backend.")
                    show_manual = questionary.confirm(
                        "  Show manual setup instructions?",
                        default=True,
                        style=custom_style,
                    ).ask()
                    if show_manual:
                        print_manual_setup_instructions(selected, existing)
                        return
                break

    # Check final configuration
    final_existing = check_existing_keys()
    final_configured = [p for p, has_key in final_existing.items() if has_key]
    final_count = len(final_configured)

    print()
    print("=" * 60)
    print("  Setup Complete!")
    print("=" * 60)
    print()

    if not final_configured:
        print("No API keys were configured.")
        print("Run 'second-opinion-mcp setup' to try again.")
        return

    print(f"Configured providers: {', '.join(final_configured)} ({final_count} total)")

    if final_count < 2:
        print()
        print("WARNING: Only 1 provider configured. The 'consensus' tool requires 2+.")
        print("Run 'second-opinion-mcp setup' to add more providers.")
        print()
        return

    # Ask which client to show instructions for
    client_choice = questionary.select(
        "Which client will you use?",
        choices=[
            questionary.Choice("Claude Code CLI (Recommended)", value="claude-code"),
            questionary.Choice("Claude Desktop App", value="claude-desktop"),
            questionary.Choice("OpenClaw / Other MCP Client", value="other"),
            questionary.Choice("Show all client instructions", value="all"),
        ],
        style=custom_style,
    ).ask()

    if client_choice == "all":
        print_client_instructions(final_configured)
    elif client_choice == "claude-code":
        print()
        print("Register with Claude Code CLI:")
        print()
        print(f"  {get_registration_command(final_configured)}")
        print()
        print("Then restart Claude Code.")
    elif client_choice == "claude-desktop":
        print()
        print("Add this to your claude_desktop_config.json:")
        print()
        print(get_desktop_config(final_configured))
        print()
        if platform.system() == "Windows":
            print("Location: %APPDATA%\\Claude\\claude_desktop_config.json")
        elif platform.system() == "Darwin":
            print("Location: ~/Library/Application Support/Claude/claude_desktop_config.json")
        else:
            print("Location: ~/.config/Claude/claude_desktop_config.json")
        print()
        print("Then restart Claude Desktop.")
    else:
        providers_env = ",".join(final_configured)
        print()
        print("For OpenClaw or other MCP clients:")
        print()
        print("  Server command: second-opinion-mcp")
        print(f"  Environment: SECOND_OPINION_PROVIDERS={providers_env}")
        print()
        print("  Add the server in your client's MCP settings with these values.")

    print()
    print("Test the server with:")
    print("  second-opinion-mcp")
    print()


if __name__ == "__main__":
    setup_wizard()
