"""
Compatibility Layer - Bridge between new and legacy interfaces.

Provides translation and adaptation mechanisms to ensure legacy interfaces
can interact with modern implementations while maintaining backward compatibility.
"""

import functools
import inspect
from typing import Any, Callable, Dict, List, Optional, Tuple, Type
from dataclasses import dataclass
from enum import Enum

from .exceptions import CompatibilityError


class CompatibilityMode(Enum):
    """Modes of compatibility translation."""
    STRICT = "strict"           # Exact parameter matching required
    ADAPTIVE = "adaptive"       # Automatic parameter adaptation
    PERMISSIVE = "permissive"   # Allow missing/extra parameters


@dataclass
class ParameterMapping:
    """Mapping between legacy and modern parameter names/formats."""
    legacy_name: str
    modern_name: str
    transformer: Optional[Callable] = None
    default_value: Any = None
    required: bool = True


@dataclass
class InterfaceAdapter:
    """Configuration for adapting a legacy interface to modern implementation."""
    legacy_version: str
    parameter_mappings: List[ParameterMapping]
    return_transformer: Optional[Callable] = None
    compatibility_mode: CompatibilityMode = CompatibilityMode.ADAPTIVE
    pre_processor: Optional[Callable] = None
    post_processor: Optional[Callable] = None


class CompatibilityLayer:
    """
    Provides compatibility translation between legacy and modern interfaces.

    Responsibilities:
    - Map legacy parameter names/formats to modern equivalents
    - Transform parameter values between versions
    - Adapt return values for legacy expectations
    - Provide fallback behavior for missing functionality
    - Maintain compatibility shims for breaking changes
    """

    def __init__(self):
        self._adapters: Dict[str, Dict[str, InterfaceAdapter]] = {}
        self._transformers: Dict[str, Callable] = {}
        self._setup_default_transformers()

    def register_adapter(self, command: str, adapter: InterfaceAdapter):
        """
        Register a compatibility adapter for a command version.

        Args:
            command: Command name
            adapter: Interface adapter configuration
        """
        if command not in self._adapters:
            self._adapters[command] = {}
        self._adapters[command][adapter.legacy_version] = adapter

    def create_legacy_wrapper(
        self,
        command: str,
        version: str,
        modern_implementation: Callable
    ) -> Callable:
        """
        Create a wrapper function that adapts legacy calls to modern implementation.

        Args:
            command: Command name
            version: Legacy version
            modern_implementation: Modern function to wrap

        Returns:
            Wrapped function that accepts legacy parameters
        """
        adapter = self._adapters.get(command, {}).get(version)
        if not adapter:
            # No specific adapter, return modern implementation with warning
            @functools.wraps(modern_implementation)
            def passthrough_wrapper(*args, **kwargs):
                return modern_implementation(*args, **kwargs)
            return passthrough_wrapper

        @functools.wraps(modern_implementation)
        def compatibility_wrapper(*args, **kwargs):
            # Pre-process if configured
            if adapter.pre_processor:
                args, kwargs = adapter.pre_processor(args, kwargs)

            # Transform parameters
            adapted_kwargs = self._adapt_parameters(kwargs, adapter)

            try:
                # Call modern implementation
                result = modern_implementation(*args, **adapted_kwargs)

                # Transform return value if configured
                if adapter.return_transformer:
                    result = adapter.return_transformer(result)

                # Post-process if configured
                if adapter.post_processor:
                    result = adapter.post_processor(result)

                return result

            except Exception as e:
                if adapter.compatibility_mode == CompatibilityMode.PERMISSIVE:
                    # Try fallback behavior
                    return self._handle_compatibility_error(command, version, e, args, kwargs)
                else:
                    raise CompatibilityError(f"Legacy compatibility failed: {e}")

        return compatibility_wrapper

    def _adapt_parameters(self, kwargs: Dict[str, Any], adapter: InterfaceAdapter) -> Dict[str, Any]:
        """Adapt legacy parameters to modern format."""
        adapted = {}

        # Apply parameter mappings
        for mapping in adapter.parameter_mappings:
            if mapping.legacy_name in kwargs:
                value = kwargs[mapping.legacy_name]

                # Apply transformer if available
                if mapping.transformer:
                    value = mapping.transformer(value)

                adapted[mapping.modern_name] = value
            elif mapping.required and mapping.default_value is not None:
                adapted[mapping.modern_name] = mapping.default_value

        # Handle unmapped parameters based on compatibility mode
        mapped_legacy_names = {m.legacy_name for m in adapter.parameter_mappings}
        unmapped = {k: v for k, v in kwargs.items() if k not in mapped_legacy_names}

        if adapter.compatibility_mode == CompatibilityMode.STRICT and unmapped:
            raise CompatibilityError(f"Unmapped legacy parameters: {list(unmapped.keys())}")
        elif adapter.compatibility_mode in [CompatibilityMode.ADAPTIVE, CompatibilityMode.PERMISSIVE]:
            # Pass through unmapped parameters
            adapted.update(unmapped)

        return adapted

    def _handle_compatibility_error(
        self,
        command: str,
        version: str,
        error: Exception,
        args: Tuple,
        kwargs: Dict[str, Any]
    ) -> Any:
        """Handle compatibility errors in permissive mode."""
        # This could implement fallback behaviors, degraded functionality, etc.
        # For now, we'll return a default response indicating the issue

        return {
            'error': 'legacy_compatibility_issue',
            'message': f"Legacy {command} {version} encountered compatibility issue: {error}",
            'fallback_used': True
        }

    def _setup_default_transformers(self):
        """Setup default parameter transformers."""

        # Format transformers
        self._transformers['format_table_to_simple'] = lambda x: 'simple' if x == 'table' else x
        self._transformers['format_simple_to_table'] = lambda x: 'table' if x == 'simple' else x

        # Path transformers
        self._transformers['string_to_path'] = lambda x: str(x) if hasattr(x, '__fspath__') else x
        self._transformers['path_to_string'] = lambda x: x if isinstance(x, str) else str(x)

        # Boolean transformers
        self._transformers['string_to_bool'] = lambda x: x.lower() in ('true', '1', 'yes') if isinstance(x, str) else bool(x)
        self._transformers['bool_to_string'] = lambda x: 'true' if x else 'false'

        # Output format legacy mappings
        self._transformers['legacy_output_format'] = lambda x: {
            'pretty': 'table',
            'raw': 'simple',
            'structured': 'json'
        }.get(x, x)

    def create_format_adapter(self, command: str, version: str) -> InterfaceAdapter:
        """Create a standard adapter for format parameter changes."""
        return InterfaceAdapter(
            legacy_version=version,
            parameter_mappings=[
                ParameterMapping(
                    legacy_name='output_format',
                    modern_name='format',
                    transformer=self._transformers['legacy_output_format'],
                    required=False
                ),
                ParameterMapping(
                    legacy_name='pretty_print',
                    modern_name='format',
                    transformer=lambda x: 'table' if x else 'simple',
                    required=False
                )
            ],
            compatibility_mode=CompatibilityMode.ADAPTIVE
        )

    def create_query_v1_adapter(self) -> InterfaceAdapter:
        """Create adapter for legacy query command v1.0."""
        return InterfaceAdapter(
            legacy_version='v1.0',
            parameter_mappings=[
                ParameterMapping(
                    legacy_name='sql_query',
                    modern_name='sql',
                    required=True
                ),
                ParameterMapping(
                    legacy_name='output_format',
                    modern_name='format',
                    transformer=self._transformers['legacy_output_format'],
                    default_value='simple'
                ),
                ParameterMapping(
                    legacy_name='verbose_output',
                    modern_name='verbose',
                    transformer=self._transformers['string_to_bool'],
                    default_value=False
                )
            ],
            return_transformer=self._legacy_query_return_transformer,
            compatibility_mode=CompatibilityMode.ADAPTIVE
        )

    def create_schema_v1_adapter(self) -> InterfaceAdapter:
        """Create adapter for legacy schema command v1.0."""
        return InterfaceAdapter(
            legacy_version='v1.0',
            parameter_mappings=[
                ParameterMapping(
                    legacy_name='show_schema',
                    modern_name='format',
                    transformer=lambda x: 'table' if x else 'simple',
                    default_value='table'
                ),
                ParameterMapping(
                    legacy_name='include_metadata',
                    modern_name='verbose',
                    transformer=self._transformers['string_to_bool'],
                    default_value=False
                )
            ],
            return_transformer=self._legacy_schema_return_transformer,
            compatibility_mode=CompatibilityMode.ADAPTIVE
        )

    def _legacy_query_return_transformer(self, result: Any) -> Any:
        """Transform modern query results for legacy v1.0 expectations."""
        # Legacy v1.0 expected results in a specific format
        if isinstance(result, list) and result and isinstance(result[0], dict):
            # Convert modern list of dicts to legacy format
            return {
                'status': 'success',
                'row_count': len(result),
                'data': result,
                'format_version': 'v1.0'
            }
        return result

    def _legacy_schema_return_transformer(self, result: Any) -> Any:
        """Transform modern schema results for legacy v1.0 expectations."""
        # Legacy v1.0 expected schema in a different structure
        if isinstance(result, list):
            return {
                'schema_version': 'v1.0',
                'tables': result,
                'table_count': len(result)
            }
        return result

    def register_breaking_change_handler(
        self,
        command: str,
        version: str,
        breaking_change: str,
        handler: Callable
    ):
        """
        Register a handler for a specific breaking change.

        Args:
            command: Command name
            version: Version where breaking change was introduced
            breaking_change: Description of the breaking change
            handler: Function to handle the compatibility issue
        """
        # This could be extended to maintain a registry of breaking change handlers
        pass

    def get_compatibility_report(self, command: str, version: str) -> Dict[str, Any]:
        """
        Generate a compatibility report for a legacy version.

        Args:
            command: Command name
            version: Legacy version

        Returns:
            Compatibility analysis report
        """
        adapter = self._adapters.get(command, {}).get(version)
        if not adapter:
            return {
                'command': command,
                'version': version,
                'compatibility': 'unknown',
                'has_adapter': False,
                'mappings': [],
                'recommendations': ['Create a compatibility adapter for this version']
            }

        return {
            'command': command,
            'version': version,
            'compatibility': 'supported',
            'has_adapter': True,
            'compatibility_mode': adapter.compatibility_mode.value,
            'mappings': [
                {
                    'legacy_parameter': mapping.legacy_name,
                    'modern_parameter': mapping.modern_name,
                    'has_transformer': mapping.transformer is not None,
                    'required': mapping.required
                }
                for mapping in adapter.parameter_mappings
            ],
            'has_return_transformer': adapter.return_transformer is not None,
            'recommendations': []
        }

    def test_compatibility(
        self,
        command: str,
        version: str,
        test_parameters: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Test compatibility adaptation with sample parameters.

        Args:
            command: Command name
            version: Legacy version
            test_parameters: Sample legacy parameters

        Returns:
            Test results showing parameter transformations
        """
        adapter = self._adapters.get(command, {}).get(version)
        if not adapter:
            return {
                'success': False,
                'error': 'No adapter found for this version'
            }

        try:
            adapted_params = self._adapt_parameters(test_parameters, adapter)
            return {
                'success': True,
                'original_parameters': test_parameters,
                'adapted_parameters': adapted_params,
                'transformations_applied': len(adapter.parameter_mappings)
            }
        except Exception as e:
            return {
                'success': False,
                'error': str(e),
                'original_parameters': test_parameters
            }

    def setup_standard_adapters(self):
        """Setup standard adapters for common legacy patterns."""

        # Register query command adapters
        self.register_adapter('query', self.create_query_v1_adapter())

        # Register schema command adapters
        self.register_adapter('schema', self.create_schema_v1_adapter())

        # Register format adapters for various commands
        for command in ['list', 'metadata', 'db-query', 'db-schema']:
            self.register_adapter(command, self.create_format_adapter(command, 'v1.0'))

    def get_adapter_statistics(self) -> Dict[str, Any]:
        """Get statistics about registered compatibility adapters."""
        stats = {
            'total_commands': len(self._adapters),
            'total_adapters': sum(len(versions) for versions in self._adapters.values()),
            'by_command': {},
            'by_compatibility_mode': {}
        }

        for command, versions in self._adapters.items():
            stats['by_command'][command] = {
                'versions': list(versions.keys()),
                'count': len(versions)
            }

            for version, adapter in versions.items():
                mode = adapter.compatibility_mode.value
                stats['by_compatibility_mode'][mode] = stats['by_compatibility_mode'].get(mode, 0) + 1

        return stats