"""Main Textual application for Ghostty Settings TUI."""

from pathlib import Path
from typing import Optional

from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.containers import Center, Horizontal, Middle, Vertical
from textual.events import Resize
from textual.reactive import reactive
from textual.widgets import Footer, Header, Label, ListItem, ListView, Static

from .config_manager import ConfigManager, ConfigError
from .models import ConfigValue, OptionDefinition, OptionModel, OptionType
from .parser import GhosttyParser, GhosttyParserError, GhosttyNotFoundError
from .categories import get_sorted_categories, get_category_icon
from .widgets.sidebar import CategoryListView
from .widgets.option_panel import (
    BackToCategories,
    DiscardRequested,
    OptionPanel,
    SaveAndExitRequested,
    SaveRequested,
)
from .widgets.option_row import OptionChanged


class GhosttySettingsTUI(App):
    """TUI application for managing Ghostty terminal settings."""

    CSS_PATH = "styles/app.tcss"
    TITLE = "Ghostty Settings"

    # Minimum window size
    MIN_WIDTH = 60
    MIN_HEIGHT = 20

    BINDINGS = [
        Binding("ctrl+s", "save", "Save", priority=True),
        Binding("ctrl+q", "quit", "Quit", priority=True),
        Binding("question_mark", "show_help", "Help"),
        Binding("escape", "focus_categories", "Back", show=False),
        Binding("tab", "focus_next", "Next", show=False),
        Binding("shift+tab", "focus_previous", "Previous", show=False),
    ]

    # Reactive state
    current_category: reactive[str] = reactive("", init=False)
    unsaved_changes: reactive[bool] = reactive(False, init=False)
    status_message: reactive[str] = reactive("", init=False)

    def __init__(
        self,
        config_path: Optional[str] = None,
        schema_file: Optional[str] = None,
    ):
        """Initialize the application.

        Args:
            config_path: Override config file path
            schema_file: Use schema from file instead of CLI (for testing)
        """
        super().__init__()
        self.parser = GhosttyParser()
        self.config_manager = ConfigManager(config_path)
        self.schema_file = schema_file

        # Data
        self.options: dict[str, OptionModel] = {}
        self.categories: list[str] = []

    def compose(self) -> ComposeResult:
        """Compose the application layout."""
        yield Header()

        # Size warning overlay (hidden by default)
        with Middle(id="size-warning"):
            with Center():
                yield Static("", id="size-warning-text")

        with Horizontal(id="main-container"):
            with Vertical(id="sidebar"):
                yield CategoryListView(id="category-list")

            yield OptionPanel(id="option-panel")

        with Horizontal(id="custom-footer"):
            yield Footer()
            yield Label("", id="status-label")

    async def on_mount(self) -> None:
        """Initialize application on mount."""
        self._set_status("Loading Ghostty configuration...", "info")
        await self._load_data()
        self._populate_categories()

        # Show welcome screen instead of auto-selecting first category
        panel = self.query_one("#option-panel", OptionPanel)
        panel.show_welcome()

        # Check initial size
        self._check_size(self.size.width, self.size.height)

        # Focus the sidebar for immediate keyboard navigation
        category_list = self.query_one("#category-list", CategoryListView)
        category_list.index = 0  # Highlight first item
        category_list.focus()

        self._set_status("Ready", "success")

    def on_resize(self, event: Resize) -> None:
        """Handle terminal resize events."""
        self._check_size(event.size.width, event.size.height)

    def _check_size(self, width: int, height: int) -> None:
        """Check if window size is sufficient and show warning if not."""
        warning = self.query_one("#size-warning")
        main = self.query_one("#main-container")
        footer = self.query_one("#custom-footer")

        if width < self.MIN_WIDTH or height < self.MIN_HEIGHT:
            # Show size warning
            warning_text = self.query_one("#size-warning-text", Static)
            warning_text.update(
                f"Window Too Small\n\n"
                f"Current size:  {width} × {height}\n"
                f"Minimum size:  {self.MIN_WIDTH} × {self.MIN_HEIGHT}\n\n"
                f"Please resize your terminal window."
            )
            warning.display = True
            main.display = False
            footer.display = False
        else:
            # Hide warning, show main UI
            warning.display = False
            main.display = True
            footer.display = True

    def _set_status(self, message: str, severity: str = "info") -> None:
        """Set status bar message with auto-clear.

        Args:
            message: Status message to display
            severity: Message severity (info, warning, error, success)
        """
        try:
            label = self.query_one("#status-label", Label)
            label.update(f" {message} ")
            label.remove_class("status-info", "status-warning", "status-error", "status-success")
            label.add_class(f"status-{severity}")
            # Auto-clear after 5 seconds
            self.set_timer(5.0, self._clear_status)
        except Exception:
            # If status label not yet mounted, fail silently
            pass

    def _clear_status(self) -> None:
        """Clear status message."""
        try:
            label = self.query_one("#status-label", Label)
            label.update("")
        except Exception:
            # If status label not available, fail silently
            pass

    async def _load_data(self) -> None:
        """Load schema and current config."""
        # Load schema
        try:
            if self.schema_file:
                schema_list = self.parser.parse_from_file(self.schema_file)
            else:
                self.parser.fetch_schema()
                schema_list = self.parser.parse()
        except GhosttyNotFoundError:
            self._set_status("Ghostty not found. Please install Ghostty first.", "error")
            schema_list = []
        except GhosttyParserError as e:
            self._set_status(f"Error loading schema: {e}", "error")
            schema_list = []

        # Load current values
        try:
            current_values = self.config_manager.load()
        except ConfigError as e:
            self._set_status(f"Error loading config: {e}", "warning")
            current_values = {}

        # Build option models
        self.options.clear()
        for defn in schema_list:
            current = current_values.get(defn.name, ConfigValue(name=defn.name))
            self.options[defn.name] = OptionModel(definition=defn, current=current)

        # Get categories
        cats = {opt.definition.category for opt in self.options.values()}
        self.categories = get_sorted_categories(cats)

    def _populate_categories(self) -> None:
        """Populate category sidebar."""
        category_list = self.query_one("#category-list", CategoryListView)
        category_list.clear()

        # Add "Main" item at the top for welcome screen
        main_item = ListItem(
            Static("🏠  Main"),
            id="cat-Main"
        )
        category_list.append(main_item)

        if not self.categories:
            # Show message when no categories (Ghostty not found)
            item = ListItem(
                Static("No categories\n(Ghostty not found)"),
                id="cat-empty"
            )
            category_list.append(item)
            return

        for category in self.categories:
            count = sum(
                1 for opt in self.options.values()
                if opt.definition.category == category
            )
            icon = get_category_icon(category)
            item = ListItem(
                Static(f"{icon}  {category} ({count})"),
                id=f"cat-{category.replace('/', '-')}"
            )
            category_list.append(item)

        # Add "Save" item at the bottom
        save_item = ListItem(
            Static("💾  Save"),
            id="cat-Save"
        )
        category_list.append(save_item)

    def _render_options(self) -> None:
        """Render options for current category."""
        panel = self.query_one("#option-panel", OptionPanel)
        filtered = self._get_filtered_options()
        panel.show_category(self.current_category, filtered)

    def _get_filtered_options(self) -> list[OptionModel]:
        """Get options matching current category."""
        options = [
            opt for opt in self.options.values()
            if opt.definition.category == self.current_category
        ]
        options.sort(key=lambda o: o.definition.name)
        return options[:50]  # Limit for performance

    def on_list_view_highlighted(self, event: ListView.Highlighted) -> None:
        """Handle category highlight - show page immediately."""
        if event.item and event.item.id:
            self._show_category_page(event.item.id)

    def on_list_view_selected(self, event: ListView.Selected) -> None:
        """Handle category selection - focus the panel."""
        if event.item and event.item.id:
            self._show_category_page(event.item.id)
            # Move focus to panel on explicit selection
            self.call_after_refresh(self.action_focus_panel)

    def _show_category_page(self, item_id: str) -> None:
        """Show the page for a given category item ID."""
        panel = self.query_one("#option-panel", OptionPanel)

        # Main page
        if item_id == "cat-Main":
            self.current_category = ""
            panel.show_welcome()
            return

        # Save page
        if item_id == "cat-Save":
            self.current_category = ""
            modified = {
                name: opt for name, opt in self.options.items()
                if opt.is_modified
            }
            config_preview = self.config_manager.get_preview()
            panel.show_save_preview(modified, config_preview)
            return

        # Category page - find and render directly
        for cat in self.categories:
            if cat.replace("/", "-") == item_id[4:]:
                self.current_category = cat
                # Render directly, don't rely on watch
                filtered = self._get_filtered_options()
                panel.show_category(cat, filtered)
                break

    def on_option_changed(self, event: OptionChanged) -> None:
        """Handle option value changes."""
        if event.option_name in self.options:
            opt = self.options[event.option_name]
            opt.current.value = event.new_value
            opt.current.is_modified = True
            self.unsaved_changes = True
            self._set_status(f"Modified: {event.option_name}", "info")

    def action_save(self) -> None:
        """Show save confirmation dialog."""
        from .screens.save_dialog import SaveDialog
        self.push_screen(SaveDialog(), self._handle_save_dialog)

    def _handle_save_dialog(self, confirmed: bool) -> None:
        """Handle save dialog result."""
        if not confirmed:
            return
        self._do_save()

    def _do_save(self) -> None:
        """Actually save configuration."""
        try:
            # Check for external changes
            if self.config_manager.has_external_changes():
                self._set_status(
                    "Config modified externally!",
                    "warning"
                )
                return

            # Update config manager with current values
            for name, opt in self.options.items():
                if opt.is_modified:
                    self.config_manager.set(name, opt.current.value)

            self.config_manager.save()
            self.unsaved_changes = False
            self._set_status("Configuration saved!", "success")

            # Reset modification flags
            for opt in self.options.values():
                opt.current.is_modified = False

            self._render_options()
        except ConfigError as e:
            self._set_status(f"Save failed: {e}", "error")

    def action_show_help(self) -> None:
        """Show help screen."""
        from .screens.help_screen import HelpScreen
        self.push_screen(HelpScreen())

    def action_focus_categories(self) -> None:
        """Focus the category list."""
        self.action_focus_sidebar()

    def action_focus_sidebar(self) -> None:
        """Focus the sidebar category list."""
        try:
            category_list = self.query_one("#category-list", CategoryListView)
            category_list.focus()
        except Exception:
            pass

    def action_focus_panel(self) -> None:
        """Focus the first focusable widget in the option panel."""
        try:
            panel = self.query_one("#option-panel", OptionPanel)
            # Try to focus the first interactive widget in the panel
            focusables = list(panel.query("Input, Select, Switch, Button").results())
            if focusables:
                focusables[0].focus()
            else:
                panel.focus()
        except Exception:
            pass

    def on_back_to_categories(self, event: BackToCategories) -> None:
        """Handle back to categories request."""
        self.action_focus_categories()

    def on_save_requested(self, event: SaveRequested) -> None:
        """Handle save request from save preview page."""
        self.action_save()
        # Show welcome after save
        panel = self.query_one("#option-panel", OptionPanel)
        panel.show_welcome()

    def on_save_and_exit_requested(self, event: SaveAndExitRequested) -> None:
        """Handle save and exit request."""
        self.action_save()
        self.exit()

    def on_discard_requested(self, event: DiscardRequested) -> None:
        """Handle discard request - reload config and show welcome."""
        self.run_worker(self._load_data())
        self.unsaved_changes = False
        # Reset modification flags
        for opt in self.options.values():
            opt.current.is_modified = False
        self._set_status("Changes discarded", "info")
        # Show welcome
        panel = self.query_one("#option-panel", OptionPanel)
        panel.show_welcome()

    def action_quit(self) -> None:
        """Show quit confirmation dialog."""
        from .screens.quit_dialog import QuitDialog
        has_unsaved = self.unsaved_changes or self.config_manager.has_unsaved_changes
        self.push_screen(QuitDialog(has_unsaved=has_unsaved), self._handle_quit_dialog)

    def _handle_quit_dialog(self, result: str) -> None:
        """Handle quit dialog result.

        Args:
            result: Dialog result - 'save', 'discard', or 'cancel'
        """
        if result == "save":
            self._do_save()
            self.exit()
        elif result == "discard":
            self.exit()
        # 'cancel' - do nothing, stay in app
