"""
Textual TUI module for cl-preset package.

Provides a terminal user interface for strategy selection and installation.
"""

from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING

from rich.console import Console
from rich.text import Text
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.containers import Container, Horizontal, Vertical
from textual.events import Key
from textual.widgets import Button, DataTable, Footer, Header, Static

from cl_preset.git_strategy import GitStrategyManager
from cl_preset.manager import PresetManager
from cl_preset.models import PresetMetadata, PresetScope, PresetType

if TYPE_CHECKING:
    from textual.widgets import DataTable


class ScopeStatus(str, Enum):
    """Scope status for TUI selection."""

    NONE = ""
    USER = "U"
    PROJECT = "P"


@dataclass
class StrategyItem:
    """A strategy item for the TUI table."""

    name: str
    strategy_type: str
    description: str
    path: Path | None = None
    source: str = "builtin"
    scope_status: ScopeStatus = ScopeStatus.NONE


class StrategyTable(DataTable[str]):
    """Custom DataTable for strategy selection with scope cycling."""

    def __init__(self, *args, strategies: list[StrategyItem] | None = None, **kwargs) -> None:
        """Initialize the strategy table.

        Args:
            *args: Positional arguments for DataTable
            strategies: List of StrategyItem objects
            **kwargs: Keyword arguments for DataTable
        """
        super().__init__(*args, **kwargs)
        self.strategies = strategies or []

    def on_key(self, event: Key) -> None:
        """Handle key events for scope cycling."""
        if event.key == "space":
            event.stop()
            self.cycle_scope()
        elif event.key == "enter":
            event.stop()
            self.submit_selection()

    def cycle_scope(self) -> None:
        """Cycle the scope status for the currently selected row."""
        try:
            # Check if table has rows
            if self.row_count == 0:
                return

            row_key = self.cursor_row
            if row_key is None:
                return

            # Get current scope status - use correct column name "Status"
            cell = self.get_cell(row_key, "Status")
            current_text = cell.plain if cell else ""

            # Cycle through scopes: none -> user -> project -> none
            scope_cycle = {
                "": ScopeStatus.USER,
                "U": ScopeStatus.PROJECT,
                "P": ScopeStatus.NONE,
            }
            new_status = scope_cycle.get(current_text, ScopeStatus.USER)

            # Update strategy item
            row_idx = self.get_row_index(row_key)
            if 0 <= row_idx < len(self.strategies):
                self.strategies[row_idx].scope_status = new_status

            # Update cell display
            status_text = Text(str(new_status.value))
            if new_status == ScopeStatus.USER:
                status_text.style = "bold green"
            elif new_status == ScopeStatus.PROJECT:
                status_text.style = "bold yellow"
            else:
                status_text.style = "dim"

            self.update_cell(row_key, "Status", status_text)
            self.move_cursor(row=row_key, animate=False)

        except Exception:
            # Catch all exceptions including CellDoesNotExist
            pass

    def on_focus(self) -> None:
        """Handle focus event - show cursor when table is focused."""
        self.cursor_type = "cell"
        self.show_cursor = True

    def on_blur(self) -> None:
        """Handle blur event - hide cursor when table loses focus."""
        self.show_cursor = False

    def submit_selection(self) -> None:
        """Submit the current selection."""
        # Get selected strategies
        selected = [s for s in self.strategies if s.scope_status != ScopeStatus.NONE]

        if not selected:
            return

        # Emit a custom event or call app method
        app = self.app
        if isinstance(app, StrategyTUI):
            app.install_selected_strategies()

    def get_selected_strategies(self) -> list[tuple[StrategyItem, PresetScope]]:
        """Get all strategies with their selected scopes.

        Returns:
            List of (strategy, scope) tuples
        """
        scope_map = {
            ScopeStatus.USER: PresetScope.USER,
            ScopeStatus.PROJECT: PresetScope.PROJECT,
        }

        return [
            (s, scope_map[s.scope_status])
            for s in self.strategies
            if s.scope_status != ScopeStatus.NONE
        ]


class StatusPanel(Static):
    """Status panel showing installation results."""

    def __init__(self, *args, **kwargs) -> None:
        """Initialize the status panel."""
        super().__init__("", *args, **kwargs)
        self.console = Console()

    def on_mount(self) -> None:
        """Initialize the status panel on mount."""
        self.update(
            "[dim]Status: Ready[/dim]\n\n"
            "Use [bold]Space[/bold] to toggle scope: [ ] none -> [U] user -> [P] project\n"
            "Use [bold]Arrow keys[/bold] to navigate\n"
            "Press [bold]Enter[/bold] or click [bold]Submit[/bold] to install"
        )

    def show_results(
        self,
        results: list[dict],
    ) -> None:
        """Show installation results.

        Args:
            results: List of installation result dictionaries
        """
        successful = [r for r in results if r["success"]]
        failed = [r for r in results if not r["success"]]

        if len(successful) == len(results):
            text = "[bold green]All strategies installed successfully![/bold green]\n\n"
            for r in results:
                text += f"  {r['name']} -> {r['scope']} ({r['type']})\n"
        elif len(successful) > 0:
            text = (
                f"[bold yellow]Partial success: {len(successful)}/{len(results)} "
                "installed[/bold yellow]\n\n"
            )
            text += "[green]Successfully installed:[/green]\n"
            for r in successful:
                text += f"  {r['name']} -> {r['scope']}\n"
            text += "\n[red]Failed to install:[/red]\n"
            for r in failed:
                text += f"  {r['name']}\n"
        else:
            text = "[bold red]All installations failed.[/bold red]\n\n"
            text += "Please check the error messages above and try again."

        # Use renderable for proper rich markup display
        from rich.text import Text as RichText

        rich_text = RichText.from_markup(text)
        self.update(rich_text)


class StrategyTUI(App[None]):
    """Textual TUI app for strategy selection."""

    CSS = """
    Screen {
        background: $background;
    }

    Header {
        background: $primary;
        text-style: bold;
    }

    #main-container {
        height: 1fr;
    }

    StrategyTable {
        height: 1fr;
    }

    DataTable {
        background: $surface;
    }

    DataTable.--header {
        background: $primary;
        text-style: bold;
    }

    DataTable.--cursor {
        background: $secondary;
        text-style: bold;
    }

    #button-container {
        height: 3;
        dock: top;
        padding: 0 1;
        background: $surface;
    }

    Button {
        width: 1fr;
    }

    Button:hover {
        background: $secondary;
    }

    #status-panel {
        height: 10;
        background: $surface;
        border: solid $primary;
        padding: 1 2;
        margin: 1 2;
    }

    #help-footer {
        height: 3;
        background: $primary;
        text-style: bold;
        padding: 0 1;
    }
    """

    BINDINGS = [
        Binding("q", "quit", "Quit"),
        Binding("up", "move_cursor_up", "Up"),
        Binding("down", "move_cursor_down", "Down"),
        Binding("space", "cycle_scope", "Toggle Scope"),
        Binding("enter", "submit_selection", "Submit"),
    ]

    def __init__(self) -> None:
        """Initialize the TUI app."""
        super().__init__()
        self.console = Console()
        self.preset_manager = PresetManager()
        self.git_manager = GitStrategyManager()
        self.strategies: list[StrategyItem] = []
        self.status_panel: StatusPanel | None = None

    def compose(self) -> ComposeResult:
        """Compose the TUI UI."""
        yield Header()
        yield Container(
            Vertical(
                Container(id="button-container"),
                StrategyTable(
                    id="strategy-table",
                    strategies=self.strategies,
                    zebra_stripes=True,
                ),
            ),
            id="main-container",
        )
        yield StatusPanel(id="status-panel")
        yield Footer()

    def on_mount(self) -> None:
        """Initialize the TUI on mount."""
        self.title = "cl-preset Strategy Selector"
        self.sub_title = "Select strategies and scopes to install"

        # Load strategies
        self.strategies = self._load_strategies()

        # Set up the table
        table = self.query_one("#strategy-table", StrategyTable)
        table.strategies = self.strategies

        # Add columns
        table.add_column("Status", width=6)
        table.add_column("Type", width=12)
        table.add_column("Name", width=30)
        table.add_column("Description", width=70)

        # Add rows
        for strategy in self.strategies:
            status_text = Text(str(strategy.scope_status.value))
            status_text.style = "dim"

            type_label = self._get_type_label(strategy.strategy_type)

            # Truncate description if too long
            desc = strategy.description
            if len(desc) > 67:
                desc = desc[:67] + "..."

            table.add_row(
                status_text,
                type_label,
                strategy.name,
                desc,
            )

        # Add buttons
        button_container = self.query_one("#button-container", Container)
        button_container.mount(
            Horizontal(
                Button("Submit & Install", id="submit-button", variant="primary"),
                Button("Quit", id="quit-button", variant="default"),
            )
        )

        # Get status panel reference
        self.status_panel = self.query_one("#status-panel", StatusPanel)

        # Set focus to the table for keyboard navigation
        table.focus()

    def _load_strategies(self) -> list[StrategyItem]:
        """Load all available strategies.

        Returns:
            List of StrategyItem objects
        """
        strategies = []

        # Add built-in presets from examples directory
        examples_path = Path(__file__).parent.parent.parent / "examples" / "presets"
        if examples_path.exists():
            for preset_dir in examples_path.iterdir():
                if preset_dir.is_dir():
                    readme_path = preset_dir / "README.md"
                    description = ""
                    if readme_path.exists():
                        description = readme_path.read_text()
                        # Extract first line after title
                        for line in description.split("\n")[1:10]:
                            line = line.strip()
                            if line and not line.startswith("#"):
                                description = line
                                break

                    strategies.append(
                        StrategyItem(
                            name=preset_dir.name,
                            strategy_type="preset",
                            description=description or f"Preset from {preset_dir.name}",
                            path=preset_dir,
                        )
                    )

        # Add git strategies
        git_strategies = self.git_manager.list_strategies(include_builtin=True)
        for strategy in git_strategies:
            strategies.append(
                StrategyItem(
                    name=strategy["name"],
                    strategy_type="git-strategy",
                    description=strategy["description"],
                    source=strategy.get("source", "builtin"),
                )
            )

        # Discover agents from ~/.claude/agents/
        agents_path = Path.home() / ".claude" / "agents"
        if agents_path.exists():
            for agent_file in agents_path.glob("*.md"):
                name = agent_file.stem
                description = self._extract_frontmatter_description(agent_file)
                strategies.append(
                    StrategyItem(
                        name=name,
                        strategy_type="agent",
                        description=description or f"Agent: {name}",
                        path=agent_file,
                        source="user",
                    )
                )

        # Discover agents from project .claude/agents/
        project_agents_path = Path.cwd() / ".claude" / "agents"
        if project_agents_path.exists():
            for agent_file in project_agents_path.glob("*.md"):
                name = agent_file.stem
                # Skip if already added from user scope
                if any(s.name == name and s.strategy_type == "agent" for s in strategies):
                    continue
                description = self._extract_frontmatter_description(agent_file)
                strategies.append(
                    StrategyItem(
                        name=name,
                        strategy_type="agent",
                        description=description or f"Agent: {name}",
                        path=agent_file,
                        source="project",
                    )
                )

        # Discover skills from ~/.claude/skills/
        skills_path = Path.home() / ".claude" / "skills"
        if skills_path.exists():
            for skill_file in skills_path.glob("*.md"):
                name = skill_file.stem
                description = self._extract_frontmatter_description(skill_file)
                strategies.append(
                    StrategyItem(
                        name=name,
                        strategy_type="skill",
                        description=description or f"Skill: {name}",
                        path=skill_file,
                        source="user",
                    )
                )

        # Discover skills from project .claude/skills/
        project_skills_path = Path.cwd() / ".claude" / "skills"
        if project_skills_path.exists():
            for skill_file in project_skills_path.glob("*.md"):
                name = skill_file.stem
                if any(s.name == name and s.strategy_type == "skill" for s in strategies):
                    continue
                description = self._extract_frontmatter_description(skill_file)
                strategies.append(
                    StrategyItem(
                        name=name,
                        strategy_type="skill",
                        description=description or f"Skill: {name}",
                        path=skill_file,
                        source="project",
                    )
                )

        # Discover rules from ~/.claude/rules/
        rules_path = Path.home() / ".claude" / "rules"
        if rules_path.exists():
            for category_dir in rules_path.iterdir():
                if category_dir.is_dir():
                    category = category_dir.name
                    for rule_file in category_dir.glob("*.md"):
                        name = rule_file.stem
                        description = self._extract_frontmatter_description(rule_file)
                        strategies.append(
                            StrategyItem(
                                name=f"{category}/{name}",
                                strategy_type="rule",
                                description=description or f"Rule: {category}/{name}",
                                path=rule_file,
                                source="user",
                            )
                        )

        # Discover rules from project .claude/rules/
        project_rules_path = Path.cwd() / ".claude" / "rules"
        if project_rules_path.exists():
            for category_dir in project_rules_path.iterdir():
                if category_dir.is_dir():
                    category = category_dir.name
                    for rule_file in category_dir.glob("*.md"):
                        name = rule_file.stem
                        full_name = f"{category}/{name}"
                        if any(
                            s.name == full_name and s.strategy_type == "rule" for s in strategies
                        ):
                            continue
                        description = self._extract_frontmatter_description(rule_file)
                        strategies.append(
                            StrategyItem(
                                name=full_name,
                                strategy_type="rule",
                                description=description or f"Rule: {category}/{name}",
                                path=rule_file,
                                source="project",
                            )
                        )

        return strategies

    def _extract_frontmatter_description(self, file_path: Path) -> str:
        """Extract description from YAML frontmatter."""
        try:
            content = file_path.read_text()
            if content.startswith("---"):
                parts = content.split("---", 2)
                if len(parts) >= 2:
                    frontmatter = parts[1]
                    # Look for description field
                    for line in frontmatter.split("\n"):
                        if line.startswith("description:"):
                            # Extract description value
                            desc = line.split(":", 1)[1].strip()
                            # Remove quotes if present
                            desc = desc.strip('"').strip("'")
                            return desc
        except Exception:
            pass
        return ""

    def _get_type_label(self, strategy_type: str) -> str:
        """Get formatted label for strategy type."""
        labels = {
            "preset": "[Preset]",
            "git-strategy": "[Git]",
            "agent": "[Agent]",
            "skill": "[Skill]",
            "rule": "[Rule]",
        }
        return labels.get(strategy_type, f"[{strategy_type}]")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Handle button press events.

        Args:
            event: Button pressed event
        """
        if event.button.id == "submit-button":
            self.install_selected_strategies()
        elif event.button.id == "quit-button":
            self.exit()

    def action_move_cursor_up(self) -> None:
        """Move cursor up in the table."""
        table = self.query_one("#strategy-table", StrategyTable)
        table.action_cursor_up()

    def action_move_cursor_down(self) -> None:
        """Move cursor down in the table."""
        table = self.query_one("#strategy-table", StrategyTable)
        table.action_cursor_down()

    def action_cycle_scope(self) -> None:
        """Cycle scope for current row."""
        table = self.query_one("#strategy-table", StrategyTable)
        table.cycle_scope()

    def action_submit_selection(self) -> None:
        """Submit the current selection."""
        self.install_selected_strategies()

    def install_selected_strategies(self) -> None:
        """Install all selected strategies."""
        table = self.query_one("#strategy-table", StrategyTable)
        selections = table.get_selected_strategies()

        if not selections:
            if self.status_panel:
                from rich.text import Text as RichText

                message = (
                    "[yellow]No strategies selected.[/yellow]\n"
                    "Press [bold]Space[/bold] on a row to select a scope."
                )
                rich_text = RichText.from_markup(message)
                self.status_panel.update(rich_text)
            return

        # Install all selected strategies
        results = []
        for strategy, scope in selections:
            strategy_type = strategy.strategy_type
            strategy_name = strategy.name

            if strategy_type == "preset":
                success = self._install_preset(strategy, scope)
            else:  # git-strategy
                success = self._apply_git_strategy(strategy, scope)

            results.append(
                {
                    "name": strategy_name,
                    "type": strategy_type,
                    "scope": scope.value,
                    "success": success,
                }
            )

        # Show results
        if self.status_panel:
            self.status_panel.show_results(results)

    def _install_preset(self, strategy: StrategyItem, scope: PresetScope) -> bool:
        """Install a preset strategy.

        Args:
            strategy: StrategyItem to install
            scope: Installation scope

        Returns:
            True if successful, False otherwise
        """
        from cl_preset.manager import Preset

        if strategy.path is None:
            return False

        source_path = strategy.path

        # Try to read preset metadata
        metadata_path = source_path / "preset.json"
        if metadata_path.exists():
            import json

            metadata_data = json.loads(metadata_path.read_text())
            metadata = PresetMetadata(**metadata_data)
        else:
            # Create metadata from directory name
            metadata = PresetMetadata(
                name=strategy.name,
                version="1.0.0",
                description=strategy.description,
                preset_type=PresetType.STRATEGY,
            )

        preset = Preset(
            config={
                "metadata": metadata,
                "source_path": source_path,
                "scope": scope,
            }
        )

        return self.preset_manager.install(preset)

    def _apply_git_strategy(self, strategy: StrategyItem, scope: PresetScope) -> bool:
        """Apply a git strategy.

        Args:
            strategy: StrategyItem to apply
            scope: Installation scope

        Returns:
            True if successful, False otherwise
        """
        import subprocess

        name = strategy.name
        git_strategy = self.git_manager.get_strategy(name)

        if git_strategy is None:
            return False

        # Check if we're in a git repository
        try:
            result = subprocess.run(
                ["git", "rev-parse", "--git-dir"],
                capture_output=True,
                text=True,
                cwd=Path.cwd(),
            )
            if result.returncode != 0:
                return False

        except Exception:
            return False

        # Create .moai/config/sections directory
        config_dir = Path.cwd() / ".moai" / "config" / "sections"
        config_dir.mkdir(parents=True, exist_ok=True)

        # Export strategy config
        strategy_file = config_dir / "git-strategy.yaml"
        return self.git_manager.export_strategy_yaml(name, strategy_file)
