"""Component validator for Anvil forms."""

from __future__ import annotations

from typing import Any, Dict, List


class ComponentValidator:
    """Validate Anvil component configurations."""

    def __init__(self):
        self._valid_components = {
            "Button",
            "TextBox",
            "Label",
            "CheckBox",
            "RadioButton",
            "DropDown",
            "TextArea",
            "FileLoader",
            "Image",
            "Link",
            "Spacer",
            "ColumnPanel",
            "FlowPanel",
            "GridPanel",
            "XYPanel",
            "RepeatingPanel",
            "DataGrid",
            "LinearPanel",
            "Card",
            "AlertDialog",
            "ConfirmDialog",
            "InfoDialog",
        }

        self._valid_roles = {
            # Material 3 Button Roles
            "outlined-button",
            "filled-button",
            "tonal-button",
            "text-button",
            "elevated-button",
            # Legacy Button Roles
            "raised-button",
            "flat-button",
            "contained-button",
            # Material 3 Card Roles
            "outlined-card",
            "filled-card",
            "elevated-card",
            # Material 3 Text Field Roles
            "outlined-text-field",
            "filled-text-field",
            # Legacy Text Field Roles
            "standard-text-field",
            # Color Roles
            "primary-color",
            "secondary-color",
            "tertiary-color",
            "success-color",
            "warning-color",
            "error-color",
            "info-color",
            "surface-color",
            "background-color",
            # Material 3 Component Roles
            "floating-action-button",
            "icon-button",
            "chip",
            "data-table",
            "navigation-drawer",
            "navigation-rail",
            "top-app-bar",
            "bottom-app-bar",
            "list-item",
        }

        # Component property definitions for validation
        self._component_properties = {
            "Button": {
                "required": ["text"],
                "optional": ["icon", "role", "enabled", "visible", "tooltip"],
                "events": ["click", "show", "hide", "focus", "blur"],
            },
            "TextBox": {
                "required": [],
                "optional": [
                    "text",
                    "placeholder",
                    "role",
                    "enabled",
                    "visible",
                    "max_length",
                ],
                "events": ["change", "focus", "blur", "pressed_enter", "lost_focus"],
            },
            "Label": {
                "required": ["text"],
                "optional": [
                    "role",
                    "visible",
                    "align",
                    "font_size",
                    "bold",
                    "italic",
                    "underline",
                ],
                "events": [],
            },
            "CheckBox": {
                "required": ["text"],
                "optional": ["checked", "enabled", "visible", "role"],
                "events": ["change", "focus", "blur"],
            },
            "DropDown": {
                "required": ["items"],
                "optional": [
                    "selected_value",
                    "placeholder",
                    "enabled",
                    "visible",
                    "role",
                ],
                "events": ["change", "focus", "blur"],
            },
            "ColumnPanel": {
                "required": [],
                "optional": ["spacing", "padding", "align", "role"],
                "events": ["show", "hide"],
            },
            "FlowPanel": {
                "required": [],
                "optional": ["spacing", "padding", "align", "wrap", "role"],
                "events": ["show", "hide"],
            },
            "GridPanel": {
                "required": [],
                "optional": ["spacing", "padding", "role"],
                "events": ["show", "hide"],
            },
            "XYPanel": {
                "required": [],
                "optional": ["role"],
                "events": ["show", "hide"],
            },
            "DataGrid": {
                "required": [],
                "optional": [
                    "columns",
                    "rows",
                    "auto_header",
                    "show_row_numbers",
                    "role",
                ],
                "events": ["row_selected", "cell_edited", "sort", "filter"],
            },
            "Image": {
                "required": [],
                "optional": ["source", "width", "height", "role", "visible"],
                "events": ["click", "show", "hide"],
            },
            "FileLoader": {
                "required": [],
                "optional": [
                    "text",
                    "enabled",
                    "visible",
                    "file_types",
                    "multiple",
                    "role",
                ],
                "events": ["change", "focus", "blur"],
            },
        }

    def validate_component(self, component: Dict[str, Any]) -> tuple[bool, List[str]]:
        """Validate a component configuration."""
        errors = []

        if "type" not in component:
            errors.append("Component type is required")
            return False, errors

        component_type = component["type"]
        if component_type not in self._valid_components:
            errors.append(f"Invalid component type: {component_type}")

        if not self._is_container_type(component_type) and "name" not in component:
            errors.append("Component name is required for non-containers")

        self._validate_properties(component, errors)
        self._validate_layout_properties(component, errors)
        self._validate_event_bindings(component, errors)
        self._validate_data_bindings(component, errors)

        return len(errors) == 0, errors

    def validate_form_config(
        self, form_config: Dict[str, Any]
    ) -> tuple[bool, List[str]]:
        """Validate complete form configuration."""
        errors = []

        if "container" not in form_config:
            errors.append("Form container is required")

        if "components" not in form_config:
            errors.append("Form components list is required")
            return False, errors

        container = form_config["container"]
        if "type" not in container:
            errors.append("Container type is required")

        components = form_config["components"]
        if not isinstance(components, list):
            errors.append("Components must be a list")
            return False, errors

        for i, component in enumerate(components):
            is_valid, component_errors = self.validate_component(component)
            if not is_valid:
                errors.extend([f"Component {i}: {error}" for error in component_errors])

        return len(errors) == 0, errors

    def _is_container_type(self, component_type: str) -> bool:
        """Check if component type is a container."""
        return component_type in {
            "ColumnPanel",
            "FlowPanel",
            "GridPanel",
            "XYPanel",
            "RepeatingPanel",
            "LinearPanel",
            "Card",
        }

    def _validate_properties(
        self, component: Dict[str, Any], errors: List[str]
    ) -> None:
        """Validate component properties."""
        properties = component.get("properties", {})
        if not isinstance(properties, dict):
            errors.append("Properties must be a dictionary")
            return

        role = properties.get("role")
        if role and role not in self._valid_roles:
            errors.append(f"Invalid role: {role}")

    def _validate_layout_properties(
        self, component: Dict[str, Any], errors: List[str]
    ) -> None:
        """Validate layout properties."""
        layout_props = component.get("layout_properties", {})
        if not isinstance(layout_props, dict):
            errors.append("Layout properties must be a dictionary")
            return

        if "grid_position" in layout_props:
            grid_pos = layout_props["grid_position"]
            if not isinstance(grid_pos, str) or "," not in grid_pos:
                errors.append("Grid position must be in format 'x,y'")

    def _validate_event_bindings(
        self, component: Dict[str, Any], errors: List[str]
    ) -> None:
        """Validate event bindings."""
        event_bindings = component.get("event_bindings", {})
        if not isinstance(event_bindings, dict):
            errors.append("Event bindings must be a dictionary")
            return

    def _validate_data_bindings(
        self, component: Dict[str, Any], errors: List[str]
    ) -> None:
        """Validate data bindings."""
        data_bindings = component.get("data_bindings", [])
        if not isinstance(data_bindings, list):
            errors.append("Data bindings must be a list")
            return

        for binding in data_bindings:
            if not isinstance(binding, dict):
                errors.append("Each data binding must be a dictionary")
                continue

            if "property" not in binding:
                errors.append("Data binding property is required")

            if "code" not in binding:
                errors.append("Data binding code is required")

    def validate_component_properties(
        self, component_type: str, properties: Dict[str, Any]
    ) -> tuple[bool, List[str]]:
        """Validate component properties against component specification."""
        errors = []

        if component_type not in self._component_properties:
            errors.append(f"Unknown component type: {component_type}")
            return False, errors

        spec = self._component_properties[component_type]

        # Check required properties
        for required_prop in spec["required"]:
            if required_prop not in properties:
                errors.append(
                    f"Required property '{required_prop}' missing for {component_type}"
                )

        # Check property types and values
        for prop_name, prop_value in properties.items():
            if not self._validate_property_value(component_type, prop_name, prop_value):
                errors.append(
                    f"Invalid property '{prop_name}' value for {component_type}: {prop_value}"
                )

        return len(errors) == 0, errors

    def validate_event_binding(
        self, component_type: str, event_name: str, handler_name: str
    ) -> bool:
        """Validate event binding against component capabilities."""
        if component_type not in self._component_properties:
            return False

        valid_events = self._component_properties[component_type]["events"]
        return event_name in valid_events

    def validate_layout_configuration(
        self, layout_type: str, layout_config: Dict[str, Any]
    ) -> tuple[bool, List[str]]:
        """Validate layout configuration."""
        errors = []

        if layout_type == "grid":
            if "columns" in layout_config:
                columns = layout_config["columns"]
                if not isinstance(columns, int) or columns <= 0:
                    errors.append("Grid columns must be a positive integer")

            if "row_height" in layout_config:
                row_height = layout_config["row_height"]
                if not isinstance(row_height, (int, str)) or (
                    isinstance(row_height, int) and row_height <= 0
                ):
                    errors.append("Grid row height must be positive")

        elif layout_type == "linear":
            if "direction" in layout_config:
                direction = layout_config["direction"]
                if direction not in ["vertical", "horizontal"]:
                    errors.append(
                        "Linear layout direction must be 'vertical' or 'horizontal'"
                    )

        return len(errors) == 0, errors

    def validate_data_binding_syntax(self, binding_code: str) -> tuple[bool, List[str]]:
        """Validate data binding code syntax."""
        errors = []

        # Basic syntax checks
        if not binding_code.strip():
            errors.append("Data binding code cannot be empty")
            return False, errors

        # Check for common patterns
        if "self." in binding_code and not any(
            method in binding_code for method in ["get_by_id", "item", "row"]
        ):
            errors.append(
                "Data binding with 'self.' should reference form properties or data"
            )

        # Check for potentially unsafe code
        dangerous_patterns = ["exec(", "eval(", "import ", "__import__", "open("]
        for pattern in dangerous_patterns:
            if pattern in binding_code:
                errors.append(f"Potentially unsafe code in data binding: {pattern}")

        return len(errors) == 0, errors

    def validate_form_structure(
        self, form_config: Dict[str, Any]
    ) -> tuple[bool, List[str]]:
        """Validate overall form structure."""
        errors = []

        # Check container
        container = form_config.get("container")
        if not container:
            errors.append("Form must have a container")
        else:
            container_type = container.get("type")
            if not container_type:
                errors.append("Container must have a type")
            elif not self._is_container_type(container_type):
                errors.append(
                    f"Container type '{container_type}' is not a valid container"
                )

        # Check components
        components = form_config.get("components", [])
        if not isinstance(components, list):
            errors.append("Components must be a list")
        else:
            component_names = set()
            for i, component in enumerate(components):
                if not isinstance(component, dict):
                    errors.append(f"Component {i} must be a dictionary")
                    continue

                component_name = component.get("name")
                if component_name:
                    if component_name in component_names:
                        errors.append(f"Duplicate component name: {component_name}")
                    component_names.add(component_name)

        return len(errors) == 0, errors

    def _validate_property_value(
        self, component_type: str, property_name: str, value: Any
    ) -> bool:
        """Validate individual property value."""
        # Basic type validation
        if property_name in ["text", "placeholder", "icon", "tooltip"]:
            return isinstance(value, str)
        elif property_name in [
            "enabled",
            "visible",
            "checked",
            "auto_header",
            "show_row_numbers",
            "multiple",
        ]:
            return isinstance(value, bool)
        elif property_name in [
            "width",
            "height",
            "spacing",
            "padding",
            "max_length",
            "columns",
            "row_height",
        ]:
            return isinstance(value, (int, str)) and (
                isinstance(value, int) or str(value).isdigit()
            )
        elif property_name in ["items", "rows"]:
            return isinstance(value, list)
        elif property_name == "columns":  # DataGrid specific
            return isinstance(value, list)

        return True  # Unknown properties pass by default
