"""Unified control panel for Cleave TUI."""

from collections.abc import Callable
from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal

from textual.app import ComposeResult
from textual.containers import Horizontal, Vertical
from textual.reactive import reactive
from textual.widgets import (
    Button,
    Checkbox,
    Input,
    Label,
    OptionList,
    RadioButton,
    RadioSet,
    Select,
    Static,
)
from textual.widgets.option_list import Option

from cleave.core.settings import (
    detect_available_backends,
    get_active_backend,
    load_settings,
)


@dataclass
class CleaveParams:
    """Parameters for Cleave execution."""

    parallel: bool = True
    dry_run: bool = False
    max_depth: int = 3
    model: str = "claude-sonnet-4-20250514"
    backend: str = "claude"
    selected_calf: Path | None = None
    # Additional backend-specific config
    backend_config: dict = field(default_factory=dict)


class ControlPanel(Vertical):
    """Unified control panel with file list and cleave parameters.

    Combines file selection with execution controls.

    Interface:
        get_params() -> CleaveParams - Get current execution parameters
        refresh_files() - Refresh the file list
        on_file_selected: Callable[[Path], None] - File selection callback
        on_calf_workflow: Callable[[str, Path | None], None] - Start interrogation callback
        on_cleave: Callable[[CleaveParams], None] - Execute cleave callback
    """

    DEFAULT_CSS = """
    ControlPanel {
        height: 100%;
        padding: 1;
    }

    ControlPanel OptionList {
        height: auto;
        max-height: 8;
        margin-bottom: 1;
        border: solid $panel;
    }

    ControlPanel .options-row {
        layout: horizontal;
        height: auto;
        margin-bottom: 1;
    }

    ControlPanel .options-row Checkbox {
        width: auto;
        margin-right: 2;
    }

    ControlPanel .depth-row {
        layout: horizontal;
        height: auto;
        margin-bottom: 1;
    }

    ControlPanel .depth-row Label {
        width: auto;
        margin-right: 1;
    }

    ControlPanel .depth-row Input {
        width: 4;
    }

    ControlPanel .model-row {
        layout: horizontal;
        height: auto;
        margin-bottom: 1;
    }

    ControlPanel .model-row Label {
        width: auto;
        margin-right: 1;
    }

    ControlPanel .model-row RadioSet {
        layout: horizontal;
        height: auto;
        width: 1fr;
    }

    ControlPanel .model-row RadioButton {
        width: auto;
        margin-right: 1;
    }

    ControlPanel .backend-row {
        layout: horizontal;
        height: auto;
        margin-bottom: 1;
    }

    ControlPanel .backend-row Label {
        width: auto;
        margin-right: 1;
    }

    ControlPanel .backend-row Select {
        width: 1fr;
    }

    ControlPanel .status-row {
        height: auto;
        margin-bottom: 1;
    }

    ControlPanel .status-row Static {
        width: 100%;
        text-style: italic;
        color: $text-muted;
    }

    ControlPanel Button {
        width: 100%;
        margin-bottom: 1;
    }

    ControlPanel #cleave-button {
        margin-bottom: 0;
    }
    """

    # Directory to scan for .calf files
    directory: reactive[Path | None] = reactive(None)

    # Currently selected file
    selected_file: reactive[Path | None] = reactive(None)

    # Callbacks
    # on_calf_workflow receives (mode, selected_file) where mode is "raise" or "fatten"
    on_file_selected: Callable[[Path], None] | None = None
    on_calf_workflow: Callable[[str, Path | None], None] | None = None
    on_cleave: Callable[[CleaveParams], None] | None = None

    def compose(self) -> ComposeResult:
        """Compose the unified control panel."""
        # File list
        yield OptionList(id="calf-list")

        # Workflow button
        yield Button("Raise a Calf", id="calf-raises-button", variant="success")

        # Backend row
        with Horizontal(classes="backend-row"):
            yield Label("Backend:")
            yield Select(
                self._get_backend_options(),
                id="backend-select",
                value=get_active_backend(),
            )

        # Connection status
        with Vertical(classes="status-row"):
            yield Static("", id="connection-status")

        # Options row: checkboxes side by side
        with Horizontal(classes="options-row"):
            yield Checkbox("Parallel", id="parallel-check", value=True)
            yield Checkbox("Dry run", id="dry-run-check", value=False)

        # Depth row
        with Horizontal(classes="depth-row"):
            yield Label("Depth:")
            yield Input("3", id="max-depth-input", type="integer", max_length=2)

        # Model row (dynamic based on backend)
        with Horizontal(classes="model-row"):
            yield Label("Model:")
            with RadioSet(id="model-radio"):
                yield RadioButton("S", id="model-sonnet")
                yield RadioButton("O", id="model-opus", value=True)
                yield RadioButton("H", id="model-haiku")

        # Execute button
        yield Button("CLEAVE", id="cleave-button", variant="primary")

    def on_mount(self) -> None:
        """Initialize with current directory if set."""
        if self.directory:
            self.refresh_files()
        self._update_connection_status()
        self._update_model_options()

    def _get_backend_options(self) -> list[tuple[str, str]]:
        """Get available backends as (label, value) tuples for Select."""
        backends = detect_available_backends()
        options = []
        for name, available, _ in backends:
            status = "●" if available else "○"
            label = f"{status} {name.capitalize()}"
            options.append((label, name))
        return options

    def _update_connection_status(self) -> None:
        """Update the connection status display."""
        if not self.is_mounted:
            return

        status_widget = self.query_one("#connection-status", Static)
        backend_select = self.query_one("#backend-select", Select)

        if backend_select.value is None or backend_select.value == Select.BLANK:
            status_widget.update("No backend selected")
            return

        backend_name = str(backend_select.value)
        backends = detect_available_backends()

        for name, available, msg in backends:
            if name == backend_name:
                if available:
                    status_widget.update(f"✓ {msg}")
                    status_widget.styles.color = "green"
                else:
                    status_widget.update(f"✗ {msg}")
                    status_widget.styles.color = "red"
                return

        status_widget.update("Unknown backend")

    def _update_model_options(self) -> None:
        """Update model options based on selected backend."""
        if not self.is_mounted:
            return

        backend_select = self.query_one("#backend-select", Select)
        if backend_select.value is None or backend_select.value == Select.BLANK:
            return

        backend_name = str(backend_select.value)
        radio_set = self.query_one("#model-radio", RadioSet)

        # For Claude, show S/O/H shortcuts
        # For other backends, we'd need to dynamically populate
        # For now, keep Claude-style for claude backend, disable for others
        if backend_name != "claude":
            # Disable model selection for non-Claude backends (use settings default)
            for button in radio_set.query(RadioButton):
                button.disabled = True
        else:
            for button in radio_set.query(RadioButton):
                button.disabled = False

    def on_select_changed(self, event: Select.Changed) -> None:
        """Handle backend selection change."""
        if event.select.id == "backend-select":
            self._update_connection_status()
            self._update_model_options()

    def watch_directory(self, value: Path | None) -> None:
        """Refresh file list when directory changes."""
        if value is not None and self.is_mounted:
            self.refresh_files()

    def watch_selected_file(self, value: Path | None) -> None:
        """Update button label when selection changes."""
        if not self.is_mounted:
            return
        button = self.query_one("#calf-raises-button", Button)
        if value is not None:
            button.label = "Fatten Calf"
        else:
            button.label = "Raise a Calf"

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        """Handle file selection."""
        if event.option.id:
            path = Path(event.option.id)
            self.selected_file = path

            if self.on_file_selected is not None:
                self.on_file_selected(path)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Handle button presses."""
        if event.button.id == "calf-raises-button" and self.on_calf_workflow is not None:
            # Determine mode based on selection
            mode = "fatten" if self.selected_file is not None else "raise"
            self.on_calf_workflow(mode, self.selected_file)
        elif event.button.id == "cleave-button" and self.on_cleave is not None:
            self.on_cleave(self.get_params())

    def refresh_files(self) -> None:
        """Refresh the file list from the directory."""
        option_list = self.query_one("#calf-list", OptionList)
        option_list.clear_options()

        if self.directory is None or not self.directory.exists():
            return

        calf_files = sorted(self.directory.glob("*.calf"))

        for calf_file in calf_files:
            option_list.add_option(Option(calf_file.name, id=str(calf_file)))

    def get_files(self) -> list[Path]:
        """Get list of .calf files in the directory."""
        if self.directory is None or not self.directory.exists():
            return []
        return sorted(self.directory.glob("*.calf"))

    def get_params(self) -> CleaveParams:
        """Get current parameter values."""
        parallel = self.query_one("#parallel-check", Checkbox).value
        dry_run = self.query_one("#dry-run-check", Checkbox).value

        depth_input = self.query_one("#max-depth-input", Input)
        try:
            max_depth = int(depth_input.value)
        except ValueError:
            max_depth = 3

        # Get backend
        backend_select = self.query_one("#backend-select", Select)
        backend = "claude"
        if backend_select.value is not None and backend_select.value != Select.BLANK:
            backend = str(backend_select.value)

        # Get model based on backend
        settings = load_settings()
        backend_config = settings.get_backend_config(backend)

        if backend == "claude":
            # Use radio selection for Claude
            radio_set = self.query_one("#model-radio", RadioSet)
            model = "claude-sonnet-4-20250514"  # default
            if radio_set.pressed_button:
                button_id = radio_set.pressed_button.id
                if button_id == "model-sonnet":
                    model = "claude-sonnet-4-20250514"
                elif button_id == "model-opus":
                    model = "claude-opus-4-20250514"
                elif button_id == "model-haiku":
                    model = "claude-haiku-4-20250514"
        else:
            # Use settings default for other backends
            model = backend_config.default_model or "qwen2.5-coder:7b"

        # Build backend-specific config
        config = {}
        if backend_config.base_url:
            config["base_url"] = backend_config.base_url

        return CleaveParams(
            parallel=parallel,
            dry_run=dry_run,
            max_depth=max_depth,
            model=model,
            backend=backend,
            selected_calf=self.selected_file,
            backend_config=config,
        )
