"""
Dialogs - Interactive dialog components for TUI

Provides modal dialogs for various interactions:
- Command Palette (Ctrl+P) - Searchable command list
- Model Selector - Choose AI model
- Agent Selector - Choose agent type
- Session List - Browse and switch sessions
- Help Dialog - Contextual help
- Stash Dialog - Save/restore prompts
- Theme Selector - Choose color theme

Examples:
    # Create model selector
    dialog = ModelSelectorDialog()
    result = dialog.show(available_models)
    if result.selected:
        print(f"Selected: {result.selected}")

    # Create command palette
    palette = CommandPaletteDialog()
    result = palette.show()
    if result.executed:
        print(f"Executed: {result.command}")
"""

from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable, Optional
from datetime import datetime


class DialogResult(Enum):
    """Result type from dialog"""
    CONFIRMED = "confirmed"
    CANCELLED = "cancelled"
    EXECUTED = "executed"


@dataclass
class DialogResponse:
    """Response from a dialog"""
    result: DialogResult
    selected: Optional[Any] = None
    data: dict[str, Any] = field(default_factory=dict)

    @property
    def confirmed(self) -> bool:
        """Check if user confirmed"""
        return self.result == DialogResult.CONFIRMED

    @property
    def cancelled(self) -> bool:
        """Check if user cancelled"""
        return self.result == DialogResult.CANCELLED

    @property
    def executed(self) -> bool:
        """Check if command was executed"""
        return self.result == DialogResult.EXECUTED


@dataclass
class ModelInfo:
    """Information about an AI model"""
    name: str
    display_name: str
    provider: str
    context_limit: int
    input_cost: float  # per 1M tokens
    output_cost: float  # per 1M tokens
    description: str = ""

    def format_cost(self) -> str:
        """Format cost information"""
        return f"${self.input_cost:.2f}/${self.output_cost:.2f} per 1M tokens"


@dataclass
class AgentInfo:
    """Information about an agent"""
    name: str
    display_name: str
    description: str
    icon: str = "🤖"
    tools_available: int = 0
    read_only: bool = False


@dataclass
class SessionInfo:
    """Information about a session"""
    name: str
    created_at: datetime
    updated_at: datetime
    message_count: int
    total_tokens: int
    total_cost: float
    current: bool = False


class BaseDialog:
    """
    Base class for dialog components

    Provides common functionality for all dialogs:
    - Title and prompt text
    - Cancel handling
    - Result management
    """

    def __init__(self, title: str = "Dialog", cancelable: bool = True):
        """
        Initialize dialog

        Args:
            title: Dialog title
            cancelable: Whether dialog can be cancelled with Esc
        """
        self.title = title
        self.cancelable = cancelable
        self._result: Optional[DialogResponse] = None

    def show(self, **kwargs) -> DialogResponse:
        """
        Show dialog and wait for user interaction

        Args:
            **kwargs: Dialog-specific parameters

        Returns:
            DialogResponse with user selection
        """
        raise NotImplementedError("Subclasses must implement show()")

    def _create_cancelled_response(self) -> DialogResponse:
        """Create a cancelled response"""
        return DialogResponse(result=DialogResult.CANCELLED)

    def _create_confirmed_response(self, selected: Any, **data) -> DialogResponse:
        """Create a confirmed response"""
        return DialogResponse(
            result=DialogResult.CONFIRMED,
            selected=selected,
            data=data
        )


class ModelSelectorDialog(BaseDialog):
    """
    Model selector dialog

    Allows user to browse and select an AI model from available options.
    Shows model details including cost, context limit, and provider.

    Example:
        dialog = ModelSelectorDialog()
        models = [
            ModelInfo("gpt-4o", "GPT-4o", "openai", 128000, 5.0, 15.0),
            ModelInfo("claude-3-5-sonnet", "Claude 3.5 Sonnet", "anthropic", 200000, 3.0, 15.0),
        ]
        result = dialog.show(models=models)
        if result.confirmed:
            print(f"Selected: {result.selected.name}")
    """

    def __init__(self):
        super().__init__(title="Select Model")
        self.search_query: str = ""

    def show(self, models: list[ModelInfo], current_model: Optional[str] = None) -> DialogResponse:
        """
        Show model selector dialog

        Args:
            models: List of available models
            current_model: Name of currently selected model

        Returns:
            DialogResponse with selected model
        """
        # In a real implementation, this would use prompt_toolkit or rich
        # to show an interactive dialog. For now, return mock response.
        if models:
            selected = models[0]
            return self._create_confirmed_response(selected, current=current_model)
        return self._create_cancelled_response()

    def filter_models(self, models: list[ModelInfo], query: str) -> list[ModelInfo]:
        """Filter models by search query"""
        if not query:
            return models

        query = query.lower()
        return [
            m for m in models
            if query in m.name.lower() or query in m.display_name.lower()
        ]


class AgentSelectorDialog(BaseDialog):
    """
    Agent selector dialog

    Allows user to browse and select an agent type.
    Shows agent capabilities, tool access, and permissions.

    Example:
        dialog = AgentSelectorDialog()
        agents = [
            AgentInfo("build", "Build Agent", "Full coding access", "🛠️", 14, False),
            AgentInfo("plan", "Plan Agent", "Read-only analysis", "📋", 8, True),
        ]
        result = dialog.show(agents=agents)
        if result.confirmed:
            print(f"Selected: {result.selected.name}")
    """

    def __init__(self):
        super().__init__(title="Select Agent")

    def show(self, agents: list[AgentInfo], current_agent: Optional[str] = None) -> DialogResponse:
        """
        Show agent selector dialog

        Args:
            agents: List of available agents
            current_agent: Name of currently selected agent

        Returns:
            DialogResponse with selected agent
        """
        if agents:
            selected = agents[0]
            return self._create_confirmed_response(selected, current=current_agent)
        return self._create_cancelled_response()


class SessionListDialog(BaseDialog):
    """
    Session list dialog

    Browse, search, and switch between sessions.
    Shows session metadata including message count, tokens, and cost.

    Example:
        dialog = SessionListDialog()
        sessions = [
            SessionInfo("my-session", datetime.now(), datetime.now(), 50, 10000, 0.05),
        ]
        result = dialog.show(sessions=sessions)
        if result.confirmed:
            print(f"Switching to: {result.selected.name}")
    """

    def __init__(self):
        super().__init__(title="Sessions")
        self.search_query: str = ""

    def show(self, sessions: list[SessionInfo], current_session: Optional[str] = None) -> DialogResponse:
        """
        Show session list dialog

        Args:
            sessions: List of available sessions
            current_session: Name of current session

        Returns:
            DialogResponse with selected session
        """
        if sessions:
            selected = sessions[0]
            return self._create_confirmed_response(selected, current=current_session)
        return self._create_cancelled_response()

    def filter_sessions(self, sessions: list[SessionInfo], query: str) -> list[SessionInfo]:
        """Filter sessions by search query"""
        if not query:
            return sessions

        query = query.lower()
        return [s for s in sessions if query in s.name.lower()]


class HelpDialog(BaseDialog):
    """
    Help dialog

    Contextual help based on current state.
    Shows available commands, shortcuts, and tips.

    Example:
        dialog = HelpDialog()
        result = dialog.show(context="repl")
        # Shows help specific to REPL context
    """

    def __init__(self):
        super().__init__(title="Help")

    def show(self, context: str = "general", **kwargs) -> DialogResponse:
        """
        Show help dialog

        Args:
            context: Help context (general, repl, agent, etc.)
            **kwargs: Additional context data

        Returns:
            DialogResponse (help is informational only)
        """
        return self._create_confirmed_response(None, context=context)


class StashDialog(BaseDialog):
    """
    Stash dialog

    Save and restore prompt text for later use.
    Like a clipboard history for prompts.

    Example:
        dialog = StashDialog()

        # Save prompt
        dialog.stash_prompt("My long prompt text")

        # Restore prompt
        result = dialog.show()
        if result.confirmed:
            prompt_text = result.selected
    """

    def __init__(self):
        super().__init__(title="Stash")
        self.stash_file = None  # Will be set to actual path
        self._stashed_prompts: list[tuple[str, datetime]] = []

    def show(self, mode: str = "restore") -> DialogResponse:
        """
        Show stash dialog

        Args:
            mode: "save" or "restore"

        Returns:
            DialogResponse with selected prompt or save confirmation
        """
        if mode == "save":
            return self._create_confirmed_response(None, mode="save")
        elif mode == "restore" and self._stashed_prompts:
            selected = self._stashed_prompts[0][0]
            return self._create_confirmed_response(selected, mode="restore")
        return self._create_cancelled_response()

    def stash_prompt(self, prompt: str) -> bool:
        """
        Save a prompt to stash

        Args:
            prompt: Prompt text to save

        Returns:
            True if saved successfully
        """
        self._stashed_prompts.append((prompt, datetime.now()))
        return True

    def get_stashed_prompts(self) -> list[tuple[str, datetime]]:
        """Get all stashed prompts"""
        return self._stashed_prompts.copy()


class ThemeSelectorDialog(BaseDialog):
    """
    Theme selector dialog

    Browse and preview color themes.
    Shows theme preview and allows switching.

    Example:
        dialog = ThemeSelectorDialog()
        themes = ["tokyonight", "everforest", "catppuccin"]
        result = dialog.show(themes=themes)
        if result.confirmed:
            print(f"Selected theme: {result.selected}")
    """

    def __init__(self):
        super().__init__(title="Select Theme")

    def show(self, themes: list[str], current_theme: Optional[str] = None) -> DialogResponse:
        """
        Show theme selector dialog

        Args:
            themes: List of available theme names
            current_theme: Name of current theme

        Returns:
            DialogResponse with selected theme
        """
        if themes:
            selected = themes[0]
            return self._create_confirmed_response(selected, current=current_theme)
        return self._create_cancelled_response()


class CommandPaletteDialog(BaseDialog):
    """
    Command palette dialog (Ctrl+P)

    Searchable command interface with fuzzy matching.
    Integrates with CommandPalette for command execution.

    Example:
        from groknroll.tui import CommandPalette

        palette = CommandPalette()
        dialog = CommandPaletteDialog(palette)
        result = dialog.show()
        if result.executed:
            print(f"Executed: {result.data['command']}")
    """

    def __init__(self, command_palette: Optional[Any] = None):
        super().__init__(title="Command Palette", cancelable=True)
        self.command_palette = command_palette
        self.search_query: str = ""

    def show(self, initial_query: str = "") -> DialogResponse:
        """
        Show command palette dialog

        Args:
            initial_query: Initial search query

        Returns:
            DialogResponse with executed command
        """
        self.search_query = initial_query

        if self.command_palette:
            results = self.command_palette.search(initial_query)
            if results.commands:
                # In real implementation, user would select from list
                # For now, return first match
                command = results.commands[0]
                return DialogResponse(
                    result=DialogResult.EXECUTED,
                    selected=command,
                    data={"command": command.name, "action": command.action}
                )

        return self._create_cancelled_response()


# Dialog management

class DialogStack:
    """
    Manages a stack of dialogs

    Allows multiple dialogs to be shown hierarchically.
    Only the top dialog is visible and interactive.
    """

    def __init__(self):
        self._stack: list[BaseDialog] = []

    def push(self, dialog: BaseDialog):
        """Push a dialog onto the stack"""
        self._stack.append(dialog)

    def pop(self) -> Optional[BaseDialog]:
        """Pop the top dialog from the stack"""
        if self._stack:
            return self._stack.pop()
        return None

    def peek(self) -> Optional[BaseDialog]:
        """Get the top dialog without removing it"""
        if self._stack:
            return self._stack[-1]
        return None

    def clear(self):
        """Clear all dialogs from the stack"""
        self._stack.clear()

    @property
    def is_empty(self) -> bool:
        """Check if dialog stack is empty"""
        return len(self._stack) == 0

    @property
    def depth(self) -> int:
        """Get current stack depth"""
        return len(self._stack)


# Convenience functions

def show_model_selector(models: list[ModelInfo], current: Optional[str] = None) -> Optional[ModelInfo]:
    """
    Show model selector and return selected model

    Args:
        models: Available models
        current: Current model name

    Returns:
        Selected ModelInfo or None if cancelled
    """
    dialog = ModelSelectorDialog()
    result = dialog.show(models=models, current_model=current)
    return result.selected if result.confirmed else None


def show_agent_selector(agents: list[AgentInfo], current: Optional[str] = None) -> Optional[AgentInfo]:
    """
    Show agent selector and return selected agent

    Args:
        agents: Available agents
        current: Current agent name

    Returns:
        Selected AgentInfo or None if cancelled
    """
    dialog = AgentSelectorDialog()
    result = dialog.show(agents=agents, current_agent=current)
    return result.selected if result.confirmed else None


def show_session_list(sessions: list[SessionInfo], current: Optional[str] = None) -> Optional[SessionInfo]:
    """
    Show session list and return selected session

    Args:
        sessions: Available sessions
        current: Current session name

    Returns:
        Selected SessionInfo or None if cancelled
    """
    dialog = SessionListDialog()
    result = dialog.show(sessions=sessions, current_session=current)
    return result.selected if result.confirmed else None


def show_help(context: str = "general") -> None:
    """
    Show contextual help dialog

    Args:
        context: Help context
    """
    dialog = HelpDialog()
    dialog.show(context=context)


def show_stash(mode: str = "restore") -> Optional[str]:
    """
    Show stash dialog

    Args:
        mode: "save" or "restore"

    Returns:
        Selected prompt text or None
    """
    dialog = StashDialog()
    result = dialog.show(mode=mode)
    return result.selected if result.confirmed else None


def show_theme_selector(themes: list[str], current: Optional[str] = None) -> Optional[str]:
    """
    Show theme selector and return selected theme

    Args:
        themes: Available themes
        current: Current theme name

    Returns:
        Selected theme name or None if cancelled
    """
    dialog = ThemeSelectorDialog()
    result = dialog.show(themes=themes, current_theme=current)
    return result.selected if result.confirmed else None


def show_command_palette(palette: Optional[Any] = None, initial_query: str = "") -> Optional[Any]:
    """
    Show command palette and return executed command

    Args:
        palette: CommandPalette instance
        initial_query: Initial search query

    Returns:
        Executed command or None if cancelled
    """
    dialog = CommandPaletteDialog(palette)
    result = dialog.show(initial_query=initial_query)
    return result.selected if result.executed else None
