"""Rich UI Components for AiCippy CLI.

Provides beautiful terminal widgets including:
- Animated progress bars with gradients
- Status sidebar with agent visualization
- Task checklist footer
- Keyboard shortcut overlay
"""

from __future__ import annotations

import time
from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING, Final

from rich.align import Align
from rich.box import ROUNDED
from rich.console import Console, Group, RenderableType
from rich.layout import Layout
from rich.markdown import Markdown
from rich.panel import Panel
from rich.progress import ProgressColumn, Task
from rich.rule import Rule
from rich.text import Text

from aicippy.cli import themes
from aicippy.cli.mascot import MascotAnimator, MascotMood
from aicippy.cli.terminal_caps import get_layout_mode
from aicippy.cli.terminal_caps import status_icon as _tc_status_icon

if TYPE_CHECKING:
    from collections.abc import Sequence

# ============================================================================
# Constants & Theme
# ============================================================================

# Brand colors are read from themes module at call time so that
# runtime theme changes (via /theme) are always reflected.

# Backward-compatible re-exports: other modules may still do
#   ``from aicippy.cli.ui_components import BRAND_PRIMARY``
# The module-level __getattr__ below delegates these to the themes module
# so that runtime theme switches propagate automatically.
_BRAND_REEXPORTS: Final[frozenset[str]] = frozenset(
    {
        "BRAND_PRIMARY",
        "BRAND_SECONDARY",
        "BRAND_ACCENT",
        "BRAND_SUCCESS",
        "BRAND_WARNING",
        "BRAND_ERROR",
        "BRAND_INFO",
    },
)


# Magic value constants
_AGENT_NAME_MAX_LEN: Final[int] = 12
_AGENT_NAME_TRUNC_LEN: Final[int] = 11
_LATENCY_GOOD: Final[int] = 100
_LATENCY_FAIR: Final[int] = 300
_MAX_TOOL_OUTPUT_LINES: Final[int] = 20
_MAX_STREAMING_CONTENT_LEN: Final[int] = 100
_STREAM_TRUNC_LEN: Final[int] = 97


def __getattr__(name: str) -> str:
    """Lazy re-export of BRAND_* constants from themes module."""
    if name in _BRAND_REEXPORTS:
        value: str = getattr(themes, name)
        return value
    msg = f"module {__name__!r} has no attribute {name!r}"
    raise AttributeError(msg)


# Gradient colors for progress bars
PROGRESS_GRADIENT: Final[tuple[str, ...]] = (
    "#667eea",
    "#764ba2",
    "#f093fb",
    "#f5576c",
)

# Status icons — delegates to terminal_caps.status_icon() for UTF-8 safety
STATUS_ICONS: dict[str, str] = {
    k: _tc_status_icon(k)
    for k in [
        "running",
        "thinking",
        "complete",
        "error",
        "idle",
        "pending",
        "in_progress",
    ]
}

# Keyboard shortcuts
KEYBOARD_SHORTCUTS: Final[list[tuple[str, str]]] = [
    ("Ctrl+C", "Cancel/Interrupt"),
    ("Ctrl+D", "Exit session"),
    ("↑/↓", "History navigation"),
    ("Tab", "Auto-complete"),
    ("/help", "Show commands"),
    ("quit", "Exit AiCippy"),
]


# ============================================================================
# Data Classes
# ============================================================================


@dataclass
class TaskItem:
    """A task item for the checklist."""

    content: str
    status: str = "pending"  # pending, in_progress, completed
    progress: int = 0

    @property
    def icon(self) -> str:
        """Get status icon."""
        return STATUS_ICONS.get(self.status, "○")

    @property
    def style(self) -> str:
        """Get status style."""
        return {
            "pending": "dim",
            "in_progress": f"bold {themes.BRAND_INFO}",
            "completed": f"{themes.BRAND_SUCCESS}",
        }.get(self.status, "white")


@dataclass
class AgentWidget:
    """Agent status widget data."""

    id: str
    name: str
    status: str
    progress: int = 0
    message: str = ""
    tokens: int = 0


# ============================================================================
# Custom Progress Columns
# ============================================================================


class GradientBarColumn(ProgressColumn):
    """A progress bar with gradient effect."""

    def __init__(
        self,
        bar_width: int = 20,
        complete_style: str = themes.BRAND_SUCCESS,
        finished_style: str = themes.BRAND_SUCCESS,
    ) -> None:
        """Initialize gradient bar column."""
        self.bar_width = bar_width
        self.complete_style = complete_style
        self.finished_style = finished_style
        super().__init__()

    def render(self, task: Task) -> Text:
        """Render the gradient progress bar."""
        completed = int(task.percentage / 100 * self.bar_width)
        remaining = self.bar_width - completed

        # Build gradient bar
        bar = Text()

        # Completed portion with gradient
        for i in range(completed):
            color_idx = int(i / self.bar_width * len(PROGRESS_GRADIENT))
            color_idx = min(color_idx, len(PROGRESS_GRADIENT) - 1)
            bar.append("█", style=PROGRESS_GRADIENT[color_idx])

        # Remaining portion
        bar.append("░" * remaining, style="dim")

        return bar


class TaskStatusColumn(ProgressColumn):
    """Column showing task status with icon."""

    def render(self, task: Task) -> Text:
        """Render status with icon."""
        status = task.fields.get("status", "running")
        icon = STATUS_ICONS.get(status, "○")

        color = {
            "running": themes.BRAND_INFO,
            "thinking": themes.BRAND_WARNING,
            "complete": themes.BRAND_SUCCESS,
            "error": themes.BRAND_ERROR,
            "idle": "dim",
        }.get(status, "white")

        return Text(f"{icon} {status}", style=color)


# ============================================================================
# Widget Components
# ============================================================================


def create_header_panel(version: str = "1.0.0") -> Panel:
    """Create the main header panel with branding.

    Args:
        version: Application version.

    Returns:
        Rich Panel with header content.

    """
    # ASCII art logo with gradient - clean AiCippy text
    logo_text = Text()
    logo_lines = [
        "     _    _  ____  _                     ",
        "    / \\  (_)/ ___||_|_ __  _ __  _   _   ",
        "   / _ \\ | | |    | | '_ \\| '_ \\| | | |  ",
        "  / ___ \\| | |___ | | |_) | |_) | |_| |  ",
        " /_/   \\_\\_|\\____||_| .__/| .__/ \\__, |  ",
        "                    |_|   |_|    |___/   ",
    ]

    for i, line in enumerate(logo_lines):
        color_idx = int(i / len(logo_lines) * len(PROGRESS_GRADIENT))
        color_idx = min(color_idx, len(PROGRESS_GRADIENT) - 1)
        logo_text.append(line + "\n", style=PROGRESS_GRADIENT[color_idx])

    subtitle = Text()
    sub_style = f"italic {themes.BRAND_SECONDARY}"
    subtitle.append("\n  Enterprise Multi-Agent System  ", style=sub_style)
    subtitle.append(f"v{version}", style="dim")

    return Panel(
        Group(logo_text, subtitle),
        border_style=themes.BRAND_PRIMARY,
        box=ROUNDED,
        padding=(0, 2),
    )


def create_status_sidebar(
    agents: Sequence[AgentWidget],
    session_model: str = "odin",
    tokens_used: int = 0,
    connected: bool = True,
    mascot_mood: str = "idle",
    mascot_frame: int = 0,
) -> Panel:
    """Create the status sidebar widget with mini mascot.

    Args:
        agents: List of agent widgets.
        session_model: Current model name.
        tokens_used: Total tokens used.
        connected: Connection status.
        mascot_mood: Current mascot mood.
        mascot_frame: Current animation frame.

    Returns:
        Rich Panel with sidebar content.

    """
    content_parts: list[RenderableType] = []

    # Mini mascot at the top
    mood_map = {
        "idle": MascotMood.IDLE,
        "thinking": MascotMood.THINKING,
        "working": MascotMood.WORKING,
        "happy": MascotMood.HAPPY,
        "waving": MascotMood.WAVING,
    }
    mood = mood_map.get(mascot_mood, MascotMood.IDLE)
    animator = MascotAnimator(mood=mood, use_mini=True)
    frames = animator.get_frames()
    frame = frames[mascot_frame % len(frames)]
    mascot_text = animator.render_frame(frame)
    content_parts.append(Align.center(mascot_text))
    content_parts.append(Rule(style="dim"))
    content_parts.append(Text())

    # Connection status
    conn_icon = "●" if connected else "○"
    conn_color = themes.BRAND_SUCCESS if connected else themes.BRAND_ERROR
    conn_text = "Connected" if connected else "Offline"
    conn_status = Text()
    conn_status.append(f" {conn_icon} ", style=conn_color)
    conn_status.append(conn_text, style=f"bold {conn_color}")
    content_parts.append(conn_status)
    content_parts.append(Text())

    # Model info
    model_display = Text()
    model_display.append(" ◈ ", style=themes.BRAND_ACCENT)
    model_display.append("Model: ", style="dim")
    model_display.append(session_model.upper(), style=f"bold {themes.BRAND_PRIMARY}")
    content_parts.append(model_display)

    # Tokens
    tokens_display = Text()
    tokens_display.append(" ◇ ", style=themes.BRAND_WARNING)
    tokens_display.append("Tokens: ", style="dim")
    tokens_display.append(f"{tokens_used:,}", style=f"bold {themes.BRAND_WARNING}")
    content_parts.append(tokens_display)

    content_parts.append(Text())
    content_parts.append(Rule(style="dim"))
    content_parts.append(Text())

    # Agents header
    agents_header = Text()
    agents_header.append(" ⚡ AGENTS ", style=f"bold {themes.BRAND_INFO}")
    agents_header.append(f"({len(agents)}/10)", style="dim")
    content_parts.append(agents_header)
    content_parts.append(Text())

    # Agent list
    if agents:
        for agent in agents:
            agent_line = Text()
            icon = STATUS_ICONS.get(agent.status, "○")
            color = {
                "running": themes.BRAND_INFO,
                "thinking": themes.BRAND_WARNING,
                "complete": themes.BRAND_SUCCESS,
                "error": themes.BRAND_ERROR,
                "idle": "dim",
            }.get(agent.status, "white")

            agent_line.append(f"   {icon} ", style=color)
            display_name = (
                agent.name[:_AGENT_NAME_TRUNC_LEN] + "\u2026"
                if len(agent.name) > _AGENT_NAME_MAX_LEN
                else agent.name
            )
            agent_line.append(f"{display_name:<12}", style="white")

            # Mini progress bar
            bar_width = 8
            completed = int(agent.progress / 100 * bar_width)
            bar = "█" * completed + "░" * (bar_width - completed)
            agent_line.append(f" {bar} ", style=color)
            agent_line.append(f"{agent.progress:>3}%", style="dim")

            content_parts.append(agent_line)
    else:
        no_agents = Text("   No active agents", style="dim italic")
        content_parts.append(no_agents)

    return Panel(
        Group(*content_parts),
        title="[bold]◐ Status[/bold]",
        title_align="left",
        border_style=themes.BRAND_PRIMARY,
        box=ROUNDED,
        width=32,
        padding=(1, 1),
    )


def create_task_footer(
    tasks: Sequence[TaskItem],
    current_task_idx: int = 0,
) -> Panel:
    """Create the task checklist footer.

    Args:
        tasks: List of task items.
        current_task_idx: Index of current task.

    Returns:
        Rich Panel with task checklist.

    """
    content = Text()

    # Tasks header
    content.append(" 📋 TASKS ", style=f"bold {themes.BRAND_INFO}")

    completed = sum(1 for t in tasks if t.status == "completed")
    content.append(f"({completed}/{len(tasks)}) ", style="dim")

    # Show current and next tasks
    if tasks:
        content.append("\n")

        for i, task in enumerate(tasks):
            if i > current_task_idx + 2:
                break

            # Task line
            icon = task.icon
            style = task.style

            # Highlight current task
            if i == current_task_idx:
                content.append(f"  → {icon} ", style=f"bold {themes.BRAND_INFO}")
                content.append(f"{task.content[:40]}", style="bold white")
                if task.progress > 0 and task.status == "in_progress":
                    content.append(f" ({task.progress}%)", style=themes.BRAND_INFO)
            else:
                content.append(f"    {icon} ", style=style)
                content.append(f"{task.content[:40]}", style=style)

            if i < len(tasks) - 1 and i < current_task_idx + 2:
                content.append("\n")
    else:
        content.append("\n    No tasks", style="dim italic")

    return Panel(
        content,
        border_style="dim",
        box=ROUNDED,
        padding=(0, 1),
    )


def create_shortcuts_panel() -> Panel:
    """Create the keyboard shortcuts panel.

    Returns:
        Rich Panel with shortcuts.

    """
    content = Text()
    content.append(" ⌨ ", style=themes.BRAND_ACCENT)

    for i, (key, desc) in enumerate(KEYBOARD_SHORTCUTS[:4]):  # Show first 4
        if i > 0:
            content.append("  ", style="dim")
        content.append(key, style=themes.BRAND_PRIMARY)
        content.append(f" {desc}", style="dim")

    return Panel(
        content,
        border_style="dim",
        box=ROUNDED,
        padding=(0, 1),
    )


def create_input_prompt(
    mode: str = "agent",
    model: str = "odin",
    show_hint: bool = True,
) -> Text:
    """Create a beautiful input prompt.

    Args:
        mode: Current mode.
        model: Current model.
        show_hint: Whether to show hint line.

    Returns:
        Rich Text for prompt.

    """
    prompt = Text()

    # Mode indicator
    mode_colors = {
        "agent": themes.BRAND_INFO,
        "edit": themes.BRAND_WARNING,
        "research": themes.BRAND_ACCENT,
        "code": themes.BRAND_SUCCESS,
    }
    mode_color = mode_colors.get(mode, themes.BRAND_PRIMARY)

    prompt.append("┌─", style="dim")
    prompt.append(f"[{mode}]", style=mode_color)
    prompt.append("─", style="dim")
    prompt.append(f"[{model}]", style=themes.BRAND_SECONDARY)
    prompt.append("─────────────────────", style="dim")
    prompt.append("\n")

    # Add hint line
    if show_hint:
        prompt.append("│ ", style="dim")
        prompt.append("Type message, ", style="dim")
        prompt.append("/help", style=themes.BRAND_INFO)
        prompt.append(" for commands, or ", style="dim")
        prompt.append("quit", style=themes.BRAND_WARNING)
        prompt.append(" to exit", style="dim")
        prompt.append("\n")

    prompt.append("└─> ", style=themes.BRAND_PRIMARY)

    return prompt


def create_response_panel(
    content: str,
    agent_type: str | None = None,
    tokens_used: int = 0,
    execution_time: float = 0.0,
) -> Panel:
    """Create a panel for AI response display.

    Args:
        content: Response content (markdown).
        agent_type: Type of agent that responded.
        tokens_used: Tokens used for response.
        execution_time: Response time in seconds.

    Returns:
        Rich Panel with response.

    """
    # Header with agent info
    header_parts = []
    if agent_type:
        header_parts.append(f"[{themes.BRAND_INFO}]◈ {agent_type.upper()}[/]")
    if tokens_used:
        header_parts.append(f"[dim]{tokens_used:,} tokens[/]")
    if execution_time:
        header_parts.append(f"[dim]{execution_time:.1f}s[/]")

    title = " │ ".join(header_parts) if header_parts else "Response"

    return Panel(
        Markdown(content),
        title=title,
        title_align="left",
        border_style=themes.BRAND_SUCCESS,
        box=ROUNDED,
        padding=(1, 2),
    )


def create_thinking_indicator(frame_idx: int = 0) -> Panel:
    """Create a thinking/processing indicator with animated mascot.

    Args:
        frame_idx: Animation frame index for mascot.

    Returns:
        Rich Panel with spinner and mascot.

    """
    # Mini mascot in thinking pose
    animator = MascotAnimator(mood=MascotMood.WORKING, use_mini=True)
    frames = animator.get_frames()
    frame = frames[frame_idx % len(frames)]
    mascot_text = animator.render_frame(frame)

    content = Text()
    content.append("  ", style="dim")
    content.append("◐ ", style=f"bold {themes.BRAND_INFO}")
    content.append("Thinking", style=themes.BRAND_INFO)
    content.append(" ", style="dim")
    # Use animated dots without blink (better terminal compatibility)
    dot_styles = [themes.BRAND_ACCENT, themes.BRAND_SECONDARY, themes.BRAND_PRIMARY]
    for _i, color in enumerate(dot_styles):
        # Alternating brightness based on frame for visual interest
        content.append("●", style=f"bold {color}")

    return Panel(
        Group(Align.center(mascot_text), content),
        border_style=themes.BRAND_INFO,
        box=ROUNDED,
        padding=(0, 1),
    )


def create_error_panel(error: str, suggestion: str | None = None) -> Panel:
    """Create an error display panel.

    Args:
        error: Error message.
        suggestion: Optional suggestion for fixing.

    Returns:
        Rich Panel with error.

    """
    content = Text()
    content.append(" ✗ ", style=f"bold {themes.BRAND_ERROR}")
    content.append(error, style=themes.BRAND_ERROR)

    if suggestion:
        content.append("\n")
        content.append(" 💡 ", style=themes.BRAND_WARNING)
        content.append(suggestion, style="italic dim")

    return Panel(
        content,
        title="[bold red]Error[/bold red]",
        border_style=themes.BRAND_ERROR,
        box=ROUNDED,
        padding=(0, 1),
    )


def create_success_panel(message: str, details: str | None = None) -> Panel:
    """Create a success display panel.

    Args:
        message: Success message.
        details: Optional additional details.

    Returns:
        Rich Panel with success message.

    """
    content = Text()
    content.append(" ✓ ", style=f"bold {themes.BRAND_SUCCESS}")
    content.append(message, style=themes.BRAND_SUCCESS)

    if details:
        content.append("\n")
        content.append(f"   {details}", style="dim")

    return Panel(
        content,
        border_style=themes.BRAND_SUCCESS,
        box=ROUNDED,
        padding=(0, 1),
    )


# ============================================================================
# New Layout Components for Live Preview Interface
# ============================================================================

# Preview area configuration
PREVIEW_LINES: Final[int] = 25  # Number of lines in preview area


@dataclass
class PreviewLine:
    """A line in the preview buffer."""

    content: str
    style: str = "white"
    timestamp: str = ""
    line_type: str = "info"  # info, command, output, error, success, agent


class LivePreviewBuffer:
    """Buffer for the live preview area with auto-scrolling.

    Maintains a fixed-size buffer of lines that can be displayed
    in the preview panel with auto-scroll to latest content.
    """

    def __init__(self, max_lines: int = PREVIEW_LINES) -> None:
        """Initialize preview buffer."""
        self.max_lines = max_lines
        self._lines: list[PreviewLine] = []

    def add_line(
        self,
        content: str,
        style: str = "white",
        line_type: str = "info",
    ) -> None:
        """Add a line to the buffer."""
        timestamp = datetime.now().strftime("%H:%M:%S")
        self._lines.append(
            PreviewLine(
                content=content,
                style=style,
                timestamp=timestamp,
                line_type=line_type,
            ),
        )

        # Keep only max_lines
        if len(self._lines) > self.max_lines * 3:  # Keep some history
            self._lines = self._lines[-self.max_lines * 2 :]

    def add_command(self, command: str) -> None:
        """Add a command line."""
        self.add_line(f"$ {command}", style=themes.BRAND_INFO, line_type="command")

    def add_output(self, output: str) -> None:
        """Add command output lines."""
        for line in output.split("\n"):
            if line.strip():
                self.add_line(f"  {line}", style="white", line_type="output")

    def add_error(self, error: str) -> None:
        """Add an error line."""
        self.add_line(f"✗ {error}", style=themes.BRAND_ERROR, line_type="error")

    def add_success(self, message: str) -> None:
        """Add a success line."""
        self.add_line(f"✓ {message}", style=themes.BRAND_SUCCESS, line_type="success")

    def add_agent_update(self, agent_id: str, status: str, message: str = "") -> None:
        """Add an agent status update."""
        icon = STATUS_ICONS.get(status, "○")
        color = {
            "running": themes.BRAND_INFO,
            "thinking": themes.BRAND_WARNING,
            "complete": themes.BRAND_SUCCESS,
            "error": themes.BRAND_ERROR,
        }.get(status, "dim")
        text = f"{icon} [{agent_id}] {status}"
        if message:
            text += f": {message}"
        self.add_line(text, style=color, line_type="agent")

    def add_streaming(self, chunk: str) -> None:
        """Add streaming content (appends to last line if same type)."""
        if self._lines and self._lines[-1].line_type == "streaming":
            self._lines[-1].content += chunk
        else:
            self.add_line(chunk, style=themes.BRAND_ACCENT, line_type="streaming")

    def get_visible_lines(self) -> list[PreviewLine]:
        """Get lines visible in the preview area."""
        return self._lines[-self.max_lines :]

    def clear(self) -> None:
        """Clear the buffer."""
        self._lines.clear()

    def render(self) -> Text:
        """Render the buffer as Rich Text."""
        text = Text()
        visible = self.get_visible_lines()

        # Pad with empty lines if needed
        empty_lines = self.max_lines - len(visible)
        for _ in range(empty_lines):
            text.append("\n")

        for i, line in enumerate(visible):
            # Timestamp
            text.append(f"[{line.timestamp}] ", style="dim")
            # Content
            text.append(line.content, style=line.style)
            if i < len(visible) - 1:
                text.append("\n")

        return text


def create_compact_header(
    version: str = "1.0.0",
    mascot_mood: str = "idle",
    working_dir: str = "",
) -> Panel:
    """Create the header bar with animated ZooZoo mascot, app name, and working directory.

    Args:
        version: Application version.
        mascot_mood: Current ZooZoo mood for animated indicator.
        working_dir: Current working directory path.

    Returns:
        Header panel with mascot, app info, and directory.

    """
    # ZooZoo mood animations — expressive live indicators
    mood_frames: dict[str, tuple[str, str]] = {
        "idle": ("🐨", themes.BRAND_SECONDARY),
        "thinking": ("🧠", themes.BRAND_WARNING),
        "working": ("⚡", themes.BRAND_INFO),
        "building": ("🔨", themes.BRAND_ACCENT),
        "testing": ("🧪", themes.BRAND_SUCCESS),
        "success": ("✨", themes.BRAND_SUCCESS),
        "error": ("💥", themes.BRAND_ERROR),
        "happy": ("🎉", themes.BRAND_SUCCESS),
        "waving": ("👋", themes.BRAND_PRIMARY),
        "sleeping": ("💤", themes.BRAND_SECONDARY),
    }
    mascot_icon, mascot_color = mood_frames.get(mascot_mood, ("🐨", themes.BRAND_SECONDARY))

    header = Text()
    header.append(f" {mascot_icon} ", style=f"bold {mascot_color}")
    header.append("AiCippy", style=f"bold {themes.BRAND_PRIMARY}")
    header.append(f" v{version}", style="dim")
    header.append(" │ ", style="dim")
    header.append("Enterprise Multi-Agent System", style=f"italic {themes.BRAND_SECONDARY}")

    if working_dir:
        # Shorten path for display: ~/project or last 2 segments
        from pathlib import Path

        path = Path(working_dir)
        home = Path.home()
        try:
            display_path = f"~/{path.relative_to(home)}"
        except ValueError:
            display_path = str(path)
        # Truncate if too long
        if len(display_path) > 40:
            display_path = "..." + display_path[-37:]
        header.append(" │ ", style="dim")
        header.append(f"📁 {display_path}", style=f"{themes.BRAND_ACCENT}")

    return Panel(
        header,
        border_style=themes.BRAND_PRIMARY,
        box=ROUNDED,
        padding=(0, 1),
    )


def create_compact_status_widget(
    agents: Sequence[AgentWidget],
    session_model: str = "odin",
    tokens_used: int = 0,
    connected: bool = True,
    mascot_mood: str = "idle",
    reconnecting: bool = False,
    latency_ms: float = 0.0,
) -> Panel:
    """Create a compact status widget for top-right corner.

    Args:
        agents: List of agent widgets.
        session_model: Current model name.
        tokens_used: Total tokens used.
        connected: Connection status.
        mascot_mood: Current mascot mood.
        reconnecting: Whether currently reconnecting.
        latency_ms: Current latency in milliseconds.

    Returns:
        Compact status panel.

    """
    content = Text()

    # Connection indicator
    if reconnecting:
        content.append("◐", style=f"bold {themes.BRAND_WARNING}")
        content.append(" Reconnecting...", style=themes.BRAND_WARNING)
    elif connected:
        content.append("●", style=themes.BRAND_SUCCESS)
        if latency_ms > 0:
            if latency_ms <= _LATENCY_GOOD:
                latency_color = themes.BRAND_SUCCESS
            elif latency_ms <= _LATENCY_FAIR:
                latency_color = themes.BRAND_WARNING
            else:
                latency_color = themes.BRAND_ERROR
            content.append(f" ({latency_ms:.0f}ms)", style=latency_color)
    else:
        content.append("○", style=themes.BRAND_ERROR)
    content.append(" ", style="dim")

    # Model
    content.append(f"{session_model.upper()}", style=f"bold {themes.BRAND_PRIMARY}")
    content.append(" │ ", style="dim")

    # Tokens
    content.append(f"{tokens_used:,}", style=themes.BRAND_WARNING)
    content.append(" tok", style="dim")
    content.append(" │ ", style="dim")

    # Agents count
    active_agents = sum(1 for a in agents if a.status in ("running", "thinking"))
    content.append(f"⚡{active_agents}/{len(agents)}", style=themes.BRAND_INFO)

    # Mood indicator
    mood_icons = {"idle": "○", "thinking": "◐", "working": "●", "happy": "★"}
    mood_icon = mood_icons.get(mascot_mood, "○")
    content.append(f" {mood_icon}", style=themes.BRAND_ACCENT)

    return Panel(
        content,
        border_style=themes.BRAND_SECONDARY,
        box=ROUNDED,
        padding=(0, 1),
        title="[dim]Status[/dim]",
        title_align="left",
    )


def create_live_preview_panel(
    buffer: LivePreviewBuffer,
    title: str = "Live Preview",
) -> Panel:
    """Create the live preview panel with scrolling content.

    Args:
        buffer: The preview buffer.
        title: Panel title.

    Returns:
        Preview panel.

    """
    return Panel(
        buffer.render(),
        title=f"[bold {themes.BRAND_INFO}]{title}[/bold {themes.BRAND_INFO}]",
        title_align="left",
        border_style=themes.BRAND_INFO,
        box=ROUNDED,
        padding=(0, 1),
        height=PREVIEW_LINES + 2,  # +2 for border
    )


def create_input_section(mode: str = "agent", model: str = "odin") -> Text:
    """Create the input prompt section with mode indicator and send symbol.

    The input area sits at the top of the footer zone. It supports multiline
    expansion — the prompt_toolkit TextArea grows vertically as the user types
    multiple lines. The send symbol (⏎) is shown in the prompt prefix.

    Args:
        mode: Current mode (agent, edit, research, code, ultrathinking).
        model: Current model (odin, arjuna).

    Returns:
        Input prompt text with mode indicator and send symbol.

    """
    prompt = Text()

    mode_colors = {
        "agent": themes.BRAND_INFO,
        "edit": themes.BRAND_WARNING,
        "research": themes.BRAND_ACCENT,
        "code": themes.BRAND_SUCCESS,
        "ultrathinking": themes.BRAND_PRIMARY,
    }
    mode_color = mode_colors.get(mode, themes.BRAND_PRIMARY)

    prompt.append(" ◈ ", style=f"bold {themes.BRAND_ACCENT}")
    prompt.append(f"{mode}", style=f"bold {mode_color}")
    prompt.append(" · ", style="dim")
    prompt.append(f"{model.upper()}", style=f"{themes.BRAND_SUCCESS}")
    prompt.append("  ⏎ ", style=f"dim {themes.BRAND_ACCENT}")
    prompt.append("\n")
    prompt.append(" ▸ ", style=f"bold {themes.BRAND_PRIMARY}")

    return prompt


def create_footer_status_panel(
    tokens_used: int = 0,
    username: str | None = None,
    session_duration: str = "00:00:00",
    model: str = "odin",
    connected: bool = True,
    background_tasks: int = 0,
) -> Panel:
    """Create the combined 2-layer footer panel (status line + keyboard shortcuts).

    Layer 1: Connection, tokens, user, timer, model, background tasks
    Layer 2: Keyboard shortcuts

    Args:
        tokens_used: Total tokens used in session.
        username: Logged in username.
        session_duration: Session duration string (HH:MM:SS).
        model: Current model name.
        connected: Connection status.
        background_tasks: Number of background tasks running.

    Returns:
        Combined footer panel.

    """
    # Layer 1: Status information
    status = Text()

    conn_icon = "●" if connected else "○"
    conn_color = themes.BRAND_SUCCESS if connected else themes.BRAND_ERROR
    status.append(f" {conn_icon}", style=conn_color)
    status.append(" Tokens: ", style="dim")
    status.append(f"{tokens_used:,}", style=f"bold {themes.BRAND_WARNING}")
    status.append(" │ ", style="dim")

    status.append("User: ", style="dim")
    if username:
        status.append(username, style=f"bold {themes.BRAND_INFO}")
    else:
        status.append("—", style="dim italic")
    status.append(" │ ", style="dim")

    status.append(session_duration, style=f"bold {themes.BRAND_ACCENT}")
    status.append(" │ ", style="dim")

    status.append(model.upper(), style=f"bold {themes.BRAND_PRIMARY}")

    if background_tasks > 0:
        status.append(" │ ", style="dim")
        status.append(f"⚡{background_tasks} bg", style=f"bold {themes.BRAND_INFO}")

    # Layer 2: Keyboard shortcuts
    shortcuts = Text()
    shortcut_items = [
        ("Ctrl+C", "Cancel"),
        ("Ctrl+L", "Clear"),
        ("↑↓", "History"),
        ("Tab", "Complete"),
        ("/help", "Cmds"),
        ("quit", "Exit"),
    ]
    shortcuts.append(" ", style="dim")
    for i, (key, desc) in enumerate(shortcut_items):
        if i > 0:
            shortcuts.append(" │ ", style="dim")
        shortcuts.append(key, style=themes.BRAND_ACCENT)
        shortcuts.append(f" {desc}", style="dim")

    # Combine both layers
    from rich.console import Group as RichGroup

    combined = RichGroup(status, shortcuts)

    return Panel(
        combined,
        border_style=themes.BRAND_PRIMARY,
        box=ROUNDED,
        padding=(0, 1),
    )


class InteractiveLayout:
    """Interactive layout manager with enriched enterprise-grade design.

    New layout structure:
    ┌─────────────────────────────────────────────────────────────┐
    │  🐨 ZooZoo [live] │ AiCippy v2.5.1 │ 📁 ~/project/path   │  HEADER
    ├─────────────────────────────────────────────────────────────┤
    │                                                              │
    │  LIVE STREAMING AREA (auto-scroll)                           │  MAIN
    │  Agent responses stream here in real-time                    │
    │  Code blocks syntax-highlighted                              │
    │                                                              │
    ├─────────────────────────────────────────────────────────────┤
    │  ◈ agent · ODIN                                              │  FOOTER L1
    │  > [multiline expandable input]                     [⏎ Send]│  (user input)
    ├─────────────────────────────────────────────────────────────┤
    │  ● Tokens: 1,234 │ User: name │ 00:12:34 │ ODIN │ ⚡2/10  │  FOOTER L2
    │  Ctrl+C Cancel │ Ctrl+L Clear │ ↑↓ History │ /help │ quit  │  (status+keys)
    └─────────────────────────────────────────────────────────────┘
    """

    def __init__(self, console: Console, version: str = "1.0.0") -> None:
        """Initialize interactive layout."""
        self.console = console
        self.version = version
        self.preview_buffer = LivePreviewBuffer(max_lines=PREVIEW_LINES)
        self._layout = Layout()
        self._current_layout_mode: str = ""
        self.background_tasks: int = 0
        self.working_dir: str = ""

    def _setup_layout(self, mode: str) -> None:
        """Set up the layout structure for the given mode.

        Args:
            mode: One of 'full', 'compact', or 'minimal'.

        """
        if mode == self._current_layout_mode:
            return
        self._current_layout_mode = mode

        self._layout = Layout()

        if mode == "minimal":
            self._layout.split_column(
                Layout(name="main_area", ratio=1),
                Layout(name="footer_status", size=5),
            )
        elif mode == "compact":
            self._layout.split_column(
                Layout(name="header", size=3),
                Layout(name="main_area", ratio=1),
                Layout(name="footer_status", size=5),
            )
        else:
            # Full: header + main(preview + sidebar) + footer(2-layer)
            self._layout.split_column(
                Layout(name="header", size=3),
                Layout(name="main_area", ratio=1),
                Layout(name="footer_status", size=5),
            )
            self._layout["main_area"].split_row(
                Layout(name="preview", ratio=3),
                Layout(name="status_widget", size=35),
            )

    def update(
        self,
        agents: Sequence[AgentWidget] | None = None,
        session_model: str = "odin",
        tokens_used: int = 0,
        connected: bool = True,
        reconnecting: bool = False,
        latency_ms: float = 0.0,
        mascot_mood: str = "idle",
        username: str | None = None,
        session_duration: str = "00:00:00",
    ) -> Layout:
        """Update the layout with current state."""
        mode = get_layout_mode()
        self._setup_layout(mode)

        if mode == "minimal":
            self._layout["main_area"].update(self.preview_buffer.render())
            self._layout["footer_status"].update(
                create_footer_status_panel(
                    tokens_used=tokens_used,
                    username=username,
                    session_duration=session_duration,
                    model=session_model,
                    connected=connected,
                    background_tasks=self.background_tasks,
                ),
            )
            return self._layout

        # Header with ZooZoo animation, app name, and working directory
        self._layout["header"].update(
            create_compact_header(
                version=self.version,
                mascot_mood=mascot_mood,
                working_dir=self.working_dir,
            ),
        )

        if mode == "compact":
            self._layout["main_area"].update(create_live_preview_panel(self.preview_buffer))
        else:
            self._layout["preview"].update(create_live_preview_panel(self.preview_buffer))
            self._layout["status_widget"].update(
                create_compact_status_widget(
                    agents=agents or [],
                    session_model=session_model,
                    tokens_used=tokens_used,
                    connected=connected,
                    mascot_mood=mascot_mood,
                    reconnecting=reconnecting,
                    latency_ms=latency_ms,
                ),
            )

        # Footer layer 2: combined status bar + shortcuts
        self._layout["footer_status"].update(
            create_footer_status_panel(
                tokens_used=tokens_used,
                username=username,
                session_duration=session_duration,
                model=session_model,
                connected=connected,
                background_tasks=self.background_tasks,
            ),
        )

        return self._layout

    def add_preview_line(self, content: str, style: str = "white", line_type: str = "info") -> None:
        """Add a line to the preview buffer."""
        self.preview_buffer.add_line(content, style, line_type)

    def add_command(self, command: str) -> None:
        """Add a command to preview."""
        self.preview_buffer.add_command(command)

    def add_output(self, output: str) -> None:
        """Add output to preview."""
        self.preview_buffer.add_output(output)

    def add_error(self, error: str) -> None:
        """Add error to preview."""
        self.preview_buffer.add_error(error)

    def add_success(self, message: str) -> None:
        """Add success message to preview."""
        self.preview_buffer.add_success(message)

    def add_agent_update(self, agent_id: str, status: str, message: str = "") -> None:
        """Add agent update to preview."""
        self.preview_buffer.add_agent_update(agent_id, status, message)

    def add_streaming(self, chunk: str) -> None:
        """Add streaming content to preview."""
        self.preview_buffer.add_streaming(chunk)

    def clear_preview(self) -> None:
        """Clear the preview buffer."""
        self.preview_buffer.clear()

    @property
    def layout(self) -> Layout:
        """Get the layout."""
        return self._layout


# ============================================================================
# Real-time Streaming UI Components
# ============================================================================


@dataclass
class StreamingState:
    """State for streaming response display."""

    content: str = ""
    tokens_received: int = 0
    tokens_per_second: float = 0.0
    is_streaming: bool = False
    cursor_visible: bool = True


def create_streaming_response_panel(
    state: StreamingState,
    agent_type: str | None = None,
) -> Panel:
    """Create a panel for streaming AI response with typewriter effect.

    Args:
        state: Current streaming state.
        agent_type: Type of agent responding.

    Returns:
        Rich Panel with streaming content.

    """
    # Header with live indicator
    header_parts = []
    if state.is_streaming:
        header_parts.append(f"[bold {themes.BRAND_INFO}]Streaming[/bold {themes.BRAND_INFO}]")
        header_parts.append(f"[{themes.BRAND_ACCENT}]...[/{themes.BRAND_ACCENT}]")
    else:
        header_parts.append(f"[{themes.BRAND_SUCCESS}]Complete[/{themes.BRAND_SUCCESS}]")

    if agent_type:
        bi = themes.BRAND_INFO
        header_parts.append(f"[dim]|[/dim] [{bi}]{agent_type.upper()}[/{bi}]")

    bw = themes.BRAND_WARNING
    header_parts.append(f"[dim]|[/dim] [{bw}]{state.tokens_received:,} tokens[/{bw}]")

    if state.tokens_per_second > 0:
        header_parts.append(f"[dim]({state.tokens_per_second:.1f} tok/s)[/dim]")

    title = " ".join(header_parts)

    # Content with optional cursor
    content = Text()
    content.append(state.content)

    if state.is_streaming and state.cursor_visible:
        # Use bold underline instead of blink for better terminal compatibility
        content.append("_", style=f"bold {themes.BRAND_ACCENT}")

    border_style = themes.BRAND_INFO if state.is_streaming else themes.BRAND_SUCCESS

    return Panel(
        Markdown(str(content)) if not state.is_streaming else content,
        title=title,
        title_align="left",
        border_style=border_style,
        box=ROUNDED,
        padding=(1, 2),
    )


def create_connection_indicator(
    connected: bool,
    latency_ms: float = 0.0,
    reconnecting: bool = False,
    reconnect_attempts: int = 0,
) -> Text:
    """Create a connection status indicator.

    Args:
        connected: Whether connected.
        latency_ms: Current latency in milliseconds.
        reconnecting: Whether currently reconnecting.
        reconnect_attempts: Number of reconnection attempts.

    Returns:
        Rich Text with connection indicator.

    """
    text = Text()

    if reconnecting:
        # Removed blink style for better terminal compatibility
        text.append("◐ ", style=f"bold {themes.BRAND_WARNING}")
        text.append("Reconnecting", style=themes.BRAND_WARNING)
        if reconnect_attempts > 0:
            text.append(f" ({reconnect_attempts})", style="dim")
    elif connected:
        # Determine latency color
        if latency_ms <= _LATENCY_GOOD:
            latency_color = themes.BRAND_SUCCESS
        elif latency_ms <= _LATENCY_FAIR:
            latency_color = themes.BRAND_WARNING
        else:
            latency_color = themes.BRAND_ERROR

        text.append("● ", style=f"bold {themes.BRAND_SUCCESS}")
        text.append("Connected", style=themes.BRAND_SUCCESS)
        if latency_ms > 0:
            text.append(f" ({latency_ms:.0f}ms)", style=latency_color)
    else:
        text.append("○ ", style=f"bold {themes.BRAND_ERROR}")
        text.append("Disconnected", style=themes.BRAND_ERROR)

    return text


def create_agent_activity_panel(
    agents: Sequence[AgentWidget],
    show_mini_progress: bool = True,
) -> Panel:
    """Create a panel showing agent activity with smooth transitions.

    Args:
        agents: List of agent widgets.
        show_mini_progress: Whether to show mini progress bars.

    Returns:
        Rich Panel with agent activity.

    """
    content_parts: list[RenderableType] = []

    # Header
    active_count = sum(1 for a in agents if a.status in ("running", "thinking"))
    header = Text()
    header.append("  AGENTS ", style=f"bold {themes.BRAND_INFO}")
    header.append(f"({active_count}/{len(agents)} active)", style="dim")
    content_parts.append(header)
    content_parts.append(Text())

    if not agents:
        content_parts.append(Text("    No agents spawned", style="dim italic"))
    else:
        for agent in agents:
            line = Text()

            # Status icon with animation hint
            icon = STATUS_ICONS.get(agent.status, "○")
            color = {
                "running": themes.BRAND_INFO,
                "thinking": themes.BRAND_WARNING,
                "complete": themes.BRAND_SUCCESS,
                "error": themes.BRAND_ERROR,
                "idle": "dim",
            }.get(agent.status, "white")

            # Add activity animation for running/thinking (no blink for compatibility)
            if agent.status in ("running", "thinking"):
                line.append(f"  {icon} ", style=f"bold {color}")
            else:
                line.append(f"  {icon} ", style=color)

            # Agent name
            line.append(f"{agent.name[:15]:<15}", style="white")

            # Progress bar if enabled
            if show_mini_progress:
                bar_width = 10
                completed = int(agent.progress / 100 * bar_width)
                bar_filled = "█" * completed
                bar_empty = "░" * (bar_width - completed)
                line.append(f" {bar_filled}", style=color)
                line.append(bar_empty, style="dim")
                line.append(f" {agent.progress:>3}%", style="dim")

            # Message if present
            if agent.message:
                line.append(f" {agent.message[:20]}", style="dim italic")

            content_parts.append(line)

    return Panel(
        Group(*content_parts),
        border_style=themes.BRAND_INFO,
        box=ROUNDED,
        padding=(0, 1),
        title="[dim]Activity[/dim]",
        title_align="left",
    )


def create_progress_indicator(
    message: str,
    progress: int = 0,
    show_percentage: bool = True,
    style: str = themes.BRAND_INFO,
) -> Text:
    """Create a progress indicator with animated spinner.

    Args:
        message: Progress message.
        progress: Progress percentage (0-100).
        show_percentage: Whether to show percentage.
        style: Color style.

    Returns:
        Rich Text with progress indicator.

    """
    text = Text()

    # Spinner frames
    spinners = ("◐", "◓", "◑", "◒")

    frame_idx = int(time.time() * 4) % len(spinners)
    spinner = spinners[frame_idx]

    text.append(f"{spinner} ", style=f"bold {style}")
    text.append(message, style=style)

    if show_percentage and progress > 0:
        text.append(f" ({progress}%)", style="dim")

    return text


def create_tool_output_panel(
    tool_name: str,
    output: str,
    success: bool,
    execution_time_ms: int = 0,
) -> Panel:
    """Create a panel for tool execution output.

    Args:
        tool_name: Name of the tool.
        output: Tool output.
        success: Whether execution was successful.
        execution_time_ms: Execution time in milliseconds.

    Returns:
        Rich Panel with tool output.

    """
    # Header
    status_icon = "✓" if success else "✗"
    status_color = themes.BRAND_SUCCESS if success else themes.BRAND_ERROR

    title_parts = [
        f"[{status_color}]{status_icon}[/{status_color}]",
        f"[bold {themes.BRAND_ACCENT}]{tool_name}[/bold {themes.BRAND_ACCENT}]",
    ]
    if execution_time_ms > 0:
        title_parts.append(f"[dim]({execution_time_ms}ms)[/dim]")

    title = " ".join(title_parts)

    # Content
    content = Text()
    for line in output.split("\n")[:_MAX_TOOL_OUTPUT_LINES]:  # Limit to 20 lines
        content.append(f"  {line}\n", style="white")

    if output.count("\n") > _MAX_TOOL_OUTPUT_LINES:
        content.append(f"  ... ({output.count(chr(10)) - _MAX_TOOL_OUTPUT_LINES} more lines)", style="dim")

    return Panel(
        content,
        title=title,
        title_align="left",
        border_style=status_color,
        box=ROUNDED,
        padding=(0, 1),
    )


class EnhancedLivePreviewBuffer(LivePreviewBuffer):
    """Enhanced live preview buffer with streaming support.

    Adds:
    - Token-by-token streaming display
    - Agent activity integration
    - Connection status display
    - Smooth scrolling
    """

    def __init__(self, max_lines: int = PREVIEW_LINES) -> None:
        """Initialize enhanced preview buffer."""
        super().__init__(max_lines)
        self._streaming_content: str = ""
        self._is_streaming: bool = False

    def start_streaming(self) -> None:
        """Start streaming mode."""
        self._is_streaming = True
        self._streaming_content = ""
        self.add_line(
            "◐ Receiving response...",
            style=themes.BRAND_INFO,
            line_type="streaming_start",
        )

    def stream_token(self, token: str) -> None:
        """Add streaming token."""
        self._streaming_content += token

        # Update the last streaming line or add new one
        if self._lines and self._lines[-1].line_type == "streaming":
            if len(self._streaming_content) > _MAX_STREAMING_CONTENT_LEN:
                self._lines[-1].content = f"  ...{self._streaming_content[-_STREAM_TRUNC_LEN:]}"
            else:
                self._lines[-1].content = f"  {self._streaming_content}"
        else:
            self.add_line(f"  {token}", style=themes.BRAND_ACCENT, line_type="streaming")

    def end_streaming(self, total_tokens: int = 0, duration_seconds: float = 0.0) -> None:
        """End streaming mode."""
        self._is_streaming = False

        # Replace streaming indicator with completion
        if self._lines and self._lines[-1].line_type in (
            "streaming",
            "streaming_start",
        ):
            stats = []
            if total_tokens > 0:
                stats.append(f"{total_tokens:,} tokens")
            if duration_seconds > 0:
                tps = total_tokens / duration_seconds if duration_seconds > 0 else 0
                stats.append(f"{tps:.1f} tok/s")
                stats.append(f"{duration_seconds:.1f}s")

            stats_str = " | ".join(stats) if stats else ""
            self._lines[-1] = PreviewLine(
                content=f"✓ Response complete{' (' + stats_str + ')' if stats_str else ''}",
                style=themes.BRAND_SUCCESS,
                timestamp=self._lines[-1].timestamp,
                line_type="success",
            )

        self._streaming_content = ""

    def add_agent_activity(
        self,
        agent_id: str,
        status: str,
        progress: int = 0,
        message: str = "",
    ) -> None:
        """Add agent activity update."""
        icon = STATUS_ICONS.get(status, "○")
        color = {
            "running": themes.BRAND_INFO,
            "thinking": themes.BRAND_WARNING,
            "complete": themes.BRAND_SUCCESS,
            "error": themes.BRAND_ERROR,
        }.get(status, "dim")

        progress_bar = ""
        if progress > 0:
            bar_width = 10
            filled = int(progress / 100 * bar_width)
            progress_bar = f" [{'█' * filled}{'░' * (bar_width - filled)}] {progress}%"

        text = f"{icon} {agent_id}: {status}{progress_bar}"
        if message:
            text += f" - {message}"

        self.add_line(text, style=color, line_type="agent")

    def add_connection_status(
        self,
        connected: bool,
        latency_ms: float = 0.0,
        reconnecting: bool = False,
    ) -> None:
        """Add connection status update."""
        if reconnecting:
            self.add_line("◐ Reconnecting...", style=themes.BRAND_WARNING, line_type="connection")
        elif connected:
            latency_str = f" ({latency_ms:.0f}ms)" if latency_ms > 0 else ""
            self.add_line(
                f"● Connected{latency_str}",
                style=themes.BRAND_SUCCESS,
                line_type="connection",
            )
        else:
            self.add_line("○ Disconnected", style=themes.BRAND_ERROR, line_type="connection")

    def render_enhanced(self, show_timestamps: bool = True) -> Text:
        """Render enhanced buffer with formatting."""
        text = Text()
        visible = self.get_visible_lines()

        # Pad with empty lines if needed
        empty_lines = self.max_lines - len(visible)
        for _ in range(empty_lines):
            text.append("\n")

        for i, line in enumerate(visible):
            # Type-specific prefix
            type_prefix = {
                "command": "$ ",
                "output": "  ",
                "error": "✗ ",
                "success": "✓ ",
                "agent": "⚡ ",
                "streaming": "◐ ",
                "streaming_start": "◐ ",
                "connection": "● ",
                "info": "  ",
            }.get(line.line_type, "  ")

            # Timestamp
            if show_timestamps and line.timestamp:
                text.append(f"[{line.timestamp}] ", style="dim")

            # Type prefix
            type_color = {
                "command": themes.BRAND_INFO,
                "error": themes.BRAND_ERROR,
                "success": themes.BRAND_SUCCESS,
                "agent": themes.BRAND_ACCENT,
                "streaming": themes.BRAND_WARNING,
                "streaming_start": themes.BRAND_WARNING,
                "connection": themes.BRAND_INFO,
            }.get(line.line_type, "dim")

            if line.line_type != "output":
                text.append(type_prefix, style=type_color)

            # Content
            text.append(line.content, style=line.style)

            if i < len(visible) - 1:
                text.append("\n")

        # Add streaming cursor if active (no blink for compatibility)
        if self._is_streaming:
            text.append("_", style=f"bold {themes.BRAND_ACCENT}")

        return text
