"""Reference mode renderer with clean adaptive layout.

Simple algorithm:
1. Height determines rows (1, 2, or 4)
2. Width determines mode (narrow = stack, wide = side-by-side)
3. Each sheet owns its columns completely
4. Fill vertically within each sheet's column allocation
"""

import math
from rich.align import Align
from rich.columns import Columns
from rich.console import Group
from rich.panel import Panel
from rich.table import Table
from rich.text import Text

from .base import ModeRenderer
from ..config import Config
from ..models import Sheet, PanelSpec, SheetStyle


class ReferenceRenderer(ModeRenderer):
    """Reference mode renderer using clean adaptive layout."""

    NARROW_MODE_THRESHOLD = 2  # Max columns for narrow/scroll mode

    @property
    def mode(self) -> str:
        return "reference"

    def render(
        self,
        sheets: list[Sheet],
        terminal_size: tuple[int, int],
        focus: str | None = None,
    ) -> list:
        """Render sheets with clean adaptive layout."""
        layout = Config.LAYOUT
        width, height = terminal_size
        max_columns = width // layout.panel_width

        if max_columns < 1:
            return [self._too_narrow_message()]

        # Sort sheets by focus if specified
        if focus:
            sheets = sorted(sheets, key=lambda s: s.name != focus)

        # Create all display units (panels/grouped panels)
        all_units = []
        sheet_info = {}  # Track units per sheet

        for sheet in sheets:
            units = self._create_display_units(sheet)
            sheet_info[sheet.name] = {
                "units": units,
                "count": len(units),
                "sheet": sheet,
            }
            all_units.extend(units)

        if not all_units:
            return []

        # Determine layout strategy
        target_rows = self._determine_target_rows(height)

        if max_columns <= self.NARROW_MODE_THRESHOLD:
            # Narrow mode: vertical stack for scrolling
            grid = self._build_vertical_stack(sheet_info, max_columns)
            return self._build_output_from_grid(grid)
        else:
            # Wide mode: side-by-side columns with vertical fill
            columns = self._build_columnar_layout(sheet_info, max_columns, target_rows)
            return self._build_output_from_columns(columns)

    def accepts_sheet(self, sheet: Sheet) -> bool:
        """Accept reference mode sheets."""
        return sheet.mode == "reference"

    def _determine_target_rows(self, height: int) -> int:
        """Determine target rows based on terminal height.

        Uses Config.LAYOUT panel heights for calculation.
        """
        layout = Config.LAYOUT
        usable_height = height - 8  # Account for header/padding

        if usable_height < 30:
            return 1
        elif usable_height < 50:
            return 2
        else:
            # Allow up to 4 rows for tall displays
            max_possible = usable_height // layout.full_panel_height
            return min(4, max_possible)

    def _create_display_units(self, sheet: Sheet) -> list:
        """Create display units for a sheet.

        Groups half-height panels together, creates panels for full-height.
        """
        units = []
        sections = sheet.sections[:8]  # Limit sections

        # Process sections to group half-height panels
        processed = self._preprocess_sections(sections)

        for unit_type, content in processed:
            if unit_type == "grouped":
                # Create grouped panels for half-height sections
                # content is a list when grouped
                if isinstance(content, list):
                    panels = [
                        self._create_panel(s, sheet.style)
                        for s in content
                        if isinstance(s, PanelSpec)
                    ]
                    if panels:
                        units.append(Group(*panels))
            else:
                # Single full-height panel
                # content is a single PanelSpec when not grouped
                if isinstance(content, PanelSpec):
                    panel = self._create_panel(content, sheet.style)
                    units.append(panel)

        return units

    def _preprocess_sections(
        self, sections: list
    ) -> list[tuple[str, list | PanelSpec]]:
        """Group half-height panels together.

        Returns list of ("grouped", [sections]) or ("single", section).
        """
        processed = []
        half_stack = []

        for section in sections:
            if not isinstance(section, PanelSpec):
                continue

            if section.size == "half":
                half_stack.append(section)
                if len(half_stack) == 2:
                    # Group two half sections
                    processed.append(("grouped", half_stack[:]))
                    half_stack = []
            else:
                # Full-height section
                if half_stack:
                    # Flush remaining half section
                    processed.append(("single", half_stack[0]))
                    half_stack = []
                processed.append(("single", section))

        # Handle remaining half section
        if half_stack:
            processed.append(("single", half_stack[0]))

        return processed

    def _build_vertical_stack(self, sheet_info: dict, max_columns: int) -> list[list]:
        """Build vertical stack for narrow displays (scrollable).

        Fill each sheet completely before moving to next.
        """
        grid = []

        for sheet_name, info in sheet_info.items():
            units = info["units"]

            # Add rows for this sheet
            for i in range(0, len(units), max_columns):
                row = units[i : i + max_columns]
                grid.append(row)

        return grid

    def _build_columnar_layout(
        self, sheet_info: dict, max_columns: int, target_rows: int
    ) -> list:
        """Build side-by-side columnar layout for wide displays.

        Returns a list of column Groups instead of rows.
        Each sheet owns its columns and fills vertically.
        """
        # Calculate column allocation for each sheet
        allocations = self._allocate_columns(sheet_info, max_columns, target_rows)

        # Build columns - each column will be a vertical Group
        columns = []

        for sheet_name, info in sheet_info.items():
            units = info["units"]
            cols_allocated = allocations[sheet_name]

            if cols_allocated == 0 or not units:
                continue

            # Calculate how many units per column
            units_per_col = math.ceil(len(units) / cols_allocated)

            # Create columns for this sheet
            for col_idx in range(cols_allocated):
                start = col_idx * units_per_col
                end = min(start + units_per_col, len(units))
                column_units = units[start:end]

                if column_units:
                    # Create a vertical group for this column
                    column_group = Group(*column_units)
                    columns.append(column_group)

        return columns

    def _allocate_columns(
        self, sheet_info: dict, max_columns: int, target_rows: int
    ) -> dict[str, int]:
        """Allocate columns to sheets based on their unit count and target rows.

        Each sheet gets enough columns for its units given the target rows.
        """
        allocations = {}
        total_units = sum(info["count"] for info in sheet_info.values())

        if total_units == 0:
            return allocations

        remaining_cols = max_columns
        sheets = list(sheet_info.items())

        for i, (sheet_name, info) in enumerate(sheets):
            unit_count = info["count"]

            # Calculate columns needed for this sheet's units
            needed_cols = math.ceil(unit_count / target_rows)

            if i == len(sheets) - 1:
                # Last sheet gets remaining columns
                cols = min(needed_cols, remaining_cols)
            else:
                # Proportional allocation with minimum 1
                proportion = unit_count / total_units
                cols = max(1, round(max_columns * proportion))
                cols = min(cols, needed_cols, remaining_cols)

            allocations[sheet_name] = cols
            remaining_cols -= cols

        return allocations

    def _build_output_from_grid(self, grid: list[list]) -> list:
        """Build output from a grid of rows (for narrow mode)."""
        header = Align.center(
            Text("KEYBOARD SHORTCUTS", style="bold cyan"), vertical="middle"
        )

        rows = []
        for row_panels in grid:
            if row_panels:
                rows.append(
                    Align.center(
                        Columns(row_panels, padding=1, expand=False), vertical="middle"
                    )
                )

        # Center everything vertically
        return [Align.center(Group(header, Text(""), *rows), vertical="middle")]

    def _build_output_from_columns(self, columns: list) -> list:
        """Build output from column Groups (for wide mode)."""
        header = Align.center(
            Text("KEYBOARD SHORTCUTS", style="bold cyan"), vertical="middle"
        )

        if columns:
            # Single row of columns, each column is a vertical Group
            content = Align.center(
                Columns(columns, padding=1, expand=False, equal=False),
                vertical="middle",
            )
        else:
            content = Text("")

        # Center everything vertically
        return [Align.center(Group(header, Text(""), content), vertical="middle")]

    def _create_panel(self, spec: PanelSpec, style: SheetStyle) -> Panel:
        """Create a styled panel from specification."""
        layout = Config.LAYOUT

        # Determine if this panel uses multi-line layout
        if spec.uses_multiline_layout:
            # Multi-line layout for commands
            table = Table(
                show_header=False,
                show_edge=False,
                padding=(0, 1),
                box=None,
                expand=True,  # Fill the panel width
            )
            # Single column for multi-line display
            table.add_column("Content", no_wrap=True)

            # Add items with command on one line, description indented below
            # Use effective_max_items which accounts for 2-line layout
            for item in spec.items[: spec.effective_max_items]:
                if "cmd" in item:
                    cmd = item.get("cmd", "")
                    desc = item.get("desc", "")
                    # Add command
                    table.add_row(f"[{style.key_color}]{cmd}[/{style.key_color}]")
                    # Add indented description if present
                    if desc:
                        indent = " " * layout.cmd_line_indent
                        table.add_row(
                            f"[{style.desc_color}]{indent}{desc}[/{style.desc_color}]"
                        )
        else:
            # Traditional single-line layout for keyboard shortcuts
            table = Table(
                show_header=False,
                show_edge=False,
                padding=(0, 1),
                box=None,
                expand=True,  # Fill the panel width
            )

            # Let Rich auto-size columns based on content
            table.add_column("Key", style=style.key_color, no_wrap=True)
            table.add_column("Desc", style=style.desc_color, no_wrap=True)

            # Add items
            for item in spec.items[: spec.max_items]:
                if "key" in item:
                    key = item.get("key", "")
                    desc = item.get("desc", "")
                    table.add_row(key, desc)

        # Build title
        title = spec.title
        if spec.base:
            title = f"{title} ({spec.base})"

        # Center content vertically within panel
        content = Align(table, vertical="middle")

        return Panel(
            content,
            title=f"[{style.title_color}]{title}[/{style.title_color}]",
            title_align="left",
            border_style=style.border_color,
            width=Config.LAYOUT.panel_width,
            height=spec.height,
        )

    def _too_narrow_message(self) -> Align:
        """Message when terminal is too narrow."""
        return Align.center(
            Panel(
                "[dim]Terminal too narrow.\nMinimum width: 40 columns[/dim]",
                border_style="red",
                title="[red]Error[/red]",
            ),
            vertical="middle",
        )
