"""Component operations validator for enhanced validation and impact analysis."""

from __future__ import annotations

from typing import Any

from ..models.project_context import ProjectContext
from ..validators.component_validator import ComponentValidator


class ComponentOperationsValidator:
    """Enhanced validation for component operations with impact analysis."""

    def __init__(self):
        self.base_validator = ComponentValidator()

    def validate_add_operation(
        self,
        form_config: dict[str, Any],
        component_config: dict[str, Any],
        project_context: ProjectContext | None = None,
    ) -> tuple[bool, list[str], list[str]]:
        """Validate component addition with impact analysis."""
        errors = []
        warnings = []

        # Base validation
        is_valid, base_errors = self.base_validator.validate_component(component_config)
        if not is_valid:
            errors.extend(base_errors)

        # Context-aware validation
        if project_context:
            context_errors, context_warnings = self._validate_context_compatibility(
                component_config, project_context
            )
            errors.extend(context_errors)
            warnings.extend(context_warnings)

        # Layout validation
        layout_errors, layout_warnings = self._validate_layout_constraints(
            form_config, component_config
        )
        errors.extend(layout_errors)
        warnings.extend(layout_warnings)

        # Naming conflicts
        naming_errors = self._validate_naming_conflicts(form_config, component_config)
        errors.extend(naming_errors)

        return len(errors) == 0, errors, warnings

    def validate_update_operation(
        self,
        form_config: dict[str, Any],
        component_name: str,
        new_properties: dict[str, Any],
        project_context: ProjectContext | None = None,
    ) -> tuple[bool, list[str], list[str], dict[str, Any]]:
        """Validate component update with impact analysis."""
        errors = []
        warnings = []
        impact_analysis = {}

        # Find existing component
        existing_component = self._find_component(form_config, component_name)
        if not existing_component:
            errors.append(f"Component '{component_name}' not found")
            return False, errors, warnings, impact_analysis

        # Create temporary component for validation
        temp_component = existing_component.copy()
        temp_component["properties"] = new_properties

        # Base validation
        is_valid, base_errors = self.base_validator.validate_component(temp_component)
        if not is_valid:
            errors.extend(base_errors)

        # Context-aware validation
        if project_context:
            context_errors, context_warnings = self._validate_context_compatibility(
                temp_component, project_context
            )
            errors.extend(context_errors)
            warnings.extend(context_warnings)

        # Impact analysis
        impact_analysis = self._analyze_update_impact(
            form_config, existing_component, new_properties
        )

        # Breaking change detection
        breaking_changes = self._detect_breaking_changes(
            existing_component, new_properties
        )
        if breaking_changes:
            warnings.extend(
                f"Breaking change detected: {change}" for change in breaking_changes
            )

        return len(errors) == 0, errors, warnings, impact_analysis

    def validate_remove_operation(
        self,
        form_config: dict[str, Any],
        component_name: str,
        project_context: ProjectContext | None = None,
    ) -> tuple[bool, list[str], list[str], dict[str, Any]]:
        """Validate component removal with dependency analysis."""
        errors = []
        warnings = []
        impact_analysis = {}

        # Find existing component
        existing_component = self._find_component(form_config, component_name)
        if not existing_component:
            errors.append(f"Component '{component_name}' not found")
            return False, errors, warnings, impact_analysis

        # Dependency analysis
        dependencies = self._analyze_component_dependencies(form_config, component_name)
        if dependencies:
            errors.append(
                f"Cannot remove component '{component_name}' - it has dependencies: {', '.join(dependencies)}"
            )

        # Impact analysis
        impact_analysis = self._analyze_removal_impact(form_config, existing_component)

        # Critical component warnings
        if self._is_critical_component(existing_component):
            warnings.append(
                f"Component '{component_name}' appears to be critical - removal may break functionality"
            )

        return len(errors) == 0, errors, warnings, impact_analysis

    def validate_move_operation(
        self,
        form_config: dict[str, Any],
        component_name: str,
        new_position: str,
        project_context: ProjectContext | None = None,
    ) -> tuple[bool, list[str], list[str]]:
        """Validate component move with layout constraint validation."""
        errors = []
        warnings = []

        # Find existing component
        existing_component = self._find_component(form_config, component_name)
        if not existing_component:
            errors.append(f"Component '{component_name}' not found")
            return False, errors, warnings

        # Position validation
        position_errors = self._validate_position_format(new_position)
        errors.extend(position_errors)

        # Layout constraint validation
        if project_context:
            constraint_errors = self._validate_layout_constraints_for_move(
                form_config, existing_component, new_position, project_context
            )
            errors.extend(constraint_errors)

        # Collision detection
        collision_warnings = self._detect_position_collisions(
            form_config, component_name, new_position
        )
        warnings.extend(collision_warnings)

        return len(errors) == 0, errors, warnings

    def _validate_context_compatibility(
        self, component_config: dict[str, Any], project_context: ProjectContext
    ) -> tuple[list[str], list[str]]:
        """Validate component compatibility with project context."""
        errors = []
        warnings = []

        component_type = component_config.get("type", "")
        properties = component_config.get("properties", {})

        # Theme compatibility
        if project_context.theme_type == "classic":
            role = properties.get("role", "")
            if role and "outlined" in role:
                warnings.append(
                    f"Material 3 role '{role}' may not be compatible with classic theme"
                )

        # Layout system compatibility
        if project_context.layout_system == "layouts":
            if component_type in ["XYPanel"]:
                errors.append(
                    f"Component type '{component_type}' not compatible with modern layout system"
                )

        # Reactive compatibility
        if not project_context.has_reactive:
            data_bindings = component_config.get("data_bindings", [])
            if data_bindings:
                warnings.append(
                    "Data bindings found but project doesn't use reactive library"
                )

        return errors, warnings

    def _validate_layout_constraints(
        self, form_config: dict[str, Any], component_config: dict[str, Any]
    ) -> tuple[list[str], list[str]]:
        """Validate layout constraints for component addition."""
        errors = []
        warnings = []

        layout_props = component_config.get("layout_properties", {})
        grid_position = layout_props.get("grid_position")

        if grid_position:
            # Validate grid position format
            position_errors = self._validate_position_format(grid_position)
            errors.extend(position_errors)

            # Check for position collisions
            collision_warnings = self._detect_position_collisions(
                form_config, "", grid_position
            )
            warnings.extend(collision_warnings)

        return errors, warnings

    def _validate_naming_conflicts(
        self, form_config: dict[str, Any], component_config: dict[str, Any]
    ) -> list[str]:
        """Validate component naming conflicts."""
        errors = []
        component_name = component_config.get("name", "")

        if not component_name:
            errors.append("Component name is required")
            return errors

        # Check for duplicate names
        existing_names = [
            comp.get("name", "") for comp in form_config.get("components", [])
        ]
        if component_name in existing_names:
            errors.append(f"Component name '{component_name}' already exists in form")

        return errors

    def _validate_position_format(self, position: str) -> list[str]:
        """Validate grid position format."""
        errors = []

        if not position:
            errors.append("Grid position is required")
            return errors

        try:
            parts = position.split(",")
            if len(parts) != 2:
                errors.append("Grid position must be in format 'x,y'")
                return errors

            x, y = parts
            int(x.strip())
            int(y.strip())
        except ValueError:
            errors.append("Grid position coordinates must be integers")

        return errors

    def _detect_position_collisions(
        self, form_config: dict[str, Any], exclude_component: str, new_position: str
    ) -> list[str]:
        """Detect position collisions with existing components."""
        warnings = []

        for component in form_config.get("components", []):
            if component.get("name") == exclude_component:
                continue

            layout_props = component.get("layout_properties", {})
            existing_position = layout_props.get("grid_position", "")

            if existing_position == new_position:
                component_name = component.get("name", "unnamed")
                warnings.append(
                    f"Position {new_position} already occupied by component '{component_name}'"
                )

        return warnings

    def _analyze_update_impact(
        self,
        form_config: dict[str, Any],
        existing_component: dict[str, Any],
        new_properties: dict[str, Any],
    ) -> dict[str, Any]:
        """Analyze impact of component property updates."""
        impact = {
            "affected_bindings": [],
            "style_changes": [],
            "behavior_changes": [],
            "compatibility_issues": [],
        }

        old_properties = existing_component.get("properties", {})

        # Check for property changes
        for prop_name, new_value in new_properties.items():
            old_value = old_properties.get(prop_name)
            if old_value != new_value:
                if prop_name in ["text", "visible", "enabled"]:
                    impact["style_changes"].append(
                        f"{prop_name}: {old_value} → {new_value}"
                    )
                elif prop_name in ["role"]:
                    impact["style_changes"].append(
                        f"{prop_name}: {old_value} → {new_value}"
                    )
                elif prop_name in ["items", "selected_value"]:
                    impact["behavior_changes"].append(
                        f"{prop_name}: {old_value} → {new_value}"
                    )

        # Check for affected bindings
        component_name = existing_component.get("name", "")
        for component in form_config.get("components", []):
            if component.get("name") == component_name:
                continue

            # Check event bindings
            event_bindings = component.get("event_bindings", {})
            for event, handler in event_bindings.items():
                if component_name in handler:
                    impact["affected_bindings"].append(
                        f"Event handler in {component.get('name', 'unknown')}: {event}"
                    )

            # Check data bindings
            data_bindings = component.get("data_bindings", [])
            for binding in data_bindings:
                if component_name in binding.get("code", ""):
                    impact["affected_bindings"].append(
                        f"Data binding in {component.get('name', 'unknown')}"
                    )

        return impact

    def _detect_breaking_changes(
        self, existing_component: dict[str, Any], new_properties: dict[str, Any]
    ) -> list[str]:
        """Detect breaking changes in component updates."""
        breaking_changes = []

        old_properties = existing_component.get("properties", {})

        # Check for removal of required properties
        component_type = existing_component.get("type", "")
        if component_type == "Button":
            if "text" in old_properties and "text" not in new_properties:
                breaking_changes.append("Removing 'text' property from Button")
        elif component_type == "Label":
            if "text" in old_properties and "text" not in new_properties:
                breaking_changes.append("Removing 'text' property from Label")
        elif component_type == "DropDown":
            if "items" in old_properties and "items" not in new_properties:
                breaking_changes.append("Removing 'items' property from DropDown")

        # Check for type changes
        for prop_name, new_value in new_properties.items():
            if prop_name in old_properties:
                old_value = old_properties[prop_name]
                if type(old_value) != type(new_value):
                    breaking_changes.append(
                        f"Type change for '{prop_name}': {type(old_value).__name__} → {type(new_value).__name__}"
                    )

        return breaking_changes

    def _analyze_component_dependencies(
        self, form_config: dict[str, Any], component_name: str
    ) -> list[str]:
        """Analyze dependencies for a component."""
        dependencies = []

        for component in form_config.get("components", []):
            if component.get("name") == component_name:
                continue

            # Check event bindings
            event_bindings = component.get("event_bindings", {})
            for event, handler in event_bindings.items():
                if component_name in handler:
                    dependencies.append(
                        f"Event handler in {component.get('name', 'unknown')}"
                    )

            # Check data bindings
            data_bindings = component.get("data_bindings", [])
            for binding in data_bindings:
                if component_name in binding.get("code", ""):
                    dependencies.append(
                        f"Data binding in {component.get('name', 'unknown')}"
                    )

        return dependencies

    def _analyze_removal_impact(
        self, form_config: dict[str, Any], component: dict[str, Any]
    ) -> dict[str, Any]:
        """Analyze impact of component removal."""
        impact = {
            "broken_bindings": [],
            "lost_functionality": [],
            "ui_changes": [],
        }

        component_name = component.get("name", "")
        component_type = component.get("type", "")

        # Find broken bindings
        for comp in form_config.get("components", []):
            if comp.get("name") == component_name:
                continue

            event_bindings = comp.get("event_bindings", {})
            for event, handler in event_bindings.items():
                if component_name in handler:
                    impact["broken_bindings"].append(
                        f"Event handler in {comp.get('name', 'unknown')}: {event}"
                    )

            data_bindings = comp.get("data_bindings", [])
            for binding in data_bindings:
                if component_name in binding.get("code", ""):
                    impact["broken_bindings"].append(
                        f"Data binding in {comp.get('name', 'unknown')}"
                    )

        # Analyze lost functionality
        if component_type == "Button":
            impact["lost_functionality"].append("Button click functionality")
        elif component_type == "TextBox":
            impact["lost_functionality"].append("Text input capability")
        elif component_type == "DataGrid":
            impact["lost_functionality"].append("Data display and interaction")

        # UI changes
        properties = component.get("properties", {})
        if properties.get("text"):
            impact["ui_changes"].append(f"Remove text: {properties['text']}")
        if properties.get("visible", True):
            impact["ui_changes"].append("Remove visible component")

        return impact

    def _is_critical_component(self, component: dict[str, Any]) -> bool:
        """Determine if component is critical for form functionality."""
        component_type = component.get("type", "")
        properties = component.get("properties", {})
        event_bindings = component.get("event_bindings", {})

        # Components with event bindings are likely critical
        if event_bindings:
            return True

        # Submit buttons are critical
        if (
            component_type == "Button"
            and "submit" in properties.get("text", "").lower()
        ):
            return True

        # Data grids with data are critical
        if component_type == "DataGrid" and properties.get("rows"):
            return True

        return False

    def _validate_layout_constraints_for_move(
        self,
        form_config: dict[str, Any],
        component: dict[str, Any],
        new_position: str,
        project_context: ProjectContext,
    ) -> list[str]:
        """Validate layout constraints specific to move operations."""
        errors = []

        # Modern layout systems have stricter constraints
        if project_context.layout_system == "layouts":
            container_type = form_config.get("container", {}).get("type", "")

            # GridPanel has specific constraints
            if container_type == "GridPanel":
                try:
                    x, y = map(int, new_position.split(","))
                    # Check if position is within reasonable bounds
                    if x < 0 or y < 0:
                        errors.append("Grid position cannot be negative")
                    if x > 20 or y > 20:
                        errors.append("Grid position exceeds reasonable bounds")
                except ValueError:
                    errors.append("Invalid grid position format")

        return errors

    def _find_component(
        self, form_config: dict[str, Any], component_name: str
    ) -> dict[str, Any] | None:
        """Find a component by name in form configuration."""
        for component in form_config.get("components", []):
            if component.get("name") == component_name:
                return component
        return None
