"""
Legacy Switch System - CLI switches for legacy version control.

Provides decorators and utilities for adding legacy switches to CLI commands.
Supports --legacy-v1, --legacy-v2 style switches with automatic version detection
and deprecation warnings.
"""

import click
import functools
from typing import Optional, Callable, Any, Dict, List
from dataclasses import dataclass

from .registry import LegacyRegistry, LegacyStatus
from .deprecation import DeprecationManager, DeprecationLevel
from .exceptions import LegacyVersionNotFoundError


@dataclass
class LegacySwitch:
    """Configuration for a legacy CLI switch."""
    version: str
    flag_name: str
    help_text: str
    deprecation_level: DeprecationLevel = DeprecationLevel.WARNING
    hidden: bool = False


class LegacySwitchManager:
    """Manages legacy switches for CLI commands."""

    def __init__(self):
        self.registry = LegacyRegistry()
        self.deprecation_manager = DeprecationManager()

    def create_switch_options(self, command: str) -> List[LegacySwitch]:
        """
        Create legacy switch options for a command.

        Args:
            command: Command name

        Returns:
            List of LegacySwitch configurations
        """
        switches = []
        legacy_versions = self.registry.get_legacy_versions(command)

        for interface in legacy_versions:
            if interface.status == LegacyStatus.REMOVED:
                continue

            # Determine deprecation level based on status
            if interface.status == LegacyStatus.SUNSET:
                level = DeprecationLevel.CRITICAL
            elif interface.status == LegacyStatus.LEGACY:
                level = DeprecationLevel.WARNING
            else:
                level = DeprecationLevel.INFO

            # Create flag name
            flag_name = f"legacy-{interface.version}"
            if flag_name.startswith("legacy-v"):
                # Already has v prefix
                pass
            elif interface.version.startswith("v"):
                flag_name = f"legacy-{interface.version}"
            else:
                flag_name = f"legacy-v{interface.version}"

            help_text = f"Use {interface.version} legacy behavior"
            if interface.description:
                help_text += f" - {interface.description}"

            switches.append(LegacySwitch(
                version=interface.version,
                flag_name=flag_name,
                help_text=help_text,
                deprecation_level=level,
                hidden=(interface.status == LegacyStatus.SUNSET)
            ))

        return switches

    def execute_with_legacy_support(
        self,
        command: str,
        modern_implementation: Callable,
        legacy_options: Dict[str, bool],
        *args,
        **kwargs
    ) -> Any:
        """
        Execute a command with legacy support.

        Args:
            command: Command name
            modern_implementation: Modern implementation function
            legacy_options: Dictionary of legacy option flags
            *args, **kwargs: Arguments for the implementation

        Returns:
            Result of the appropriate implementation
        """
        # Find which legacy option is enabled
        active_legacy = None
        for option_name, is_enabled in legacy_options.items():
            if is_enabled:
                if active_legacy:
                    raise click.ClickException(
                        "Cannot specify multiple legacy options simultaneously"
                    )
                # Extract version from option name (e.g., legacy_v1_0 -> v1.0)
                version = self._extract_version_from_option(option_name)
                active_legacy = version

        if not active_legacy:
            # Use modern implementation
            return modern_implementation(*args, **kwargs)

        # Use legacy implementation
        try:
            interface = self.registry.get_legacy_interface(command, active_legacy)
            if not interface:
                available = self.registry.get_available_versions(command)
                raise LegacyVersionNotFoundError(command, active_legacy, available)

            # Show deprecation warning
            self.deprecation_manager.warn_deprecated_usage(
                command, active_legacy, interface.status.value,
                interface.removal_date, interface.migration_guide
            )

            # Execute legacy implementation
            return self.registry.execute_legacy(command, active_legacy, *args, **kwargs)

        except LegacyVersionNotFoundError:
            # Fallback to modern implementation with warning
            click.echo(
                f"Warning: Legacy version {active_legacy} not available for {command}. "
                f"Using current implementation.", err=True
            )
            return modern_implementation(*args, **kwargs)

    def _extract_version_from_option(self, option_name: str) -> str:
        """Extract version identifier from option name."""
        # Convert legacy_v1_0 -> v1.0, legacy_v2 -> v2, etc.
        if option_name.startswith("legacy_v"):
            version_part = option_name[8:]  # Remove "legacy_v"
            return "v" + version_part.replace("_", ".")
        elif option_name.startswith("legacy_"):
            return option_name[7:]  # Remove "legacy_"
        else:
            return option_name


def legacy_option(version: str, help_text: str = None) -> Callable:
    """
    Decorator to add a legacy option to a CLI command.

    Args:
        version: Legacy version identifier (e.g., "v1.0", "v2")
        help_text: Custom help text for the option

    Returns:
        Click option decorator

    Example:
        @legacy_option("v1.0", "Use v1.0 legacy query behavior")
        @click.command()
        def my_command(legacy_v1_0):
            if legacy_v1_0:
                # Use legacy implementation
                pass
    """
    # Create flag name
    flag_name = f"legacy-{version}"
    if not flag_name.startswith("legacy-v") and not version.startswith("v"):
        flag_name = f"legacy-v{version}"

    # Create option name (replace hyphens with underscores)
    option_name = flag_name.replace("-", "_")

    # Default help text
    if not help_text:
        help_text = f"Use {version} legacy behavior (deprecated)"

    return click.option(
        f'--{flag_name}',
        option_name,
        is_flag=True,
        help=help_text
    )


def legacy_command(command_name: str, auto_switches: bool = True):
    """
    Decorator to add automatic legacy support to a CLI command.

    Args:
        command_name: Name of the command for legacy registry lookup
        auto_switches: Whether to automatically add legacy switches

    Returns:
        Decorator function

    Example:
        @legacy_command("query")
        @click.command()
        def query_command(**kwargs):
            # Implementation with automatic legacy support
            pass
    """
    def decorator(func: Callable) -> Callable:
        if auto_switches:
            # Add legacy switches automatically
            switch_manager = LegacySwitchManager()
            switches = switch_manager.create_switch_options(command_name)

            # Apply switches in reverse order (click applies decorators bottom-up)
            for switch in reversed(switches):
                option_name = switch.flag_name.replace("-", "_")
                func = click.option(
                    f'--{switch.flag_name}',
                    option_name,
                    is_flag=True,
                    help=switch.help_text,
                    hidden=switch.hidden
                )(func)

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            switch_manager = LegacySwitchManager()

            # Extract legacy options from kwargs
            legacy_options = {}
            func_kwargs = {}

            for key, value in kwargs.items():
                if key.startswith("legacy_"):
                    legacy_options[key] = value
                else:
                    func_kwargs[key] = value

            # Define modern implementation
            def modern_impl(*impl_args, **impl_kwargs):
                return func(*impl_args, **impl_kwargs)

            # Execute with legacy support
            return switch_manager.execute_with_legacy_support(
                command_name, modern_impl, legacy_options, *args, **func_kwargs
            )

        return wrapper
    return decorator


def create_legacy_switches_for_command(command: str) -> List[Callable]:
    """
    Create a list of legacy option decorators for a command.

    Args:
        command: Command name

    Returns:
        List of click option decorators

    Example:
        decorators = create_legacy_switches_for_command("query")
        for decorator in decorators:
            my_command = decorator(my_command)
    """
    switch_manager = LegacySwitchManager()
    switches = switch_manager.create_switch_options(command)

    decorators = []
    for switch in switches:
        option_name = switch.flag_name.replace("-", "_")
        decorator = click.option(
            f'--{switch.flag_name}',
            option_name,
            is_flag=True,
            help=switch.help_text,
            hidden=switch.hidden
        )
        decorators.append(decorator)

    return decorators


def with_legacy_support(command_name: str):
    """
    Simplified decorator for adding legacy support to existing commands.

    Args:
        command_name: Name of the command for legacy registry lookup

    Returns:
        Decorator function

    Example:
        @with_legacy_support("query")
        def my_existing_function(*args, **kwargs):
            # Your existing implementation
            pass
    """
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # Check if any legacy options are present in kwargs
            legacy_options = {k: v for k, v in kwargs.items() if k.startswith("legacy_") and v}

            if not legacy_options:
                # No legacy options, use modern implementation
                return func(*args, **kwargs)

            # Legacy option detected, use legacy manager
            switch_manager = LegacySwitchManager()

            def modern_impl(*impl_args, **impl_kwargs):
                # Remove legacy options from kwargs before calling modern implementation
                clean_kwargs = {k: v for k, v in impl_kwargs.items() if not k.startswith("legacy_")}
                return func(*impl_args, **clean_kwargs)

            return switch_manager.execute_with_legacy_support(
                command_name, modern_impl, legacy_options, *args, **kwargs
            )

        return wrapper
    return decorator