"""
Component Upgrader for Hephaestus

Systematically upgrades classic Anvil components to Material 3,
handling property migration, event updates, and theme integration.
"""

from typing import Dict, List, Any, Optional
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
import yaml
import copy

from ..dependencies.container import FileSystem, ProjectManager


class UpgradeStrategy(Enum):
    """Upgrade strategies for components."""

    DIRECT_REPLACEMENT = "direct_replacement"
    GRADUAL_UPGRADE = "gradual_upgrade"
    HYBRID_COMPATIBILITY = "hybrid_compatibility"
    CUSTOM_MAPPING = "custom_mapping"


class ComponentCategory(Enum):
    """Categories of components."""

    BUTTON = "button"
    INPUT = "input"
    DISPLAY = "display"
    CONTAINER = "container"
    NAVIGATION = "navigation"
    DATA_TABLE = "data_table"
    MEDIA = "media"


@dataclass
class ComponentMapping:
    """Mapping between classic and M3 components."""

    classic_type: str
    m3_type: str
    property_mappings: Dict[str, str]
    deprecated_properties: List[str]
    new_properties: Dict[str, Any]
    role_mapping: Optional[str]
    event_mappings: Dict[str, str]
    compatibility_notes: List[str]


@dataclass
class UpgradeResult:
    """Result of component upgrade operation."""

    success: bool
    original_component: str
    upgraded_component: str
    components_upgraded: int
    properties_migrated: int
    events_updated: int
    breaking_changes: List[str]
    warnings: List[str]
    backward_compatible: bool
    upgrade_strategy: UpgradeStrategy
    validation_errors: List[str]


class ComponentUpgrader:
    """Upgrades classic Anvil components to Material 3."""

    def __init__(self, file_system: FileSystem, project_manager: ProjectManager):
        self.file_system = file_system
        self.project_manager = project_manager

        # Component upgrade mappings
        self._component_mappings = self._initialize_component_mappings()

        # Property transformation rules
        self._property_transformations = self._initialize_property_transformations()

        # Theme integration rules
        self._theme_rules = self._initialize_theme_rules()

        # Validation rules
        self._validation_rules = self._initialize_validation_rules()

    def upgrade_to_m3_components(
        self,
        form_path: Path,
        strategy: UpgradeStrategy = UpgradeStrategy.GRADUAL_UPGRADE,
        preserve_compatibility: bool = True,
    ) -> UpgradeResult:
        """Systematically upgrade components to Material 3."""
        try:
            # Load form YAML
            form_yaml = self._load_form_yaml(form_path)

            # Analyze components for upgrade
            components = self._analyze_components_for_upgrade(form_yaml)

            # Create upgrade plan
            upgrade_plan = self._create_upgrade_plan(components, strategy)

            # Execute upgrades
            upgraded_form = self._execute_upgrade_plan(
                form_yaml, upgrade_plan, preserve_compatibility
            )

            # Validate upgraded form
            validation_errors = self._validate_upgraded_form(upgraded_form)

            # Save upgraded form
            if not validation_errors:
                self._save_upgraded_form(form_path, upgraded_form)

            # Generate result
            return self._generate_upgrade_result(
                form_path,
                upgrade_plan,
                upgraded_form,
                validation_errors,
                strategy,
                preserve_compatibility,
            )

        except Exception as e:
            return UpgradeResult(
                success=False,
                original_component=str(form_path),
                upgraded_component="",
                components_upgraded=0,
                properties_migrated=0,
                events_updated=0,
                breaking_changes=[],
                warnings=[],
                backward_compatible=False,
                upgrade_strategy=strategy,
                validation_errors=[str(e)],
            )

    def analyze_upgrade_feasibility(self, form_path: Path) -> Dict[str, Any]:
        """Analyze feasibility of upgrading components to M3."""
        try:
            form_yaml = self._load_form_yaml(form_path)
            components = self._analyze_components_for_upgrade(form_yaml)

            upgradeable = []
            requires_manual = []
            incompatible = []

            for component in components:
                component_type = component.get("type", "")

                if component_type in self._component_mappings:
                    mapping = self._component_mappings[component_type]

                    if mapping.m3_type:
                        upgradeable.append(
                            {
                                "name": component.get("name"),
                                "type": component_type,
                                "target": mapping.m3_type,
                                "complexity": self._assess_upgrade_complexity(
                                    component, mapping
                                ),
                            }
                        )
                    else:
                        requires_manual.append(
                            {
                                "name": component.get("name"),
                                "type": component_type,
                                "reason": "No direct M3 equivalent",
                            }
                        )
                else:
                    incompatible.append(
                        {
                            "name": component.get("name"),
                            "type": component_type,
                            "reason": "Unknown component type",
                        }
                    )

            return {
                "feasible": len(incompatible) == 0,
                "total_components": len(components),
                "upgradeable": upgradeable,
                "upgradeable_count": len(upgradeable),
                "requires_manual": requires_manual,
                "manual_count": len(requires_manual),
                "incompatible": incompatible,
                "incompatible_count": len(incompatible),
                "estimated_effort": self._estimate_upgrade_effort(
                    len(upgradeable), len(requires_manual), len(incompatible)
                ),
            }

        except Exception as e:
            return {
                "feasible": False,
                "error": str(e),
            }

    def create_upgrade_plan(
        self,
        form_path: Path,
        target_components: Optional[List[str]] = None,
    ) -> Dict[str, Any]:
        """Create detailed upgrade plan for components."""
        try:
            form_yaml = self._load_form_yaml(form_path)
            components = self._analyze_components_for_upgrade(form_yaml)

            if target_components:
                components = [
                    c for c in components if c.get("name") in target_components
                ]

            upgrade_steps = []
            for component in components:
                component_type = component.get("type", "")

                if component_type in self._component_mappings:
                    mapping = self._component_mappings[component_type]

                    upgrade_steps.append(
                        {
                            "step": len(upgrade_steps) + 1,
                            "component_name": component.get("name"),
                            "action": "upgrade",
                            "from": component_type,
                            "to": mapping.m3_type,
                            "property_changes": self._plan_property_changes(
                                component, mapping
                            ),
                            "event_changes": self._plan_event_changes(
                                component, mapping
                            ),
                            "breaking_changes": self._identify_breaking_changes(
                                component, mapping
                            ),
                            "estimated_time": self._estimate_component_upgrade_time(
                                component, mapping
                            ),
                        }
                    )

            return {
                "form_path": str(form_path),
                "total_steps": len(upgrade_steps),
                "upgrade_steps": upgrade_steps,
                "estimated_total_time": sum(
                    step["estimated_time"] for step in upgrade_steps
                ),
                "prerequisites": self._identify_prerequisites(components),
                "post_upgrade_tasks": self._identify_post_upgrade_tasks(components),
            }

        except Exception as e:
            return {
                "error": str(e),
            }

    def _initialize_component_mappings(self) -> Dict[str, ComponentMapping]:
        """Initialize classic to M3 component mappings."""
        return {
            # Button components
            "Button": ComponentMapping(
                classic_type="Button",
                m3_type="Button",
                property_mappings={
                    "text": "text",
                    "enabled": "enabled",
                    "visible": "visible",
                    "icon": "icon",
                },
                deprecated_properties=["foreground", "background"],
                new_properties={"role": "filled-button"},
                role_mapping="filled-button",
                event_mappings={"click": "click"},
                compatibility_notes=[
                    "Use role for styling instead of foreground/background"
                ],
            ),
            "Link": ComponentMapping(
                classic_type="Link",
                m3_type="Link",
                property_mappings={
                    "text": "text",
                    "url": "url",
                    "enabled": "enabled",
                },
                deprecated_properties=["foreground"],
                new_properties={"role": "default"},
                role_mapping="default",
                event_mappings={"click": "click"},
                compatibility_notes=["M3 links use theme colors automatically"],
            ),
            # Input components
            "TextBox": ComponentMapping(
                classic_type="TextBox",
                m3_type="TextBox",
                property_mappings={
                    "text": "text",
                    "placeholder": "placeholder",
                    "enabled": "enabled",
                },
                deprecated_properties=["foreground", "background"],
                new_properties={"role": "outlined"},
                role_mapping="outlined",
                event_mappings={"change": "change", "pressed_enter": "pressed_enter"},
                compatibility_notes=["Use outlined or filled role for M3 styling"],
            ),
            "TextArea": ComponentMapping(
                classic_type="TextArea",
                m3_type="TextArea",
                property_mappings={
                    "text": "text",
                    "placeholder": "placeholder",
                    "enabled": "enabled",
                    "auto_expand": "auto_expand",
                },
                deprecated_properties=["foreground", "background"],
                new_properties={"role": "outlined"},
                role_mapping="outlined",
                event_mappings={"change": "change"},
                compatibility_notes=["M3 TextArea supports outlined styling"],
            ),
            "DropDown": ComponentMapping(
                classic_type="DropDown",
                m3_type="DropDown",
                property_mappings={
                    "items": "items",
                    "selected_value": "selected_value",
                    "placeholder": "placeholder",
                    "enabled": "enabled",
                },
                deprecated_properties=["foreground", "background"],
                new_properties={"role": "outlined"},
                role_mapping="outlined",
                event_mappings={"change": "change"},
                compatibility_notes=["M3 DropDown uses outlined style by default"],
            ),
            "CheckBox": ComponentMapping(
                classic_type="CheckBox",
                m3_type="CheckBox",
                property_mappings={
                    "checked": "checked",
                    "text": "text",
                    "enabled": "enabled",
                },
                deprecated_properties=["foreground"],
                new_properties={"role": "default"},
                role_mapping="default",
                event_mappings={"change": "change"},
                compatibility_notes=["M3 CheckBox follows theme automatically"],
            ),
            "RadioButton": ComponentMapping(
                classic_type="RadioButton",
                m3_type="RadioButton",
                property_mappings={
                    "selected": "selected",
                    "text": "text",
                    "group_name": "group_name",
                    "enabled": "enabled",
                },
                deprecated_properties=["foreground"],
                new_properties={"role": "default"},
                role_mapping="default",
                event_mappings={"clicked": "clicked"},
                compatibility_notes=["M3 RadioButton uses theme colors"],
            ),
            # Display components
            "Label": ComponentMapping(
                classic_type="Label",
                m3_type="Label",
                property_mappings={
                    "text": "text",
                    "visible": "visible",
                    "icon": "icon",
                },
                deprecated_properties=["foreground", "background"],
                new_properties={"role": "default"},
                role_mapping="default",
                event_mappings={},
                compatibility_notes=["Use role and typography theme for styling"],
            ),
            "Image": ComponentMapping(
                classic_type="Image",
                m3_type="Image",
                property_mappings={
                    "source": "source",
                    "visible": "visible",
                    "height": "height",
                },
                deprecated_properties=[],
                new_properties={},
                role_mapping=None,
                event_mappings={"click": "click"},
                compatibility_notes=["Image component unchanged in M3"],
            ),
            # Container components
            "ColumnPanel": ComponentMapping(
                classic_type="ColumnPanel",
                m3_type="ColumnPanel",
                property_mappings={
                    "col_widths": "col_widths",
                    "spacing": "spacing",
                },
                deprecated_properties=["background"],
                new_properties={"role": "card"},
                role_mapping="card",
                event_mappings={},
                compatibility_notes=["Consider migrating to Layout for modern apps"],
            ),
            "FlowPanel": ComponentMapping(
                classic_type="FlowPanel",
                m3_type="FlowPanel",
                property_mappings={
                    "spacing": "spacing",
                    "align": "align",
                },
                deprecated_properties=["background"],
                new_properties={},
                role_mapping=None,
                event_mappings={},
                compatibility_notes=["FlowPanel works well with M3"],
            ),
            "Card": ComponentMapping(
                classic_type="Card",
                m3_type="Card",
                property_mappings={
                    "visible": "visible",
                },
                deprecated_properties=["background"],
                new_properties={"role": "elevated-card"},
                role_mapping="elevated-card",
                event_mappings={},
                compatibility_notes=["Use elevated-card or outlined-card role"],
            ),
            # Data components
            "DataGrid": ComponentMapping(
                classic_type="DataGrid",
                m3_type="DataGrid",
                property_mappings={
                    "columns": "columns",
                    "rows_per_page": "rows_per_page",
                    "visible": "visible",
                },
                deprecated_properties=[],
                new_properties={"role": "default"},
                role_mapping="default",
                event_mappings={"row_click": "row_click"},
                compatibility_notes=["Consider Tabulator for better M3 integration"],
            ),
            "RepeatingPanel": ComponentMapping(
                classic_type="RepeatingPanel",
                m3_type="RepeatingPanel",
                property_mappings={
                    "item_template": "item_template",
                    "items": "items",
                },
                deprecated_properties=[],
                new_properties={},
                role_mapping=None,
                event_mappings={},
                compatibility_notes=["RepeatingPanel compatible with M3"],
            ),
        }

    def _initialize_property_transformations(self) -> Dict[str, Any]:
        """Initialize property transformation rules."""
        return {
            "color_properties": {
                "foreground": lambda val: {"role": "primary-color"},
                "background": lambda val: {"role": "surface"},
            },
            "sizing_properties": {
                "width": lambda val: val,
                "height": lambda val: val,
            },
            "boolean_properties": {
                "enabled": lambda val: val,
                "visible": lambda val: val,
                "checked": lambda val: val,
            },
        }

    def _initialize_theme_rules(self) -> Dict[str, Any]:
        """Initialize theme integration rules."""
        return {
            "button_roles": [
                "filled-button",
                "outlined-button",
                "elevated-button",
                "tonal-button",
                "text-button",
            ],
            "card_roles": ["elevated-card", "filled-card", "outlined-card"],
            "input_roles": ["outlined", "filled"],
            "color_roles": [
                "primary-color",
                "secondary-color",
                "error-color",
                "surface",
                "background",
            ],
        }

    def _initialize_validation_rules(self) -> Dict[str, Any]:
        """Initialize validation rules for upgraded components."""
        return {
            "required_properties": {
                "Button": ["text"],
                "TextBox": [],
                "Label": [],
            },
            "deprecated_usage": [
                "foreground",
                "background",
            ],
        }

    def _load_form_yaml(self, form_path: Path) -> Dict[str, Any]:
        """Load form YAML file."""
        content = self.file_system.read_text(str(form_path))
        return yaml.safe_load(content)

    def _analyze_components_for_upgrade(
        self, form_yaml: Dict[str, Any]
    ) -> List[Dict[str, Any]]:
        """Analyze all components in form for upgrade."""
        components = []

        def extract_components(node):
            if isinstance(node, dict):
                if "type" in node:
                    components.append(node)
                for value in node.values():
                    extract_components(value)
            elif isinstance(node, list):
                for item in node:
                    extract_components(item)

        extract_components(form_yaml)
        return components

    def _create_upgrade_plan(
        self,
        components: List[Dict[str, Any]],
        strategy: UpgradeStrategy,
    ) -> List[Dict[str, Any]]:
        """Create upgrade plan for components."""
        plan = []

        for component in components:
            component_type = component.get("type", "")

            if component_type in self._component_mappings:
                mapping = self._component_mappings[component_type]

                plan.append(
                    {
                        "component": component,
                        "mapping": mapping,
                        "strategy": strategy,
                    }
                )

        return plan

    def _execute_upgrade_plan(
        self,
        form_yaml: Dict[str, Any],
        upgrade_plan: List[Dict[str, Any]],
        preserve_compatibility: bool,
    ) -> Dict[str, Any]:
        """Execute the upgrade plan."""
        upgraded_form = copy.deepcopy(form_yaml)

        for plan_item in upgrade_plan:
            component = plan_item["component"]
            mapping = plan_item["mapping"]

            self._upgrade_component(
                upgraded_form, component, mapping, preserve_compatibility
            )

        return upgraded_form

    def _upgrade_component(
        self,
        form_yaml: Dict[str, Any],
        component: Dict[str, Any],
        mapping: ComponentMapping,
        preserve_compatibility: bool,
    ):
        """Upgrade a single component."""
        component_name = component.get("name", "")

        if not component_name:
            return

        # Find component in form
        target_component = self._find_component_by_name(form_yaml, component_name)

        if target_component and component_name:
            # Update component type if needed
            if mapping.m3_type != mapping.classic_type:
                target_component["type"] = mapping.m3_type

            # Migrate properties
            self._migrate_properties(target_component, mapping)

            # Add new M3 properties
            if mapping.new_properties:
                for key, value in mapping.new_properties.items():
                    if key not in target_component.get("properties", {}):
                        if "properties" not in target_component:
                            target_component["properties"] = {}
                        target_component["properties"][key] = value

            # Update events
            self._migrate_events(target_component, mapping)

            # Remove deprecated properties
            if not preserve_compatibility:
                self._remove_deprecated_properties(target_component, mapping)

    def _migrate_properties(self, component: Dict[str, Any], mapping: ComponentMapping):
        """Migrate component properties."""
        if "properties" not in component:
            return

        properties = component["properties"]

        for old_prop, new_prop in mapping.property_mappings.items():
            if old_prop in properties and old_prop != new_prop:
                properties[new_prop] = properties.pop(old_prop)

    def _migrate_events(self, component: Dict[str, Any], mapping: ComponentMapping):
        """Migrate event handlers."""
        if "event_bindings" not in component:
            return

        events = component["event_bindings"]

        for old_event, new_event in mapping.event_mappings.items():
            if old_event in events and old_event != new_event:
                events[new_event] = events.pop(old_event)

    def _remove_deprecated_properties(
        self, component: Dict[str, Any], mapping: ComponentMapping
    ):
        """Remove deprecated properties."""
        if "properties" not in component:
            return

        properties = component["properties"]

        for deprecated_prop in mapping.deprecated_properties:
            if deprecated_prop in properties:
                del properties[deprecated_prop]

    def _validate_upgraded_form(self, form_yaml: Dict[str, Any]) -> List[str]:
        """Validate upgraded form."""
        errors = []

        components = self._analyze_components_for_upgrade(form_yaml)

        for component in components:
            component_type = component.get("type", "")

            if component_type in self._validation_rules.get("required_properties", {}):
                required = self._validation_rules["required_properties"][component_type]
                properties = component.get("properties", {})

                for req_prop in required:
                    if req_prop not in properties:
                        errors.append(
                            f"Component '{component.get('name')}' missing required property: {req_prop}"
                        )

        return errors

    def _save_upgraded_form(self, form_path: Path, form_yaml: Dict[str, Any]):
        """Save upgraded form YAML."""
        yaml_content = yaml.dump(form_yaml, default_flow_style=False, sort_keys=False)
        self.file_system.write_text(str(form_path), yaml_content)

    def _generate_upgrade_result(
        self,
        form_path: Path,
        upgrade_plan: List[Dict[str, Any]],
        upgraded_form: Dict[str, Any],
        validation_errors: List[str],
        strategy: UpgradeStrategy,
        preserve_compatibility: bool,
    ) -> UpgradeResult:
        """Generate upgrade result."""
        components_upgraded = len(upgrade_plan)
        properties_migrated = sum(
            len(item["mapping"].property_mappings) for item in upgrade_plan
        )
        events_updated = sum(
            len(item["mapping"].event_mappings) for item in upgrade_plan
        )

        breaking_changes = []
        warnings = []

        for item in upgrade_plan:
            mapping = item["mapping"]
            if mapping.deprecated_properties and not preserve_compatibility:
                breaking_changes.append(
                    f"Removed deprecated properties from {item['component'].get('name')}: {', '.join(mapping.deprecated_properties)}"
                )

            if mapping.compatibility_notes:
                warnings.extend(mapping.compatibility_notes)

        return UpgradeResult(
            success=len(validation_errors) == 0,
            original_component=str(form_path),
            upgraded_component=str(form_path),
            components_upgraded=components_upgraded,
            properties_migrated=properties_migrated,
            events_updated=events_updated,
            breaking_changes=breaking_changes,
            warnings=warnings,
            backward_compatible=preserve_compatibility,
            upgrade_strategy=strategy,
            validation_errors=validation_errors,
        )

    def _assess_upgrade_complexity(
        self, component: Dict[str, Any], mapping: ComponentMapping
    ) -> str:
        """Assess complexity of upgrading a component."""
        complexity_score = 0

        # Check deprecated properties
        properties = component.get("properties", {})
        for deprecated in mapping.deprecated_properties:
            if deprecated in properties:
                complexity_score += 1

        # Check custom properties
        if "style" in properties:
            complexity_score += 2

        # Determine complexity level
        if complexity_score == 0:
            return "simple"
        elif complexity_score <= 2:
            return "moderate"
        else:
            return "complex"

    def _estimate_upgrade_effort(
        self, upgradeable: int, manual: int, incompatible: int
    ) -> str:
        """Estimate effort required for upgrade."""
        total_score = (upgradeable * 1) + (manual * 3) + (incompatible * 5)

        if total_score < 5:
            return "low (< 1 hour)"
        elif total_score < 15:
            return "medium (1-3 hours)"
        elif total_score < 30:
            return "high (3-8 hours)"
        else:
            return "very high (> 8 hours)"

    def _plan_property_changes(
        self, component: Dict[str, Any], mapping: ComponentMapping
    ) -> List[Dict[str, Any]]:
        """Plan property changes for component."""
        changes = []
        properties = component.get("properties", {})

        for old_prop, new_prop in mapping.property_mappings.items():
            if old_prop in properties:
                changes.append(
                    {
                        "action": "rename" if old_prop != new_prop else "keep",
                        "from": old_prop,
                        "to": new_prop,
                        "value": properties[old_prop],
                    }
                )

        for deprecated in mapping.deprecated_properties:
            if deprecated in properties:
                changes.append(
                    {
                        "action": "remove",
                        "property": deprecated,
                        "reason": "deprecated in M3",
                    }
                )

        for new_prop, value in mapping.new_properties.items():
            changes.append(
                {
                    "action": "add",
                    "property": new_prop,
                    "value": value,
                }
            )

        return changes

    def _plan_event_changes(
        self, component: Dict[str, Any], mapping: ComponentMapping
    ) -> List[Dict[str, Any]]:
        """Plan event handler changes."""
        changes = []
        events = component.get("event_bindings", {})

        for old_event, new_event in mapping.event_mappings.items():
            if old_event in events:
                changes.append(
                    {
                        "action": "rename" if old_event != new_event else "keep",
                        "from": old_event,
                        "to": new_event,
                        "handler": events[old_event],
                    }
                )

        return changes

    def _identify_breaking_changes(
        self, component: Dict[str, Any], mapping: ComponentMapping
    ) -> List[str]:
        """Identify breaking changes for component."""
        breaking = []
        properties = component.get("properties", {})

        for deprecated in mapping.deprecated_properties:
            if deprecated in properties:
                breaking.append(f"Property '{deprecated}' will be removed")

        if mapping.m3_type != mapping.classic_type:
            breaking.append(
                f"Component type changes from {mapping.classic_type} to {mapping.m3_type}"
            )

        return breaking

    def _estimate_component_upgrade_time(
        self, component: Dict[str, Any], mapping: ComponentMapping
    ) -> int:
        """Estimate time in minutes to upgrade component."""
        base_time = 5

        # Add time for property changes
        base_time += len(mapping.property_mappings) * 2
        base_time += len(mapping.deprecated_properties) * 3

        # Add time for event changes
        base_time += len(mapping.event_mappings) * 2

        # Add time for custom styling
        if "style" in component.get("properties", {}):
            base_time += 15

        return base_time

    def _identify_prerequisites(self, components: List[Dict[str, Any]]) -> List[str]:
        """Identify prerequisites for upgrade."""
        prerequisites = []

        has_data_grid = any(c.get("type") == "DataGrid" for c in components)
        if has_data_grid:
            prerequisites.append(
                "Consider adding Tabulator dependency for better M3 integration"
            )

        prerequisites.append("Ensure Material 3 theme is installed")
        prerequisites.append("Backup form before upgrading")

        return prerequisites

    def _identify_post_upgrade_tasks(
        self, components: List[Dict[str, Any]]
    ) -> List[str]:
        """Identify post-upgrade tasks."""
        return [
            "Test all component interactions",
            "Verify theme styling is applied correctly",
            "Check responsive behavior on different screen sizes",
            "Update any custom CSS to work with M3 classes",
            "Test event handlers still work correctly",
        ]

    def _find_component_by_name(
        self, form_yaml: Dict[str, Any], name: str
    ) -> Optional[Dict[str, Any]]:
        """Find component by name in form YAML."""

        def search(node):
            if isinstance(node, dict):
                if node.get("name") == name:
                    return node
                for value in node.values():
                    result = search(value)
                    if result:
                        return result
            elif isinstance(node, list):
                for item in node:
                    result = search(item)
                    if result:
                        return result
            return None

        return search(form_yaml)
