"""Option panel widget for displaying configuration options."""

import uuid

from textual.binding import Binding
from textual.containers import ScrollableContainer, Vertical
from textual.message import Message
from textual.widgets import Button, Input, Select, Static, Switch

from ..models import OptionModel, OptionType
from .option_row import OptionChanged
from .option_input import IntegerValidator, FloatValidator, ColorValidator


class BackToCategories(Message):
    """Message emitted when user wants to go back to categories."""
    pass


class SaveRequested(Message):
    """Message emitted when user clicks Save button."""
    pass


class SaveAndExitRequested(Message):
    """Message emitted when user clicks Save and Exit button."""
    pass


class DiscardRequested(Message):
    """Message emitted when user clicks Discard button."""
    pass


class OptionPanel(ScrollableContainer):
    """Panel widget for displaying and editing configuration options."""

    BINDINGS = [
        Binding("escape", "back_to_sidebar", "Back", show=False),
    ]

    DEFAULT_CSS = """
    OptionPanel {
        width: 1fr;
        height: 100%;
        padding: 1 2;
        background: #282a36;
    }

    OptionPanel .category-header {
        text-style: bold;
        color: #bd93f9;
        padding: 1 0;
        border-bottom: solid #bd93f9;
        margin-bottom: 1;
        height: auto;
    }

    OptionPanel .option-container {
        padding: 1;
        margin-bottom: 1;
        background: #44475a;
        border: solid #6272a4;
        height: auto;
    }

    OptionPanel .option-container:focus-within {
        border: solid #bd93f9;
    }

    OptionPanel .option-name {
        text-style: bold;
        color: #8be9fd;
        height: auto;
    }

    OptionPanel .option-description {
        color: #6272a4;
        margin-top: 1;
        height: auto;
    }

    OptionPanel .empty-state {
        text-align: center;
        color: #6272a4;
        padding: 4;
    }

    OptionPanel .value-display {
        height: auto;
        padding: 0 1;
    }

    OptionPanel .type-label {
        color: #ff79c6;
        height: auto;
    }

    OptionPanel .value-label {
        color: #f8f8f2;
        height: auto;
    }

    OptionPanel .value-row {
        height: auto;
    }

    OptionPanel .panel-footer {
        width: 100%;
        margin-top: 2;
        padding: 1;
        border-top: solid #6272a4;
        height: auto;
        background: #282a36;
    }

    OptionPanel .panel-footer-text {
        width: 100%;
        color: #6272a4;
        text-align: center;
        height: auto;
    }

    """

    def __init__(self, **kwargs) -> None:
        """Initialize the option panel."""
        super().__init__(**kwargs)
        self._options: list[OptionModel] = []
        self._modified_values: dict[str, str | None] = {}
        self._render_id: str = ""  # Unique ID per render to avoid duplicates

    def show_category(
        self,
        category: str,
        options: list[OptionModel]
    ) -> None:
        """Display options for a category.

        Args:
            category: Category name to display
            options: List of option models to show
        """
        self._options = options
        self._render_options(f"Category: {category}")


    def _render_options(self, header: str) -> None:
        """Render options to the panel."""
        self.remove_children()

        # Generate unique render ID to avoid duplicate widget IDs
        self._render_id = uuid.uuid4().hex[:8]

        # Header
        self.mount(Static(header, classes="category-header"))

        if not self._options:
            self.mount(Static(
                "No options to display.\nSelect a category from the left.",
                classes="empty-state"
            ))
            return

        # Render each option
        for opt in self._options:
            self._mount_option_container(opt)

        # Footer with keyboard shortcuts
        footer = Vertical(classes="panel-footer")
        self.mount(footer)
        footer.mount(Static(
            "Tab: Next field | Ctrl+S: Save | ?: Help",
            classes="panel-footer-text"
        ))

    def _mount_option_container(self, model: OptionModel) -> None:
        """Mount an option container with all its children.

        Args:
            model: Option model to create widget for
        """
        # Create the container with unique ID
        container = Vertical(
            classes="option-container",
            id=f"opt-{model.definition.name}-{self._render_id}"
        )
        self.mount(container)

        # Mount children into the container
        modified_marker = " *" if model.is_modified else ""
        name_label = Static(
            f"{model.definition.name}{modified_marker}",
            classes="option-name"
        )
        container.mount(name_label)

        # Mount value widgets directly (not wrapped in another container)
        self._mount_value_widgets(container, model)

        # Description (truncated)
        if model.definition.description:
            desc = model.definition.description.split("\n")[0]
            if len(desc) > 100:
                desc = desc[:100] + "..."
            desc_label = Static(desc, classes="option-description")
            container.mount(desc_label)

    def _mount_value_widgets(self, container: Vertical, model: OptionModel) -> None:
        """Mount value display/edit widgets directly into container.

        Args:
            container: Parent container to mount widgets into
            model: Option model with value data
        """
        opt_type = model.definition.option_type
        # Get current user-set value (not display_value which includes default)
        # Handle various value types safely
        current_value = model.current.value
        if current_value is None or current_value == "":
            value = ""
        elif isinstance(current_value, bool):
            value = "true" if current_value else "false"
        else:
            value = str(current_value)
        default = model.definition.default_value or ""

        # Create appropriate widget based on option type
        if opt_type == OptionType.BOOLEAN:
            # Boolean: use Switch - use display_value for boolean to show default state
            display_val = model.display_value
            is_true = display_val.lower() in ("true", "1", "yes", "on") if display_val else False
            switch = Switch(value=is_true, id=f"switch-{model.definition.name}-{self._render_id}")
            switch.option_name = model.definition.name
            container.mount(switch)

        elif opt_type == OptionType.ENUM and model.definition.enum_values:
            # Enum: use Select dropdown
            enum_values = model.definition.enum_values
            options = [(v, v) for v in enum_values]
            # Only show selected value if user explicitly set it
            # Use Select.NULL (not Select.BLANK) for "no selection" state
            initial_value: str | type[Select.NULL] = Select.NULL
            if value and value in enum_values:
                initial_value = value
            # Show default value hint in prompt if available
            prompt_text = f"Select (default: {default})" if default else "Select"
            select = Select[str](
                options=options,
                value=initial_value,
                prompt=prompt_text,
                id=f"select-{model.definition.name}-{self._render_id}",
                allow_blank=True
            )
            select.option_name = model.definition.name
            container.mount(select)

        else:
            # String/Integer/Float/Other: use Input with appropriate validators
            validators = []
            placeholder = f"Default: {default}" if default else "Enter value..."

            if opt_type == OptionType.INTEGER:
                validators = [IntegerValidator()]
                if not default:
                    placeholder = "Enter integer..."
            elif opt_type == OptionType.FLOAT:
                validators = [FloatValidator()]
                if not default:
                    placeholder = "Enter number..."
            elif opt_type == OptionType.COLOR:
                validators = [ColorValidator()]
                if not default:
                    placeholder = "#RRGGBB or #RGB"

            input_widget = Input(
                value=value,
                placeholder=placeholder,
                validators=validators if validators else None,
                id=f"input-{model.definition.name}-{self._render_id}"
            )
            input_widget.option_name = model.definition.name
            container.mount(input_widget)

    def get_modified_values(self) -> dict[str, str | None]:
        """Get all modified values.

        Returns:
            Dictionary of option name to new value
        """
        return dict(self._modified_values)

    def clear(self) -> None:
        """Clear the panel."""
        self._options = []
        self._modified_values = {}
        self.remove_children()
        self.mount(Static(
            "Select a category or search for options.",
            classes="empty-state"
        ))

    def show_welcome(self) -> None:
        """Show welcome screen with usage instructions."""
        self.remove_children()

        welcome_text = """
[bold #ff79c6]Welcome to Ghostty Settings[/bold #ff79c6]
[#f8f8f2]A terminal-based configuration editor for Ghostty[/#f8f8f2]

[bold #bd93f9]Getting Started[/bold #bd93f9]
  [#f8f8f2]1. Select a category from the sidebar on the left[/#f8f8f2]
  [#f8f8f2]2. Modify settings using the controls[/#f8f8f2]
  [#f8f8f2]3. Press [/#f8f8f2][#8be9fd]Ctrl+S[/#8be9fd][#f8f8f2] to save your changes[/#f8f8f2]

[bold #bd93f9]Keyboard Shortcuts[/bold #bd93f9]
  [#8be9fd]Tab[/#8be9fd] / [#8be9fd]Shift+Tab[/#8be9fd]    [#f8f8f2]Navigate between fields[/#f8f8f2]
  [#8be9fd]Up[/#8be9fd] / [#8be9fd]Down[/#8be9fd]          [#f8f8f2]Navigate list items[/#f8f8f2]
  [#8be9fd]Right[/#8be9fd] / [#8be9fd]Enter[/#8be9fd]      [#f8f8f2]Select category[/#f8f8f2]
  [#8be9fd]Escape[/#8be9fd]             [#f8f8f2]Back to category list[/#f8f8f2]
  [#8be9fd]Ctrl+S[/#8be9fd]             [#f8f8f2]Save configuration[/#f8f8f2]
  [#8be9fd]Ctrl+Q[/#8be9fd]             [#f8f8f2]Quit application[/#f8f8f2]
  [#8be9fd]?[/#8be9fd]                  [#f8f8f2]Show help screen[/#f8f8f2]

[bold #bd93f9]Configuration File[/bold #bd93f9]
  [#6272a4]~/.config/ghostty/config[/#6272a4]
"""
        self.mount(Static(welcome_text, classes="welcome-content"))

    def show_save_preview(
        self,
        modified_options: dict[str, "OptionModel"],
        config_preview: str
    ) -> None:
        """Show save preview with config file content and action buttons.

        Args:
            modified_options: Dictionary of modified option models
            config_preview: Preview of the config file content
        """
        self.remove_children()

        # Header
        self.mount(Static("Save Configuration", classes="save-title"))

        # Modified options summary
        if modified_options:
            self.mount(Static(f"\n Modified Options ({len(modified_options)})", classes="save-section"))
            for name, opt in modified_options.items():
                value = opt.current.value or "(empty)"
                self.mount(Static(f"  • {name} = {value}", classes="save-modified-item"))
        else:
            self.mount(Static("\n No changes to save", classes="save-no-changes"))

        # Config file preview
        self.mount(Static("\n Config File Preview", classes="save-section"))
        self.mount(Static("  ~/.config/ghostty/config", classes="save-path"))

        # Preview content in a container
        preview_container = Vertical(classes="save-preview-container")
        self.mount(preview_container)

        if config_preview:
            preview_container.mount(Static(config_preview, classes="save-preview-content"))
        else:
            preview_container.mount(Static("(New file will be created)", classes="save-preview-empty"))

        # Action buttons
        button_container = Vertical(classes="save-buttons")
        self.mount(button_container)

        button_container.mount(Button("Save", id="save-btn", variant="success"))
        button_container.mount(Button("Save and Exit", id="save-exit-btn", variant="primary"))
        button_container.mount(Button("Discard Changes", id="discard-btn", variant="warning"))

    # Event handlers
    def on_switch_changed(self, event: Switch.Changed) -> None:
        """Handle switch toggle."""
        widget = event.switch
        if hasattr(widget, 'option_name'):
            value = "true" if event.value else "false"
            self._modified_values[widget.option_name] = value
            self.post_message(OptionChanged(
                option_name=widget.option_name,
                old_value=None,
                new_value=value
            ))

    def on_select_changed(self, event: Select.Changed) -> None:
        """Handle select change."""
        widget = event.select
        if hasattr(widget, 'option_name'):
            # Use Select.NULL check for "no selection" state
            value = "" if event.value is Select.NULL else str(event.value)
            self._modified_values[widget.option_name] = value
            self.post_message(OptionChanged(
                option_name=widget.option_name,
                old_value=None,
                new_value=value
            ))

    def on_input_submitted(self, event: Input.Submitted) -> None:
        """Handle input submit (Enter key pressed)."""
        widget = event.input
        if hasattr(widget, 'option_name'):
            self._modified_values[widget.option_name] = event.value
            self.post_message(OptionChanged(
                option_name=widget.option_name,
                old_value=None,
                new_value=event.value
            ))

    def on_blur(self, event) -> None:
        """Handle blur event - commit input changes when focus leaves."""
        # Find the input that lost focus
        try:
            focused = event.widget
            if isinstance(focused, Input) and hasattr(focused, 'option_name'):
                value = focused.value
                if focused.option_name not in self._modified_values or \
                   self._modified_values.get(focused.option_name) != value:
                    self._modified_values[focused.option_name] = value
                    self.post_message(OptionChanged(
                        option_name=focused.option_name,
                        old_value=None,
                        new_value=value
                    ))
        except Exception:
            pass

    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Handle button press."""
        if event.button.id == "save-btn":
            self.post_message(SaveRequested())
        elif event.button.id == "save-exit-btn":
            self.post_message(SaveAndExitRequested())
        elif event.button.id == "discard-btn":
            self.post_message(DiscardRequested())

    def action_back_to_sidebar(self) -> None:
        """Go back to sidebar."""
        self.post_message(BackToCategories())
