"""Status rendering system for terminal applications.

This module provides block-based status rendering for terminal applications
with plugin-configurable views and navigation.

Uses core.ui.design_system for T(), S, C, TagBox, Box, solid, gradient.
"""

import re
import logging
import sys
from dataclasses import dataclass
from typing import List, Dict, Any, Optional, Callable

from core.ui.design_system import T, S, C, TagBox, Box, solid, solid_fg, gradient
from core.io.terminal_state import get_global_width

logger = logging.getLogger(__name__)


def _fg(text: str, color: tuple) -> str:
    """Apply foreground color only (no background)."""
    r, g, b = color
    return f"\033[38;2;{r};{g};{b}m{text}\033[39m"


def _bg_line(text: str, bg: tuple, width: int) -> str:
    """Apply background to entire line, preserving any existing foreground colors.

    Args:
        text: Text that may contain ANSI foreground codes
        bg: RGB tuple for background
        width: Total width to pad to

    Returns:
        Text with background applied, preserving fg codes
    """
    r, g, b = bg
    # Set background at start, pad to width, reset at end
    # The \033[K clears to end of line with current bg
    visible_len = len(re.sub(r'\033\[[0-9;]*m', '', text))
    padding = max(0, width - visible_len)
    return f"\033[48;2;{r};{g};{b}m{text}{' ' * padding}\033[0m"

# Platform check for keyboard shortcut display
IS_WINDOWS = sys.platform == "win32"


@dataclass
class BlockConfig:
    """Configuration for a single status block."""

    width_fraction: float  # 0.25, 0.33, 0.5, 0.67, 1.0
    content_provider: Callable[[], List[str]]  # Function that returns status content
    title: str  # Block title/label
    priority: int = 0  # Block priority within view


@dataclass
class StatusViewConfig:
    """Configuration for a complete status view."""

    name: str  # "Session Stats", "Performance", "My Plugin View"
    plugin_source: str  # Plugin that registered this view
    priority: int  # Display order priority
    blocks: List[BlockConfig]  # Block layout configuration


class StatusViewRegistry:
    """Registry for plugin-configurable status views with navigation."""

    def __init__(self, event_bus=None):
        """Initialize status view registry.

        Args:
            event_bus: Event bus for firing status change events.
        """
        self.views: List[StatusViewConfig] = []
        self.current_index = 0
        self.event_bus = event_bus
        logger.info("StatusViewRegistry initialized")

    def register_status_view(
        self, plugin_name: str, config: StatusViewConfig
    ) -> None:
        """Register a new status view from a plugin.

        Args:
            plugin_name: Name of the plugin registering the view.
            config: StatusViewConfig for the new view.
        """
        self.views.append(config)
        self.views.sort(key=lambda v: v.priority, reverse=True)

        logger.info(
            f"Registered status view '{config.name}' from plugin '{plugin_name}' "
            f"with priority {config.priority}"
        )

    def _view_has_content(self, view: StatusViewConfig) -> bool:
        """Check if a view has any content to display."""
        for block in view.blocks:
            try:
                content = block.content_provider()
                if content:
                    return True
            except Exception:
                pass
        return False

    def cycle_next(self) -> Optional[StatusViewConfig]:
        """Navigate to next status view (skips empty views)."""
        if not self.views:
            return None

        start_index = self.current_index
        for _ in range(len(self.views)):
            self.current_index = (self.current_index + 1) % len(self.views)
            current_view = self.views[self.current_index]

            if self._view_has_content(current_view):
                if self.event_bus:
                    try:
                        from ..events.models import EventType, Event

                        event = Event(
                            type=EventType.STATUS_VIEW_CHANGED,
                            data={"view_name": current_view.name, "direction": "next"},
                            source="status_view_registry",
                        )
                        self.event_bus.fire_event(event)
                    except Exception as e:
                        logger.warning(f"Failed to fire STATUS_VIEW_CHANGED event: {e}")

                logger.debug(f"Cycled to next status view: '{current_view.name}'")
                return current_view

        self.current_index = start_index
        return self.views[self.current_index] if self.views else None

    def cycle_previous(self) -> Optional[StatusViewConfig]:
        """Navigate to previous status view (skips empty views)."""
        if not self.views:
            return None

        start_index = self.current_index
        for _ in range(len(self.views)):
            self.current_index = (self.current_index - 1) % len(self.views)
            current_view = self.views[self.current_index]

            if self._view_has_content(current_view):
                if self.event_bus:
                    try:
                        from ..events.models import EventType, Event

                        event = Event(
                            type=EventType.STATUS_VIEW_CHANGED,
                            data={
                                "view_name": current_view.name,
                                "direction": "previous",
                            },
                            source="status_view_registry",
                        )
                        self.event_bus.fire_event(event)
                    except Exception as e:
                        logger.warning(f"Failed to fire STATUS_VIEW_CHANGED event: {e}")

                logger.debug(f"Cycled to previous status view: '{current_view.name}'")
                return current_view

        self.current_index = start_index
        return self.views[self.current_index] if self.views else None

    def get_current_view(self) -> Optional[StatusViewConfig]:
        """Get the currently active status view."""
        if not self.views:
            return None
        return self.views[self.current_index]

    def get_view_count(self) -> int:
        """Get total number of views with content."""
        return sum(1 for view in self.views if self._view_has_content(view))

    def get_current_view_index(self) -> int:
        """Get 1-indexed position of current view among views with content."""
        if not self.views:
            return 0
        current_view = self.views[self.current_index]
        index = 0
        for view in self.views:
            if self._view_has_content(view):
                index += 1
                if view is current_view:
                    return index
        return index

    def get_view_names(self) -> List[str]:
        """Get names of all registered views."""
        return [view.name for view in self.views]

    def get_active_view_names(self) -> List[str]:
        """Get names of views with content."""
        return [view.name for view in self.views if self._view_has_content(view)]


class StatusRenderer:
    """Block-based status rendering system."""

    def __init__(
        self,
        terminal_width: int = 80,
        status_registry: Optional[StatusViewRegistry] = None,
    ):
        """Initialize status renderer.

        Args:
            terminal_width: Terminal width for layout calculations.
            status_registry: Status view registry for block-based rendering.
        """
        self.terminal_width = terminal_width
        self.status_registry = status_registry
        self.spacing = 4  # Spacing between columns

    def set_terminal_width(self, width: int) -> None:
        """Update terminal width for layout calculations."""
        self.terminal_width = width

    def render_horizontal_layout(self, colorizer_func=None) -> List[str]:
        """Render status views in horizontal layout.

        Args:
            colorizer_func: Optional function to apply colors to text.

        Returns:
            List of formatted status lines.
        """
        if not self.status_registry:
            return []
        return self._render_block_layout(colorizer_func)

    def _render_block_layout(self, colorizer_func=None) -> List[str]:
        """Render flexible block-based layout using StatusViewRegistry.

        Design system style with gutter + solid dark background:
        - Top border (solid dark ▄)
        - Content lines: █ gutter (accent) + content (dark bg)
        - Bottom border with embedded hint section
        """
        if not self.status_registry:
            return []

        current_view = self.status_registry.get_current_view()
        if not current_view:
            return []

        # Get content from all blocks in the current view
        block_contents = []
        for block in current_view.blocks:
            try:
                content = block.content_provider()
                if content:
                    block_contents.append(
                        {
                            "width_fraction": block.width_fraction,
                            "title": block.title,
                            "content": content,
                            "priority": block.priority,
                        }
                    )
            except Exception as e:
                logger.warning(
                    f"Failed to get content from block '{block.title}': {e}"
                )

        if not block_contents:
            return []

        # Sort blocks by priority
        block_contents.sort(key=lambda b: b["priority"], reverse=True)

        # === TOP BORDER (solid dark ▄) ===
        width = get_global_width()
        top_border = solid_fg("▄" * width, T().dark[0])

        lines = [top_border]

        # === CONTENT LINES with gutter + dark background ===
        # Collect all content lines from blocks
        all_content_lines = []
        for block in block_contents:
            all_content_lines.extend(block["content"])

        # Gutter width and content width
        gutter_width = 1
        content_width = width - gutter_width

        # Render each content line with gutter + dark background
        for line in all_content_lines:
            # Strip ANSI to get visible length
            visible_len = len(self._strip_ansi(line))
            # Truncate if too long (preserve ANSI codes)
            if visible_len > content_width - 1:  # -1 for leading space
                display_text = self._truncate_with_ansi(line, content_width - 2) + "…"
            else:
                display_text = line

            # Build: gutter (space with dark bg) + content (dark bg, preserve fg colors)
            gutter = solid(" ", T().dark[0], T().text_dim, gutter_width)
            content = _bg_line(" " + display_text, T().dark[0], content_width)
            lines.append(gutter + content)

        # === BOTTOM BORDER with embedded hint ===
        view_count = self.status_registry.get_view_count()
        if view_count > 1:
            current_index = self.status_registry.get_current_view_index()
            mod_key = "Alt" if IS_WINDOWS else "Opt"

            # Build embedded hint in bottom border
            # Format: ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ ☰ ⇐ Opt+ ⇒ ▉ 1/9 Overview █▀▀▀▀▀▀▀
            hint_visible = f" ☰ ⇐ {mod_key}+ ⇒ ▉ {current_index}/{view_count} {current_view.name} "
            hint_len = len(hint_visible)

            # Build hint with black ▉ separator (blends with black background)
            dim = T().text_dim
            dim_fg = f"\033[38;2;{dim[0]};{dim[1]};{dim[2]}m"
            black_fg = "\033[38;2;0;0;0m"
            hint_text = f"{dim_fg} ☰ ⇐ {mod_key}+ ⇒ {black_fg}▉{dim_fg} {current_index}/{view_count} {current_view.name} "

            # Calculate border widths (right-aligned hint)
            total_width = width
            # Hint section is right-aligned: more border on left, less on right
            right_border_len = 6  # Fixed small right border
            left_border_len = total_width - hint_len - 2 - right_border_len  # -2 for █ markers

            # Build the embedded bottom border
            left_border = solid_fg("▀" * left_border_len, T().dark[0])
            left_marker = solid("█", T().ai_tag, T().text_dark, 1)
            # Manual hint section: black bg with pre-colored text
            hint_section = f"\033[48;2;0;0;0m{hint_text}\033[0m"
            right_marker = solid("█", T().ai_tag, T().text_dark, 1)
            right_border = solid_fg("▀" * right_border_len, T().dark[0])

            bottom_border = left_border + left_marker + hint_section + right_marker + right_border
            lines.append(bottom_border)
        else:
            # No hint - plain bottom border
            lines.append(solid_fg("▀" * width, T().dark[0]))

        return lines

    def _render_single_row_blocks(
        self, block_contents: List[Dict], colorizer_func=None
    ) -> List[str]:
        """Render blocks in a single horizontal row."""
        lines = []

        # Calculate column widths
        total_spacing = (
            (len(block_contents) - 1) * self.spacing
            if len(block_contents) > 1
            else 0
        )
        available_width = self.terminal_width - total_spacing

        column_widths = []
        for block in block_contents:
            width = int(available_width * block["width_fraction"])
            column_widths.append(max(10, width))

        # Find maximum lines across all blocks
        max_lines = (
            max(len(block["content"]) for block in block_contents)
            if block_contents
            else 0
        )

        # Create each row
        for line_idx in range(max_lines):
            columns = []

            for i, block in enumerate(block_contents):
                if line_idx < len(block["content"]):
                    text = block["content"][line_idx]

                    # Skip colorizer for pre-colored content
                    if colorizer_func and "\033[" not in text:
                        text = colorizer_func(text)

                    # Truncate if too long
                    visible_text = self._strip_ansi(text)
                    max_width = column_widths[i]

                    if len(visible_text) > max_width:
                        if max_width > 3:
                            text = self._truncate_with_ansi(text, max_width - 3) + "..."
                        else:
                            text = "..."

                    columns.append(text)
                else:
                    columns.append("")

            # Join columns with spacing
            formatted_line = ""
            for i, col in enumerate(columns):
                formatted_line += col

                if i < len(columns) - 1 and any(columns[i + 1:]):
                    visible_length = len(self._strip_ansi(col))
                    padding = max(0, column_widths[i] - visible_length)
                    formatted_line += " " * padding
                    formatted_line += " " * self.spacing

            if formatted_line.strip():
                lines.append(formatted_line.rstrip())

        return lines

    def _render_multi_row_blocks(
        self, block_contents: List[Dict], colorizer_func=None
    ) -> List[str]:
        """Render blocks that don't fit in a single row."""
        lines = []

        for block in block_contents:
            for content_line in block["content"]:
                # Skip colorizer for pre-colored content
                if colorizer_func and "\033[" not in content_line:
                    content_line = colorizer_func(content_line)

                # Truncate if too long
                visible_text = self._strip_ansi(content_line)
                if len(visible_text) > self.terminal_width - 3:
                    content_line = (
                        self._truncate_with_ansi(content_line, self.terminal_width - 6)
                        + "..."
                    )

                lines.append(content_line)

        return lines

    def render_horizontal_layout_v2(self, colorizer_func=None) -> List[str]:
        """Render status views using modern design system with condensed layout.

        V2 Features:
            - Uses core.ui.design_system (T(), S, C, TagBox, solid, gradient)
            - Condensed layout: no top/bottom borders (input box provides top, hint provides bottom)
            - Content from providers is passed through directly
            - Hint bar with bottom border closes the unified block

        Args:
            colorizer_func: Optional colorizer function (unused in v2, kept for API compatibility).

        Returns:
            List of formatted status lines (position="middle" style - no borders).
        """
        if not self.status_registry:
            return []

        current_view = self.status_registry.get_current_view()
        if not current_view:
            return []

        # Get content from all blocks in the current view
        # Content providers return pre-formatted lines (no borders - position="middle" style)
        # We add top border here (position="first") and bottom border at end (position="last")
        lines = []

        # Add top border to start the status block (TagBox style with tag section)
        width = get_global_width()
        tag_width = 3  # Match input box tag width
        content_width = width - tag_width
        # Tag section uses ai_tag color (green), content uses dark gradient
        top_border = solid_fg("▄" * tag_width, T().ai_tag) + solid_fg("▄" * content_width, T().dark[0])
        lines.append(top_border)

        for block in current_view.blocks:
            try:
                content = block.content_provider()
                if content:
                    # Extend with all lines from content provider
                    lines.extend(content)
            except Exception as e:
                logger.warning(
                    f"Failed to get content from block '{block.title}': {e}"
                )

        if not lines:
            return []

        # Add cycling hint bar if multiple views with content are available
        # Hint bar uses position="last" style (bottom border only)
        view_count = self.status_registry.get_view_count()
        if view_count > 1:
            current_index = self.status_registry.get_current_view_index()
            mod_key = "Alt" if IS_WINDOWS else "Opt"

            # Build hint line in status_v2 style - use global width
            width = get_global_width()
            seg_width = width // 3
            remaining = width - (seg_width * 2)

            # Left: navigation hint
            left_text = f" < {mod_key}+Left/Right >"
            # Middle: view info
            mid_text = f" ~ View {current_index}/{view_count}"
            # Right: view name
            right_text = f" # {current_view.name}"

            # Define segments with muted colors for hint
            segments = [
                (T().dark[0], T().text_dim, left_text, seg_width),
                (T().response_bg[0], T().text_dim, mid_text, seg_width),
                (T().dark[0], T().text_dim, right_text, remaining),
            ]

            # Build hint bar with position="last" (bottom border only)
            hint_mid = "".join(solid(txt.ljust(w)[:w], bg, fg, w) for bg, fg, txt, w in segments)
            hint_bot = "".join(solid_fg("▀" * w, bg) for bg, _, _, w in segments)

            lines.extend([hint_mid, hint_bot])
        else:
            # No hint bar - add bottom border to close the status block
            width = get_global_width()
            lines.append(solid_fg("▀" * width, T().dark[0]))

        return lines

    def _strip_ansi(self, text: str) -> str:
        """Remove ANSI escape codes from text."""
        return re.sub(r"\033\[[0-9;]*m", "", text)

    def _truncate_with_ansi(self, text: str, max_length: int) -> str:
        """Truncate text while preserving ANSI codes."""
        result = ""
        visible_count = 0
        i = 0

        while i < len(text) and visible_count < max_length:
            if text[i:i + 1] == "\033" and i + 1 < len(text) and text[i + 1] == "[":
                # Find end of ANSI sequence
                end = i + 2
                while end < len(text) and text[end] not in "mhlABCDEFGHJKSTfimpsuI":
                    end += 1
                if end < len(text):
                    end += 1
                result += text[i:end]
                i = end
            else:
                result += text[i]
                visible_count += 1
                i += 1

        return result
