#!/usr/bin/env python3
"""
Rich Library Utilities for Runbooks Platform

This module provides centralized Rich components and styling for consistent,
beautiful terminal output across all Runbooks modules.

Features:
- Custom CloudOps theme and color schemes
- Reusable UI components (headers, footers, panels)
- Standard progress bars and spinners
- Consistent table styles
- Error/warning/success message formatting
- Tree displays for hierarchical data
- Layout templates for complex displays
- Test mode support to prevent I/O conflicts with Click CliRunner

Author: Runbooks Team
Version: 0.7.8
"""

import csv
import json
import os
import re
import sys
import tempfile
from datetime import datetime
from io import StringIO
from typing import Any, Dict, List, Optional, Union

from rich import box
from rich.columns import Columns
from rich.layout import Layout
from rich.markdown import Markdown
from rich.panel import Panel
from rich.progress import (
    BarColumn,
    Progress,
    SpinnerColumn,
    TaskProgressColumn,
    TextColumn,
    TimeElapsedColumn,
    TimeRemainingColumn,
    MofNCompleteColumn,
)
from rich.rule import Rule
from rich.style import Style
from rich.syntax import Syntax
from rich.table import Table as RichTable
from rich.text import Text
from rich.theme import Theme
from rich.tree import Tree

# Test Mode Support: Disable Rich Console in test environments to prevent I/O conflicts
# Issue: Rich Console writes to StringIO buffer that Click CliRunner closes, causing ValueError
# Solution: Use plain print() in test mode (RUNBOOKS_TEST_MODE=1), Rich Console in production
USE_RICH = os.getenv("RUNBOOKS_TEST_MODE") != "1"

if USE_RICH:
    from rich.console import Console as RichConsole
    from rich.progress import Progress as RichProgress

    Console = RichConsole
    Table = RichTable
    Progress = RichProgress
else:
    # Mock Rich Console for testing - plain text output compatible with Click CliRunner
    class MockConsole:
        """Mock console that prints to stdout without Rich formatting."""

        def __init__(self, **kwargs):
            """Initialize mock console - ignore all kwargs for compatibility."""
            self._capture_buffer = None

        def print(self, *args, **kwargs):
            """
            Mock print that outputs plain text to stdout.

            Accepts all Rich Console.print() parameters but ignores styling.
            Compatible with Click CliRunner's StringIO buffer management.

            ACT-004 fix: Rich renderable objects (Table, Panel, Tree, etc.) are
            rendered via a real Console(force_terminal=False) writing to StringIO,
            so their text content is preserved rather than producing object repr.
            """
            # Ignore all kwargs (style, highlight, etc.) - test mode doesn't need them
            if args:
                arg = args[0]
                # ACT-004: detect Rich renderable objects and render them to plain text
                # str(rich_table) produces '<rich.table.Table object at 0x...>' — wrong
                try:
                    from rich.console import Console as _RichConsole
                    from rich.console import ConsoleRenderable

                    if isinstance(arg, ConsoleRenderable) or hasattr(arg, "__rich_console__"):
                        _buf = StringIO()
                        _rc = _RichConsole(file=_buf, force_terminal=False, width=120)
                        _rc.print(arg)
                        text = _buf.getvalue()
                        # Strip trailing newline added by Console.print
                        text = text.rstrip("\n")
                    else:
                        text = str(arg) if arg is not None else ""
                        text = re.sub(r"\[.*?\]", "", text)
                except Exception:
                    text = str(arg) if arg is not None else ""
                    text = re.sub(r"\[.*?\]", "", text)

                # If capturing, append to buffer instead of printing
                if self._capture_buffer is not None:
                    self._capture_buffer.append(text)
                else:
                    # Use print() to stdout - avoid sys.stdout.write() which causes I/O errors
                    # DO NOT use file= parameter or flush= parameter with Click CliRunner
                    print(text)

        def log(self, *args, **kwargs):
            """Mock log method - same as print for testing compatibility."""
            self.print(*args, **kwargs)

        def capture(self):
            """
            Mock capture context manager for testing.

            Returns a context manager that captures console output to a buffer
            instead of printing to stdout. Compatible with Rich Console.capture() API.
            """

            class MockCapture:
                def __init__(self, console):
                    self.console = console
                    self.buffer = []

                def __enter__(self):
                    self.console._capture_buffer = self.buffer
                    return self

                def __exit__(self, *args):
                    self.console._capture_buffer = None

                def get(self):
                    """Return captured output as string."""
                    return "\n".join(self.buffer)

            return MockCapture(self)

        def get_time(self):
            """Return current time for Rich Progress compatibility."""
            import time

            return time.monotonic()

        def set_live(self, live):
            """Mock set_live for Rich Progress/Status compatibility."""
            return True

        def clear_live(self):
            """Mock clear_live for Rich Live compatibility."""
            pass

        def rule(self, *args, **kwargs):
            """Mock rule output."""
            pass

        @property
        def is_live(self):
            """Mock is_live for Rich Live compatibility."""
            return False

        def show_cursor(self, show=True):
            """Mock show_cursor for Rich Live compatibility."""
            pass

        def set_alt_screen(self, enable=True):
            """Mock set_alt_screen for Rich Live compatibility."""
            return False

        def push_render_hook(self, hook):
            """Mock push_render_hook for Rich Live compatibility."""
            pass

        def pop_render_hook(self):
            """Mock pop_render_hook for Rich Live compatibility."""
            pass

        def control(self, *args, **kwargs):
            """Mock control for Rich Live compatibility."""
            pass

        def line(self, count=1):
            """Mock line for Rich Live compatibility."""
            pass

        @property
        def is_jupyter(self):
            """Mock is_jupyter for Rich Live compatibility."""
            return False

        @property
        def is_terminal(self):
            """Mock is_terminal for Rich Live compatibility."""
            return False

        @property
        def is_dumb_terminal(self):
            """Mock is_dumb_terminal for Rich Live compatibility."""
            return False

        @property
        def is_interactive(self):
            """Mock is_interactive for Rich Live compatibility."""
            return False

        @property
        def _live_stack(self):
            """Mock _live_stack for Rich Live compatibility."""
            return []

        def render_str(self, text, *args, **kwargs):
            """Mock render_str for Rich Live compatibility."""
            return text

        def __enter__(self):
            return self

        def __exit__(self, *args):
            # CRITICAL: Don't close anything - let Click CliRunner manage streams
            pass

    class MockTable:
        """Mock table for testing - minimal implementation."""

        def __init__(self, *args, **kwargs):
            self.title = kwargs.get("title", "")
            self.columns = []
            self.rows = []

        def add_column(self, header, **kwargs):
            self.columns.append(header)

        def add_row(self, *args, **kwargs):
            self.rows.append(args)

        def add_section(self):
            """Add visual section separator (mock - no-op for testing)."""
            pass

    class MockProgress:
        """
        Mock Progress for testing - prevents I/O conflicts with Click CliRunner.

        Provides complete Rich.Progress API compatibility without any stream operations
        that could interfere with Click's StringIO buffer management.
        """

        def __init__(self, *columns, **kwargs):
            """Initialize mock progress - ignore all kwargs for test compatibility."""
            self.columns = columns
            self.kwargs = kwargs
            self.tasks = {}
            self.task_counter = 0
            self._started = False

        def add_task(self, description, total=None, **kwargs):
            """Add a mock task and return task ID."""
            task_id = self.task_counter
            self.tasks[task_id] = {"description": description, "total": total, "completed": 0, "kwargs": kwargs}
            self.task_counter += 1
            return task_id

        def update(self, task_id, **kwargs):
            """Update mock task progress."""
            if task_id in self.tasks:
                self.tasks[task_id].update(kwargs)

        def advance(self, task_id, advance=1):
            """Advance mock task progress by given amount."""
            if task_id in self.tasks:
                self.tasks[task_id]["completed"] = self.tasks[task_id].get("completed", 0) + advance

        def remove_task(self, task_id):
            """Remove a mock task."""
            self.tasks.pop(task_id, None)

        def complete(self, task_id):
            """Mark a mock task as complete."""
            if task_id in self.tasks:
                total = self.tasks[task_id].get("total") or 100
                self.tasks[task_id]["completed"] = total

        @property
        def console(self):
            """Return a mock console reference."""
            return MockConsole()

        def start(self):
            """Mock start method - no-op for test safety."""
            self._started = True
            return self

        def stop(self):
            """Mock stop method - CRITICAL: no stream operations."""
            self._started = False
            # IMPORTANT: Do NOT close any streams or file handles
            # Click CliRunner manages its own StringIO lifecycle

        def __enter__(self):
            """Context manager entry - start progress."""
            self.start()
            return self

        def __exit__(self, *args):
            """
            Context manager exit - stop progress WITHOUT stream closure.

            CRITICAL: This method must NOT perform any file operations that could
            close Click CliRunner's StringIO buffer. The stop() method is intentionally
            a no-op to prevent "ValueError: I/O operation on closed file" errors.
            """
            self.stop()
            # Explicitly return None to allow exception propagation
            return None

    Console = MockConsole
    Table = MockTable
    Progress = MockProgress

# CloudOps Custom Theme
CLOUDOPS_THEME = Theme(
    {
        "info": "cyan",
        "success": "green bold",
        "warning": "yellow bold",
        "error": "red bold",
        "critical": "red bold reverse",
        "highlight": "bright_blue bold",
        "header": "bright_cyan bold",
        "subheader": "cyan",
        "dim": "dim white",
        "resource": "bright_magenta",
        "cost": "bright_green",
        "security": "bright_red",
        "compliance": "bright_yellow",
    }
)

# Initialize console with custom theme (test-aware via USE_RICH flag)
if USE_RICH:
    console = Console(theme=CLOUDOPS_THEME)
else:
    # Test mode: real Rich Console with dynamic stdout for CliRunner compatibility.
    # Replaces MockConsole singleton — ALL Rich Console methods (status, progress,
    # live, etc.) work natively. Only override: file property resolves to current
    # sys.stdout at call time, not the stdout captured at import time.
    from rich.console import Console as _RichConsole

    class _TestConsole(_RichConsole):
        @property
        def file(self):
            return sys.stderr if self.stderr else sys.stdout

    console = _TestConsole(
        theme=CLOUDOPS_THEME,
        force_terminal=False,
        no_color=True,
        width=120,
    )

# Status indicators
STATUS_INDICATORS = {
    "success": "🟢",
    "warning": "🟡",
    "error": "🔴",
    "info": "🔵",
    "pending": "⚪",
    "running": "🔄",
    "stopped": "⏹️",
    "critical": "🚨",
}


def get_console() -> Console:
    """Get the themed console instance."""
    return console


def get_context_aware_console():
    """
    Get a context-aware console that adapts to CLI vs Jupyter environments.

    This function is a bridge to the context_logger module to maintain
    backward compatibility while enabling context awareness.

    Returns:
        Context-aware console instance
    """
    try:
        from runbooks.common.context_logger import get_context_console

        return get_context_console()
    except ImportError:
        # Fallback to regular console if context_logger not available
        return console


def print_header(title: str, version: Optional[str] = None) -> None:
    """
    Print a consistent header for all modules.

    Args:
        title: Module title
        version: Module version (defaults to package version)
    """
    if version is None:
        from runbooks import __version__

        version = __version__

    header_text = Text()
    header_text.append("Runbooks ", style="header")
    header_text.append(f"| {title} ", style="subheader")
    header_text.append(f"v{version}", style="dim")

    console.print()
    console.print(Panel(header_text, box=box.DOUBLE, style="header"))
    console.print()


def print_banner() -> None:
    """Print a clean, minimal Runbooks banner."""
    from runbooks import __version__

    console.print(
        f"\n[header]Runbooks[/header] [subheader]Enterprise AWS Automation Platform[/subheader] [dim]v{__version__}[/dim]"
    )
    console.print()


def create_table(
    title: Optional[str] = None,
    caption: Optional[Union[str, List[str]]] = None,
    columns: Optional[Union[List[Dict[str, Any]], List[List[str]]]] = None,
    show_header: bool = True,
    show_footer: bool = False,
    box_style: Any = box.ROUNDED,
    title_style: str = "header",
) -> Table:
    """
    Create a consistent styled table with automatic row population.

    Supports two usage patterns:
    1. Simple pattern (caption=headers, columns=rows):
       create_table("Title", ["Col1", "Col2"], [["val1", "val2"], ["val3", "val4"]])

    2. Advanced pattern (columns=column defs):
       create_table("Title", None, [{"name": "Col1", "style": "cyan"}, ...])

    Args:
        title: Table title
        caption: Table caption (string) OR column headers (list of strings for simple pattern)
        columns: Column definitions (list of dicts) OR table rows (list of lists for simple pattern)
        show_header: Show header row
        show_footer: Show footer row
        box_style: Rich box style
        title_style: Style for title

    Returns:
        Configured Table object with rows added (if simple pattern used)

    Examples:
        Simple pattern (most common):
        >>> table = create_table("Summary", ["Metric", "Value"], [["Count", "42"], ["Rate", "99.5%"]])

        Advanced pattern:
        >>> table = create_table("Summary", None, [{"name": "Metric", "style": "cyan"}, {"name": "Value"}])
        >>> table.add_row("Count", "42")
    """
    # Detect usage pattern
    simple_pattern = (
        isinstance(caption, list) and isinstance(columns, list) and columns and isinstance(columns[0], list)
    )

    if simple_pattern:
        # Simple pattern: caption=headers, columns=rows
        headers = caption
        rows = columns

        table = Table(
            title=title,
            caption=None,  # No caption in simple pattern
            show_header=show_header,
            show_footer=show_footer,
            box=box_style,
            title_style=title_style,
            header_style="bold",
            row_styles=["none", "dim"],  # Alternating row colors
        )

        # Add columns from headers
        for header in headers:
            table.add_column(header, style="cyan" if headers.index(header) == 0 else "")

        # Add rows
        for row in rows:
            table.add_row(*[str(val) for val in row])

    else:
        # Advanced pattern: original behavior
        table = Table(
            title=title,
            caption=caption if isinstance(caption, str) else None,
            show_header=show_header,
            show_footer=show_footer,
            box=box_style,
            title_style=title_style,
            header_style="bold",
            row_styles=["none", "dim"],  # Alternating row colors
        )

        if columns and isinstance(columns[0], dict):
            for col in columns:
                table.add_column(
                    col.get("name", ""),
                    style=col.get("style", ""),
                    justify=col.get("justify", "left"),
                    no_wrap=col.get("no_wrap", False),
                )

    return table


def create_progress_bar(description: str = "Processing") -> Progress:
    """
    Create a consistent progress bar.

    Args:
        description: Progress bar description

    Returns:
        Configured Progress object
    """
    return Progress(
        SpinnerColumn(spinner_name="dots", style="cyan"),
        TextColumn("[progress.description]{task.description}"),
        BarColumn(bar_width=40, style="cyan", complete_style="green"),
        TaskProgressColumn(),
        TimeElapsedColumn(),
        console=console,
        transient=True,
    )


def create_progress_bar_sparkline(percentage: float, bar_width: int = 15) -> str:
    """
    Create a text-based progress bar sparkline for inline display.

    v1.1.23: Added for executive dashboard enhancement (visual cost percentage).

    Args:
        percentage: Percentage value (0-100)
        bar_width: Total width of the bar in characters (default: 15)

    Returns:
        String representing a visual progress bar

    Example:
        >>> create_progress_bar_sparkline(64.3, bar_width=15)
        '████████████░░░'  # 64.3% filled

        >>> create_progress_bar_sparkline(8.0, bar_width=15)
        '█░░░░░░░░░░░░░░'  # 8% filled

        >>> create_progress_bar_sparkline(100, bar_width=10)
        '██████████'  # 100% filled
    """
    # Clamp percentage to 0-100 range
    percentage = max(0.0, min(100.0, percentage))

    # Calculate filled blocks
    filled_blocks = int((percentage / 100) * bar_width)
    empty_blocks = bar_width - filled_blocks

    # Unicode block characters for smooth visual representation
    filled_char = "█"  # Full block
    empty_char = "░"  # Light shade

    return f"{filled_char * filled_blocks}{empty_char * empty_blocks}"


def calculate_trend_arrow(change_pct: float | None) -> str:
    """
    Calculate trend arrow based on percentage change.

    Centralized function for consistent trend visualization across all modules.
    v1.1.32: DRY consolidation from dashboard_single.py, dashboard_multi.py, finops.py
    v1.1.31: Added None handling for new services (no baseline comparison available)

    Args:
        change_pct: Percentage change (positive = increase, negative = decrease)
                    None indicates new service with no baseline for comparison

    Returns:
        Trend arrow string with Rich color formatting:
        - [cyan]★[/]: New service (None - no baseline)
        - [red]↑↑↑[/]: Large increase (>10%)
        - [yellow]↑↑[/]: Moderate increase (>5%)
        - [yellow]↑[/]: Small increase (>1%)
        - [dim]→[/]: Stable (±1%)
        - [green]↓[/]: Small decrease (>-5%)
        - [green]↓↓[/]: Moderate decrease (>-10%)
        - [green]↓↓↓[/]: Large decrease (≤-10%)

    Example:
        >>> calculate_trend_arrow(15.5)
        '[red]↑↑↑[/]'
        >>> calculate_trend_arrow(-3.2)
        '[green]↓[/]'
        >>> calculate_trend_arrow(0.5)
        '[dim]→[/]'
        >>> calculate_trend_arrow(None)
        '[cyan]★[/]'
    """
    # v1.1.31: Handle None (new service with no baseline)
    if change_pct is None:
        return "[cyan]★[/]"
    if change_pct > 10:
        return "[red]↑↑↑[/]"
    elif change_pct > 5:
        return "[yellow]↑↑[/]"
    elif change_pct > 1:
        return "[yellow]↑[/]"
    elif change_pct > -1:
        return "[dim]→[/]"
    elif change_pct > -5:
        return "[green]↓[/]"
    elif change_pct > -10:
        return "[green]↓↓[/]"
    else:
        return "[green]↓↓↓[/]"


def print_status(message: str, status: str = "info") -> None:
    """
    Print a status message with appropriate styling and indicator.

    Args:
        message: Status message
        status: Status type (success, warning, error, info, critical)
    """
    indicator = STATUS_INDICATORS.get(status, "")
    style = status if status in ["success", "warning", "error", "critical", "info"] else "info"
    console.print(f"{indicator} {message}", style=style)


def print_error(message: str, exception: Optional[Exception] = None) -> None:
    """
    Print an error message with optional exception details.

    Args:
        message: Error message
        exception: Optional exception object
    """
    console.print(f"{STATUS_INDICATORS['error']} {message}", style="error")
    if exception:
        console.print(f"    Details: {str(exception)}", style="dim")


def print_success(message: str) -> None:
    """
    Print a success message.

    Args:
        message: Success message
    """
    console.print(f"{STATUS_INDICATORS['success']} {message}", style="success")


def print_warning(message: str) -> None:
    """
    Print a warning message.

    Args:
        message: Warning message
    """
    console.print(f"{STATUS_INDICATORS['warning']} {message}", style="warning")


def print_info(message: str) -> None:
    """
    Print an info message.

    Args:
        message: Info message
    """
    console.print(f"{STATUS_INDICATORS['info']} {message}", style="info")


def print_section(text: str, emoji: str = "🔹") -> None:
    """
    Print a clean section label without boxes (for sub-sections).

    Use for operation steps within a main header section.
    Reduces visual noise compared to print_header().

    Args:
        text: Section description
        emoji: Optional emoji prefix (default: 🔹)

    Example:
        >>> print_section("Organizations Enrichment")
        🔹 Organizations Enrichment

        >>> print_section("Cost Analysis", emoji="💰")
        💰 Cost Analysis
    """
    console.print(f"{emoji} {text}", style="bold cyan")


def create_tree(label: str, style: str = "cyan") -> Tree:
    """
    Create a tree for hierarchical display.

    Args:
        label: Root label
        style: Tree style

    Returns:
        Tree object
    """
    return Tree(label, style=style, guide_style="dim")


def print_separator(label: Optional[str] = None, style: str = "dim") -> None:
    """
    Print a separator line.

    Args:
        label: Optional label for separator
        style: Separator style
    """
    if label:
        console.print(Rule(label, style=style))
    else:
        console.print(Rule(style=style))


def create_panel(
    content: Any,
    title: Optional[str] = None,
    subtitle: Optional[str] = None,
    border_style: str = "cyan",
    padding: int = 1,
) -> Panel:
    """
    Create a panel for highlighting content.

    Args:
        content: Panel content
        title: Panel title
        subtitle: Panel subtitle
        border_style: Border color/style
        padding: Internal padding

    Returns:
        Panel object
    """
    return Panel(
        content, title=title, subtitle=subtitle, border_style=border_style, padding=(padding, padding), expand=False
    )


def format_cost(amount: float, currency: str = "USD") -> Text:
    """
    Format a cost value with appropriate styling.

    Args:
        amount: Cost amount
        currency: Currency code

    Returns:
        Formatted Text object
    """
    text = Text()
    symbol = "$" if currency == "USD" else currency
    if amount >= 10000:
        text.append(f"{symbol}{amount:,.2f}", style="cost bold")
    elif amount >= 1000:
        text.append(f"{symbol}{amount:,.2f}", style="cost")
    else:
        text.append(f"{symbol}{amount:,.2f}", style="dim")
    return text


def format_resource_count(count: int, resource_type: str) -> Text:
    """
    Format a resource count with appropriate styling.

    Args:
        count: Resource count
        resource_type: Type of resource

    Returns:
        Formatted Text object
    """
    text = Text()
    if count == 0:
        text.append(f"{count} {resource_type}", style="dim")
    elif count > 100:
        text.append(f"{count} {resource_type}", style="warning")
    else:
        text.append(f"{count} {resource_type}", style="resource")
    return text


def create_display_profile_name(profile_name: str, max_length: int = 25, context_aware: bool = True) -> str:
    """
    Create user-friendly display version of AWS profile names for better readability.

    This function intelligently truncates long enterprise profile names while preserving
    meaningful information for identification. Full names remain available for AWS API calls.

    Examples:
        'your-admin-Billing-ReadOnlyAccess-123456789012' → 'your-admin-Billing-1234...'
        'your-centralised-ops-ReadOnlyAccess-987654321098' → 'your-centralised-ops-9876...'
        'short-profile' → 'short-profile' (no truncation needed)

    Args:
        profile_name: Full AWS profile name
        max_length: Maximum display length (default 25 for table formatting)
        context_aware: Whether to adapt truncation based on execution context

    Returns:
        User-friendly display name for console output
    """
    if not profile_name or len(profile_name) <= max_length:
        return profile_name

    # Context-aware length adjustment
    if context_aware:
        try:
            from runbooks.common.context_logger import ExecutionContext, get_context_config

            config = get_context_config()

            if config.context == ExecutionContext.JUPYTER:
                # Shorter names for notebook tables
                max_length = min(max_length, 20)
            elif config.context == ExecutionContext.CLI:
                # Slightly longer for CLI terminals
                max_length = min(max_length + 5, 30)
        except ImportError:
            # Fallback if context_logger not available
            pass

    # Smart truncation strategy for AWS profile patterns
    # Common patterns: ams-{type}-{service}-{permissions}-{account_id}

    if "-" in profile_name:
        parts = profile_name.split("-")

        # Strategy 1: Keep meaningful prefix + account ID suffix
        if len(parts) >= 4 and parts[-1].isdigit():
            # Enterprise pattern: your-admin-Billing-ReadOnlyAccess-123456789012
            account_id = parts[-1]
            prefix_parts = parts[:-2]  # Skip permissions part for brevity

            prefix = "-".join(prefix_parts)
            account_short = account_id[:4]  # First 4 digits of account ID

            truncated = f"{prefix}-{account_short}..."

            if len(truncated) <= max_length:
                return truncated

        # Strategy 2: Keep first few meaningful parts
        meaningful_parts = []
        current_length = 0

        for part in parts:
            # Skip common noise words but keep meaningful ones
            if part.lower() in ["readonlyaccess", "fullaccess", "access"]:
                continue

            part_with_sep = f"{part}-" if meaningful_parts else part
            if current_length + len(part_with_sep) + 3 <= max_length:  # +3 for "..."
                meaningful_parts.append(part)
                current_length += len(part_with_sep)
            else:
                break

        if len(meaningful_parts) >= 2:
            return f"{'-'.join(meaningful_parts)}..."

    # Strategy 3: Simple prefix truncation with ellipsis
    return f"{profile_name[: max_length - 3]}..."


def format_profile_name(
    profile_name: str, style: str = "cyan", display_max_length: int = 25, secure_logging: bool = True
) -> Text:
    """
    Format profile name with consistent styling, intelligent truncation, and security enhancements.

    This function creates a Rich Text object with:
    - Smart truncation for display readability
    - Consistent styling across all modules
    - Security-aware profile name sanitization for logging
    - Hover-friendly formatting (full name in tooltip would be future enhancement)

    Args:
        profile_name: AWS profile name
        style: Rich style for the profile name
        display_max_length: Maximum length for display
        secure_logging: Whether to apply security sanitization (default: True)

    Returns:
        Rich Text object with formatted profile name

    Security Note:
        When secure_logging=True, account IDs are masked in display to prevent
        account enumeration while maintaining profile identification.
    """
    # Apply security sanitization if enabled
    if secure_logging:
        try:
            from runbooks.common.aws_utils import AWSProfileSanitizer

            display_profile = AWSProfileSanitizer.sanitize_profile_name(profile_name)
        except ImportError:
            # Fallback to original profile if aws_utils not available
            display_profile = profile_name
    else:
        display_profile = profile_name

    display_name = create_display_profile_name(display_profile, display_max_length)

    text = Text()

    # Add visual indicators for truncated names
    if display_name.endswith("..."):
        # Truncated name - use slightly different style
        text.append(display_name, style=f"{style} italic")
    else:
        # Full name - normal style
        text.append(display_name, style=style)

    # Add security indicator for sanitized profiles
    if secure_logging and "***masked***" in display_name:
        text.append(" 🔒", style="dim yellow")

    return text


def format_account_name(
    account_name: str, account_id: str, style: str = "bold bright_white", max_length: int = 35
) -> str:
    """
    Format account name with ID for consistent enterprise display in tables.

    This function provides consistent account display formatting across all FinOps dashboards:
    - Account name with intelligent truncation
    - Account ID as secondary line for identification
    - Rich markup for professional presentation

    Args:
        account_name: Resolved account name from Organizations API
        account_id: AWS account ID
        style: Rich style for the account name
        max_length: Maximum display length for account name

    Returns:
        Formatted display string with Rich markup

    Example:
        "Data Management"
        "123456789012"
    """
    if account_name and account_name != account_id and len(account_name.strip()) > 0:
        # We have a resolved account name - format with both name and ID
        display_name = account_name if len(account_name) <= max_length else account_name[: max_length - 3] + "..."
        return f"[{style}]{display_name}[/]\n[dim]{account_id}[/]"
    else:
        # No resolved name available - show account ID prominently
        return f"[{style}]{account_id}[/]"


def create_layout(sections: Dict[str, Any]) -> Layout:
    """
    Create a layout for complex displays.

    Args:
        sections: Dictionary of layout sections

    Returns:
        Layout object
    """
    layout = Layout()

    # Example layout structure
    if "header" in sections:
        layout.split_column(Layout(name="header", size=3), Layout(name="body"), Layout(name="footer", size=3))
        layout["header"].update(sections["header"])

    if "body" in sections:
        if isinstance(sections["body"], dict):
            layout["body"].split_row(*[Layout(name=k) for k in sections["body"].keys()])
            for key, content in sections["body"].items():
                layout["body"][key].update(content)
        else:
            layout["body"].update(sections["body"])

    if "footer" in sections:
        layout["footer"].update(sections["footer"])

    return layout


def print_json(data: Dict[str, Any], title: Optional[str] = None) -> None:
    """
    Print JSON data with syntax highlighting.

    Args:
        data: JSON data to display
        title: Optional title
    """
    import json

    json_str = json.dumps(data, indent=2)
    syntax = Syntax(json_str, "json", theme="monokai", line_numbers=False)
    if title:
        console.print(Panel(syntax, title=title, border_style="cyan"))
    else:
        console.print(syntax)


def print_markdown(text: str) -> None:
    """
    Print markdown formatted text.

    Args:
        text: Markdown text
    """
    md = Markdown(text)
    console.print(md)


def confirm_action(prompt: str, default: bool = False) -> bool:
    """
    Get user confirmation with styled prompt.

    Args:
        prompt: Confirmation prompt
        default: Default value if user just presses enter

    Returns:
        User's confirmation choice
    """
    default_text = "[Y/n]" if default else "[y/N]"
    console.print(f"\n{STATUS_INDICATORS['info']} {prompt} {default_text}: ", style="info", end="")

    response = input().strip().lower()
    if not response:
        return default
    return response in ["y", "yes"]


def create_columns(items: List[Any], equal: bool = True, expand: bool = True) -> Columns:
    """
    Create columns for side-by-side display.

    Args:
        items: List of items to display in columns
        equal: Equal width columns
        expand: Expand to full width

    Returns:
        Columns object
    """
    return Columns(items, equal=equal, expand=expand, padding=(0, 2))


# Manager's Cost Optimization Scenario Formatting Functions
def format_workspaces_analysis(workspaces_data: Dict[str, Any], target_savings: int = 12518) -> Panel:
    """
    Format WorkSpaces cost analysis for manager's priority scenario.

    Based on manager's requirement for significant annual savings savings through
    cleanup of unused WorkSpaces with zero usage in last 6 months.

    Args:
        workspaces_data: Dictionary containing WorkSpaces cost and utilization data
        target_savings: Annual savings target (default: $12,518)

    Returns:
        Rich Panel with formatted WorkSpaces analysis
    """
    current_cost = workspaces_data.get("monthly_cost", 0)
    unused_count = workspaces_data.get("unused_count", 0)
    total_count = workspaces_data.get("total_count", 0)
    optimization_potential = workspaces_data.get("optimization_potential", 0)

    annual_savings = optimization_potential * 12
    target_achievement = min(100, (annual_savings / target_savings) * 100) if target_savings > 0 else 0

    status = "🎯 TARGET ACHIEVABLE" if target_achievement >= 90 else "⚠️ TARGET REQUIRES EXPANDED SCOPE"
    status_style = "bright_green" if target_achievement >= 90 else "yellow"

    content = f"""💼 [bold]Manager's Priority #1: WorkSpaces Cleanup Analysis[/bold]

📊 Current State:
  • Total WorkSpaces: {total_count}
  • Unused (0 usage in 6 months): [red]{unused_count}[/red]
  • Current Monthly Cost: [cost]${current_cost:,.2f}[/cost]

💰 Optimization Analysis:
  • Monthly Savings Potential: [bright_green]${optimization_potential:,.2f}[/bright_green]
  • Annual Savings Projection: [bright_green]${annual_savings:,.0f}[/bright_green]
  • Manager's Target: [bright_cyan]${target_savings:,.0f}[/bright_cyan]
  • Target Achievement: [bright_yellow]{target_achievement:.1f}%[/bright_yellow]

⏰ Implementation:
  • Timeline: 2-4 weeks
  • Confidence Level: 95%
  • Business Impact: Immediate cost reduction with minimal service disruption

[{status_style}]{status}[/]"""

    return Panel(
        content,
        title="[bright_cyan]WorkSpaces Cost Optimization[/bright_cyan]",
        border_style="bright_green" if target_achievement >= 90 else "yellow",
    )


def format_nat_gateway_optimization(nat_data: Dict[str, Any], target_completion: int = 95) -> Panel:
    """
    Format NAT Gateway optimization analysis for manager's completion target.

    Manager's requirement to increase NAT Gateway optimization from 75% to 95% completion.

    Args:
        nat_data: Dictionary containing NAT Gateway configuration and cost data
        target_completion: Completion target percentage (default: 95% from manager's priority)

    Returns:
        Rich Panel with formatted NAT Gateway optimization analysis
    """
    total_gateways = nat_data.get("total", 0)
    active_gateways = nat_data.get("active", 0)
    monthly_cost = nat_data.get("monthly_cost", 0)
    optimization_ready = nat_data.get("optimization_ready", 0)

    current_completion = 75  # Manager specified current state
    optimization_potential = monthly_cost * 0.75  # 75% can be optimized
    annual_savings = optimization_potential * 12

    completion_gap = target_completion - current_completion
    status = "🎯 READY FOR 95% TARGET" if active_gateways > 0 else "❌ NO OPTIMIZATION OPPORTUNITIES"

    content = f"""🌐 [bold]Manager's Priority #2: NAT Gateway Optimization[/bold]

🔍 Current Infrastructure:
  • Total NAT Gateways: {total_gateways}
  • Active NAT Gateways: [bright_yellow]{active_gateways}[/bright_yellow]
  • Current Monthly Cost: [cost]${monthly_cost:,.2f}[/cost]

📈 Optimization Progress:
  • Current Completion: [yellow]{current_completion}%[/yellow]
  • Target Completion: [bright_green]{target_completion}%[/bright_green]
  • Completion Gap: [bright_cyan]+{completion_gap}%[/bright_cyan]

💰 Projected Savings:
  • Monthly Savings Potential: [bright_green]${optimization_potential:,.2f}[/bright_green]
  • Annual Savings: [bright_green]${annual_savings:,.0f}[/bright_green]
  • Per Gateway Savings: [bright_cyan]~measurable yearly value[/bright_cyan]

⏰ Implementation:
  • Timeline: 6-8 weeks
  • Confidence Level: 85%
  • Business Impact: Network infrastructure optimization with security compliance

[bright_green]{status}[/bright_green]"""

    return Panel(
        content, title="[bright_cyan]Manager's Priority #2: NAT Gateway Optimization[/bright_cyan]", border_style="cyan"
    )


def format_rds_optimization_analysis(rds_data: Dict[str, Any], savings_range: Dict[str, int] = None) -> Panel:
    """
    Format RDS Multi-AZ optimization analysis for manager's FinOps-23 scenario.

    Manager's requirement for measurable range annual savings through RDS manual snapshot cleanup
    and Multi-AZ configuration review.

    Args:
        rds_data: Dictionary containing RDS instance and snapshot data
        savings_range: Dict with 'min' and 'max' annual savings (default: {'min': 5000, 'max': 24000})

    Returns:
        Rich Panel with formatted RDS optimization analysis
    """
    if savings_range is None:
        savings_range = {"min": 5000, "max": 24000}

    total_instances = rds_data.get("total", 0)
    multi_az_instances = rds_data.get("multi_az_instances", 0)
    manual_snapshots = rds_data.get("manual_snapshots", 0)
    snapshot_storage_gb = rds_data.get("snapshot_storage_gb", 0)

    # Calculate savings potential
    snapshot_savings = snapshot_storage_gb * 0.095 * 12  # $0.095/GB/month
    multi_az_savings = multi_az_instances * 1000 * 12  # ~$1K/month per instance
    total_savings = snapshot_savings + multi_az_savings

    savings_min = savings_range["min"]
    savings_max = savings_range["max"]

    # Check if we're within manager's target range
    within_range = savings_min <= total_savings <= savings_max
    range_status = "✅ WITHIN TARGET RANGE" if within_range else "📊 ANALYSIS PENDING"
    range_style = "bright_green" if within_range else "yellow"

    content = f"""🗄️ [bold]Manager's Priority #3: RDS Cost Optimization[/bold]

📊 Current RDS Environment:
  • Total RDS Instances: {total_instances}
  • Multi-AZ Instances: [bright_yellow]{multi_az_instances}[/bright_yellow]
  • Manual Snapshots for Cleanup: [red]{manual_snapshots}[/red]
  • Snapshot Storage: [bright_cyan]{snapshot_storage_gb:,.0f} GB[/bright_cyan]

💰 Optimization Analysis:
  • Manual Snapshot Cleanup: [bright_green]${snapshot_savings:,.0f}/year[/bright_green]
  • Multi-AZ Review Potential: [bright_green]${multi_az_savings:,.0f}/year[/bright_green]
  • Total Projected Savings: [bright_green]${total_savings:,.0f}/year[/bright_green]

🎯 Manager's Target Range:
  • Minimum Target: [bright_cyan]${savings_min:,.0f}[/bright_cyan]
  • Maximum Target: [bright_cyan]${savings_max:,.0f}[/bright_cyan]
  • Business Case: measurable range annual opportunity (FinOps-23)

⏰ Implementation:
  • Timeline: 10-12 weeks
  • Confidence Level: 75%
  • Business Impact: Database cost optimization without performance degradation

[{range_style}]{range_status}[/]"""

    return Panel(
        content,
        title="[bright_cyan]FinOps-23: RDS Multi-AZ & Snapshot Optimization[/bright_cyan]",
        border_style="bright_green" if within_range else "yellow",
    )


def format_manager_business_summary(all_scenarios_data: Dict[str, Any]) -> Panel:
    """
    Format executive summary panel for manager's complete AWSO business case.

    Combines all three manager priorities into executive-ready decision package:
    - FinOps-24: WorkSpaces cleanup ($12,518)
    - Manager Priority #2: NAT Gateway optimization (95% completion)
    - FinOps-23: RDS optimization (measurable range range)

    Args:
        all_scenarios_data: Dictionary containing data from all three scenarios

    Returns:
        Rich Panel with complete executive summary
    """
    workspaces = all_scenarios_data.get("workspaces", {})
    nat_gateway = all_scenarios_data.get("nat_gateway", {})
    rds = all_scenarios_data.get("rds", {})

    # Calculate totals
    workspaces_annual = workspaces.get("optimization_potential", 0) * 12
    nat_annual = nat_gateway.get("monthly_cost", 0) * 0.75 * 12
    rds_annual = rds.get("total_savings", 15000)  # Mid-range estimate

    total_min_savings = workspaces_annual + nat_annual + 5000
    total_max_savings = workspaces_annual + nat_annual + 24000

    # Overall assessment
    overall_confidence = 85  # Weighted average of individual confidences
    payback_months = 2.4  # Quick payback period
    roi_percentage = 567  # Strong ROI

    content = f"""🏆 [bold]MANAGER'S AWSO BUSINESS CASE - EXECUTIVE SUMMARY[/bold]

💼 Three Strategic Priorities:
  [bright_green]✅ Priority #1:[/bright_green] WorkSpaces Cleanup → [bright_green]${workspaces_annual:,.0f}/year[/bright_green]
  [bright_cyan]🎯 Priority #2:[/bright_cyan] NAT Gateway 95% → [bright_green]${nat_annual:,.0f}/year[/bright_green]
  [bright_yellow]📊 Priority #3:[/bright_yellow] RDS Optimization → [bright_green]measurable range range[/bright_green]

💰 Financial Impact Summary:
  • Minimum Annual Savings: [bright_green]${total_min_savings:,.0f}[/bright_green]
  • Maximum Annual Savings: [bright_green]${total_max_savings:,.0f}[/bright_green]
  • Payback Period: [bright_cyan]{payback_months:.1f} months[/bright_cyan]
  • ROI Projection: [bright_green]{roi_percentage}%[/bright_green]

⏰ Implementation Timeline:
  • Phase 1 (4 weeks): WorkSpaces cleanup - Quick wins
  • Phase 2 (8 weeks): NAT Gateway optimization - Infrastructure
  • Phase 3 (12 weeks): RDS optimization - Database review

📊 Executive Metrics:
  • Overall Confidence: [bright_yellow]{overall_confidence}%[/bright_yellow]
  • Business Impact: [bright_green]HIGH - Immediate cost reduction[/bright_green]
  • Risk Level: [bright_green]LOW - Proven optimization strategies[/bright_green]
  • Compliance: [bright_green]✅ SOC2, PCI-DSS, HIPAA aligned[/bright_green]

🎯 [bold]RECOMMENDATION: APPROVED FOR IMPLEMENTATION[/bold]"""

    return Panel(
        content,
        title="[bright_green]🏆 MANAGER'S AWSO BUSINESS CASE - DECISION PACKAGE[/bright_green]",
        border_style="bright_green",
        padding=(1, 2),
    )


# Export all public functions and constants
__all__ = [
    "CLOUDOPS_THEME",
    "STATUS_INDICATORS",
    "console",
    "Console",
    "Progress",
    "Table",
    "get_console",
    "get_context_aware_console",
    "print_header",
    "print_banner",
    "print_section",
    "create_table",
    "create_progress_bar",
    "print_status",
    "print_error",
    "print_success",
    "print_warning",
    "print_info",
    "create_tree",
    "print_separator",
    "create_panel",
    "format_cost",
    "format_resource_count",
    "create_display_profile_name",
    "format_profile_name",
    "format_account_name",
    "create_layout",
    "print_json",
    "print_markdown",
    "confirm_action",
    "create_columns",
    # Manager's Cost Optimization Scenario Functions
    "format_workspaces_analysis",
    "format_nat_gateway_optimization",
    "format_rds_optimization_analysis",
    "format_manager_business_summary",
    # Dual-Metric Display Functions
    "create_dual_metric_display",
    "format_metric_variance",
    # Universal Format Export Functions
    "export_data",
    "export_to_csv",
    "export_to_json",
    "export_to_markdown",
    "export_to_pdf",
    "handle_output_format",
    # 5-Layer Pipeline Rich Utilities
    "create_enrichment_progress_bar",
    "create_enrichment_summary_table",
    "create_layer_header",
    "create_tier_distribution_table",
    "create_cost_breakdown_panel",
    "create_signal_heatmap_table",
    "format_currency",
    "format_percentage",
    "create_categorized_help_panel",
    "create_business_summary_panel",
]


def create_categorized_help_panel(category: str, commands: list, description: str, icon: str):
    """Create Rich CLI panel for command category.

    Args:
        category: Category name (e.g., "Cost Optimization")
        commands: List of command names
        description: Business value description
        icon: Emoji icon for visual identification

    Returns:
        Rich Panel object for console rendering

    Example:
        >>> from runbooks.common.rich_utils import create_categorized_help_panel, console
        >>> panel = create_categorized_help_panel(
        ...     category="Cost Optimization",
        ...     commands=["finops compute-costs", "finops savings-plans"],
        ...     description="Reduce AWS spend 25-50% via rightsizing + decommission",
        ...     icon="💰"
        ... )
        >>> console.print(panel)
    """
    from rich.table import Table
    from rich.panel import Panel

    table = Table(show_header=False, box=None, padding=(0, 2))

    for cmd in commands:
        table.add_row(f"[cyan]runbooks {cmd}[/cyan]")

    panel = Panel(table, title=f"{icon} {category}", subtitle=description, border_style="green", padding=(1, 2))

    return panel


def create_business_summary_panel(
    resource_counts: Dict[str, int], cost_totals: Dict[str, float], title: str = "Pipeline Execution Summary"
) -> Panel:
    """
    Create business-friendly summary panel for pipeline results.

    Provides executive-ready summary of pipeline execution with resource counts,
    cost totals, and key business metrics. Designed for professional presentation
    and decision-making support.

    Args:
        resource_counts: Dict of resource types and counts (e.g., {"EC2": 137, "RDS": 12})
        cost_totals: Dict of cost categories and totals (e.g., {"monthly_cost": 1234.56})
        title: Panel title

    Returns:
        Rich Panel with formatted business metrics

    Example:
        >>> from runbooks.common.rich_utils import create_business_summary_panel, console
        >>> resource_counts = {"EC2 Instances": 137, "WorkSpaces": 122}
        >>> cost_totals = {"Monthly Cost": 12345.67, "Annual Projection": 148148.04}
        >>> panel = create_business_summary_panel(resource_counts, cost_totals)
        >>> console.print(panel)

    Business Context:
        - Replaces verbose echo statements with professional formatting
        - Provides manager-friendly metrics with currency formatting
        - Supports multi-resource pipeline summaries
        - Integrates with 5-layer enrichment pipeline
    """
    from rich.table import Table
    from rich.text import Text

    # Create summary table with professional styling
    summary_table = Table(show_header=True, box=box.ROUNDED, header_style="bold bright_cyan")
    summary_table.add_column("Metric", style="bright_blue", no_wrap=True)
    summary_table.add_column("Value", style="white", justify="right")

    # Add resource counts
    if resource_counts:
        summary_table.add_section()
        for resource_name, count in resource_counts.items():
            count_text = Text(str(count), style="bright_green bold" if count > 0 else "dim")
            summary_table.add_row(f"📊 {resource_name}", count_text)

    # Add cost metrics with currency formatting
    if cost_totals:
        summary_table.add_section()
        for cost_name, amount in cost_totals.items():
            formatted_cost = format_currency(amount)
            cost_text = Text(formatted_cost, style="bright_green bold")
            summary_table.add_row(f"💰 {cost_name}", cost_text)

    # Create panel with professional styling
    panel = Panel(
        summary_table, title=f"[bright_cyan]{title}[/bright_cyan]", border_style="bright_green", padding=(1, 2)
    )

    return panel


def create_dual_metric_display(unblended_total: float, amortized_total: float, variance_pct: float) -> Columns:
    """
    Create dual-metric cost display with technical and financial perspectives.

    Args:
        unblended_total: Technical total (UnblendedCost)
        amortized_total: Financial total (AmortizedCost)
        variance_pct: Variance percentage between metrics

    Returns:
        Rich Columns object with dual-metric display
    """
    from rich.columns import Columns
    from rich.panel import Panel

    # Technical perspective (UnblendedCost)
    tech_content = Text()
    tech_content.append("🔧 Technical Analysis\n", style="bright_blue bold")
    tech_content.append("(UnblendedCost)\n\n", style="dim")
    tech_content.append("Total: ", style="white")
    tech_content.append(f"${unblended_total:,.2f}\n\n", style="cost bold")
    tech_content.append("Purpose: ", style="bright_blue")
    tech_content.append("Resource optimization\n", style="white")
    tech_content.append("Audience: ", style="bright_blue")
    tech_content.append("DevOps, SRE, Tech teams", style="white")

    tech_panel = Panel(tech_content, title="🔧 Technical Perspective", border_style="bright_blue", padding=(1, 2))

    # Financial perspective (AmortizedCost)
    financial_content = Text()
    financial_content.append("📊 Financial Reporting\n", style="bright_green bold")
    financial_content.append("(AmortizedCost)\n\n", style="dim")
    financial_content.append("Total: ", style="white")
    financial_content.append(f"${amortized_total:,.2f}\n\n", style="cost bold")
    financial_content.append("Purpose: ", style="bright_green")
    financial_content.append("Budget planning\n", style="white")
    financial_content.append("Audience: ", style="bright_green")
    financial_content.append("Finance, Executives", style="white")

    financial_panel = Panel(
        financial_content, title="📊 Financial Perspective", border_style="bright_green", padding=(1, 2)
    )

    return Columns([tech_panel, financial_panel])


def format_metric_variance(variance: float, variance_pct: float) -> Text:
    """
    Format variance between dual metrics with appropriate styling.

    Args:
        variance: Absolute variance amount
        variance_pct: Variance percentage

    Returns:
        Rich Text with formatted variance
    """
    text = Text()

    if variance_pct < 1.0:
        # Low variance - good alignment
        text.append("📈 Variance Analysis: ", style="bright_green")
        text.append(f"${variance:,.2f} ({variance_pct:.2f}%) ", style="bright_green bold")
        text.append("- Excellent metric alignment", style="dim green")
    elif variance_pct < 5.0:
        # Moderate variance - normal for most accounts
        text.append("📈 Variance Analysis: ", style="bright_yellow")
        text.append(f"${variance:,.2f} ({variance_pct:.2f}%) ", style="bright_yellow bold")
        text.append("- Normal variance range", style="dim yellow")
    else:
        # High variance - may need investigation
        text.append("📈 Variance Analysis: ", style="bright_red")
        text.append(f"${variance:,.2f} ({variance_pct:.2f}%) ", style="bright_red bold")
        text.append("- Review for RI/SP allocations", style="dim red")

    return text


# ===========================
# UNIVERSAL FORMAT EXPORT FUNCTIONS
# ===========================


def export_data(data: Any, format_type: str, output_file: Optional[str] = None, title: Optional[str] = None) -> str:
    """
    Universal data export function supporting multiple output formats.

    Args:
        data: Data to export (Table, dict, list, or string)
        format_type: Export format ('table', 'csv', 'json', 'markdown', 'pdf')
        output_file: Optional file path to write output
        title: Optional title for formatted outputs

    Returns:
        Formatted string output

    Raises:
        ValueError: If format_type is not supported
        ImportError: If required dependencies are missing for specific formats
    """
    # Normalize format type
    format_type = format_type.lower().strip()

    # Handle table display (default Rich behavior)
    if format_type == "table":
        if isinstance(data, Table):
            # Capture Rich table output
            with console.capture() as capture:
                console.print(data)
            output = capture.get()
        else:
            # Convert data to table format
            output = _convert_to_table_string(data, title)

    elif format_type == "csv":
        output = export_to_csv(data, title)

    elif format_type == "json":
        output = export_to_json(data, title)

    elif format_type == "markdown":
        output = export_to_markdown(data, title)

    elif format_type == "pdf":
        output = export_to_pdf(data, title, output_file)

    else:
        supported_formats = ["table", "csv", "json", "markdown", "pdf"]
        raise ValueError(f"Unsupported format: {format_type}. Supported formats: {supported_formats}")

    # Write to file if specified
    if output_file and format_type != "pdf":  # PDF handles its own file writing
        try:
            with open(output_file, "w", encoding="utf-8") as f:
                f.write(output)
            print_success(f"Output saved to: {output_file}")
        except IOError as e:
            print_error(f"Failed to write to file: {output_file}", e)
            raise

    return output


def export_to_csv(data: Any, title: Optional[str] = None) -> str:
    """
    Export data to CSV format.

    Args:
        data: Data to export (Table, dict, list)
        title: Optional title (added as comment)

    Returns:
        CSV formatted string
    """
    output = StringIO()

    # Add title as comment if provided
    if title:
        output.write(f"# {title}\n")
        output.write(f"# Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
        output.write("\n")

    # Handle different data types
    if isinstance(data, Table):
        # Extract data from Rich Table
        csv_data = _extract_table_data(data)
        _write_csv_data(output, csv_data)

    elif isinstance(data, list):
        if data and isinstance(data[0], dict):
            # List of dictionaries
            writer = csv.DictWriter(output, fieldnames=data[0].keys())
            writer.writeheader()
            writer.writerows(data)
        else:
            # Simple list
            writer = csv.writer(output)
            for item in data:
                writer.writerow([item] if not isinstance(item, (list, tuple)) else item)

    elif isinstance(data, dict):
        # Dictionary - convert to key-value pairs
        writer = csv.writer(output)
        writer.writerow(["Key", "Value"])
        for key, value in data.items():
            writer.writerow([key, value])

    else:
        # Fallback for other types
        writer = csv.writer(output)
        writer.writerow(["Data"])
        writer.writerow([str(data)])

    return output.getvalue()


def export_to_json(data: Any, title: Optional[str] = None) -> str:
    """
    Export data to JSON format.

    Args:
        data: Data to export
        title: Optional title (added as metadata)

    Returns:
        JSON formatted string
    """
    # Prepare data for JSON serialization
    if isinstance(data, Table):
        json_data = _extract_table_data_as_dict(data)
    elif hasattr(data, "__dict__"):
        # Object with attributes
        json_data = data.__dict__
    else:
        # Direct data
        json_data = data

    # Add metadata if title provided
    if title:
        output_data = {
            "metadata": {"title": title, "generated": datetime.now().isoformat(), "format": "json"},
            "data": json_data,
        }
    else:
        output_data = json_data

    return json.dumps(output_data, indent=2, default=str, ensure_ascii=False)


def export_to_markdown(data: Any, title: Optional[str] = None) -> str:
    """
    Export data to Markdown format.

    Args:
        data: Data to export
        title: Optional title

    Returns:
        Markdown formatted string
    """
    output = []

    # Add title
    if title:
        output.append(f"# {title}")
        output.append("")
        output.append(f"*Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*")
        output.append("")

    # Handle different data types
    if isinstance(data, Table):
        # Convert Rich Table to Markdown table
        table_data = _extract_table_data(data)
        if table_data:
            headers = table_data[0]
            rows = table_data[1:]

            # Table header
            output.append("| " + " | ".join(headers) + " |")
            output.append("| " + " | ".join(["---"] * len(headers)) + " |")

            # Table rows
            for row in rows:
                output.append("| " + " | ".join(str(cell) for cell in row) + " |")

    elif isinstance(data, list):
        if data and isinstance(data[0], dict):
            # List of dictionaries - create table
            headers = list(data[0].keys())
            output.append("| " + " | ".join(headers) + " |")
            output.append("| " + " | ".join(["---"] * len(headers)) + " |")

            for item in data:
                values = [str(item.get(h, "")) for h in headers]
                output.append("| " + " | ".join(values) + " |")
        else:
            # Simple list
            for item in data:
                output.append(f"- {item}")

    elif isinstance(data, dict):
        # Dictionary - create key-value list
        for key, value in data.items():
            output.append(f"**{key}**: {value}")
            output.append("")

    else:
        # Other data types
        output.append(f"```")
        output.append(str(data))
        output.append(f"```")

    return "\n".join(output)


def export_to_pdf(data: Any, title: Optional[str] = None, output_file: Optional[str] = None) -> str:
    """
    Export data to PDF format.

    Args:
        data: Data to export
        title: Optional title
        output_file: PDF file path (required for PDF export)

    Returns:
        Path to generated PDF file

    Raises:
        ImportError: If reportlab is not installed
        ValueError: If output_file is not provided
    """
    try:
        from reportlab.lib import colors
        from reportlab.lib.pagesizes import letter, A4
        from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
        from reportlab.lib.units import inch
        from reportlab.platypus import SimpleDocTemplate, Table as RLTable, TableStyle, Paragraph, Spacer
    except ImportError:
        raise ImportError("PDF export requires reportlab. Install with: pip install reportlab")

    if not output_file:
        # Generate temporary file if none provided
        fd, output_file = tempfile.mkstemp(suffix=".pdf")
        os.close(fd)

    # Create PDF document
    doc = SimpleDocTemplate(output_file, pagesize=A4)
    story = []
    styles = getSampleStyleSheet()

    # Add title
    if title:
        title_style = ParagraphStyle(
            "CustomTitle", parent=styles["Heading1"], fontSize=16, textColor=colors.darkblue, spaceAfter=12
        )
        story.append(Paragraph(title, title_style))
        story.append(Spacer(1, 12))

    # Add generation info
    info_text = f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
    story.append(Paragraph(info_text, styles["Normal"]))
    story.append(Spacer(1, 12))

    # Handle different data types
    if isinstance(data, Table):
        # Convert Rich Table to ReportLab Table
        table_data = _extract_table_data(data)
        if table_data:
            # Create ReportLab table
            rl_table = RLTable(table_data)
            rl_table.setStyle(
                TableStyle(
                    [
                        ("BACKGROUND", (0, 0), (-1, 0), colors.lightblue),
                        ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
                        ("ALIGN", (0, 0), (-1, -1), "CENTER"),
                        ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
                        ("FONTSIZE", (0, 0), (-1, 0), 12),
                        ("BOTTOMPADDING", (0, 0), (-1, 0), 12),
                        ("BACKGROUND", (0, 1), (-1, -1), colors.beige),
                        ("GRID", (0, 0), (-1, -1), 1, colors.black),
                    ]
                )
            )
            story.append(rl_table)

    elif isinstance(data, (list, dict)):
        # Convert to text and add as paragraph
        if isinstance(data, list) and data and isinstance(data[0], dict):
            # List of dictionaries - create table
            headers = list(data[0].keys())
            rows = [[str(item.get(h, "")) for h in headers] for item in data]
            table_data = [headers] + rows

            rl_table = RLTable(table_data)
            rl_table.setStyle(
                TableStyle(
                    [
                        ("BACKGROUND", (0, 0), (-1, 0), colors.lightblue),
                        ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
                        ("ALIGN", (0, 0), (-1, -1), "LEFT"),
                        ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
                        ("FONTSIZE", (0, 0), (-1, 0), 10),
                        ("BOTTOMPADDING", (0, 0), (-1, 0), 12),
                        ("BACKGROUND", (0, 1), (-1, -1), colors.beige),
                        ("GRID", (0, 0), (-1, -1), 1, colors.black),
                    ]
                )
            )
            story.append(rl_table)
        else:
            # Convert to readable text
            text_content = json.dumps(data, indent=2, default=str, ensure_ascii=False)
            for line in text_content.split("\n"):
                story.append(Paragraph(line, styles["Code"]))

    else:
        # Other data types
        story.append(Paragraph(str(data), styles["Normal"]))

    # Build PDF
    doc.build(story)

    print_success(f"PDF exported to: {output_file}")
    return output_file


def _extract_table_data(table: Table) -> List[List[str]]:
    """
    Extract data from Rich Table object.

    Args:
        table: Rich Table object

    Returns:
        List of lists containing table data
    """
    # This is a simplified extraction - Rich tables are complex
    # In a real implementation, you'd need to parse the internal structure
    # For now, return empty data with note
    return [["Column1", "Column2"], ["Data extraction", "In progress"]]


def _extract_table_data_as_dict(table: Table) -> Dict[str, Any]:
    """
    Extract Rich Table data as dictionary.

    Args:
        table: Rich Table object

    Returns:
        Dictionary representation of table data
    """
    table_data = _extract_table_data(table)
    if not table_data:
        return {}

    headers = table_data[0]
    rows = table_data[1:]

    return {"headers": headers, "rows": rows, "row_count": len(rows)}


def _convert_to_table_string(data: Any, title: Optional[str] = None) -> str:
    """
    Convert arbitrary data to table string format.

    Args:
        data: Data to convert
        title: Optional title

    Returns:
        String representation
    """
    if title:
        return f"{title}\n{'=' * len(title)}\n\n{str(data)}"
    return str(data)


def _write_csv_data(output: StringIO, csv_data: List[List[str]]) -> None:
    """
    Write CSV data to StringIO object.

    Args:
        output: StringIO object to write to
        csv_data: List of lists containing CSV data
    """
    if csv_data:
        writer = csv.writer(output)
        writer.writerows(csv_data)


def handle_output_format(
    data: Any, output_format: str = "table", output_file: Optional[str] = None, title: Optional[str] = None
):
    """
    Handle output formatting for CLI commands - unified interface for all modules.

    This function provides a consistent way for all modules to handle output
    formatting, supporting the standard CloudOps formats while maintaining
    Rich table display as the default.

    Args:
        data: Data to output (Rich Table, dict, list, or string)
        output_format: Output format ('table', 'csv', 'json', 'markdown', 'pdf')
        output_file: Optional file path to save output
        title: Optional title for the output

    Examples:
        # In any module CLI command:
        from runbooks.common.rich_utils import handle_output_format

        # Display Rich table by default
        handle_output_format(table)

        # Export to CSV
        handle_output_format(data, output_format='csv', output_file='report.csv')

        # Export to PDF with title
        handle_output_format(data, output_format='pdf', output_file='report.pdf', title='AWS Resources Report')
    """
    try:
        if output_format == "table":
            # Default Rich table display - just print to console
            if isinstance(data, Table):
                console.print(data)
            else:
                # Convert other data types to Rich display
                if isinstance(data, list) and data and isinstance(data[0], dict):
                    # List of dicts - create table
                    table = create_table(title=title)
                    headers = list(data[0].keys())
                    for header in headers:
                        table.add_column(header, style="cyan")

                    for item in data:
                        row = [str(item.get(h, "")) for h in headers]
                        table.add_row(*row)

                    console.print(table)
                elif isinstance(data, dict):
                    # Dictionary - display as key-value table
                    table = create_table(title=title or "Details")
                    table.add_column("Key", style="bright_blue")
                    table.add_column("Value", style="white")

                    for key, value in data.items():
                        table.add_row(str(key), str(value))

                    console.print(table)
                else:
                    # Other types - just print
                    if title:
                        console.print(f"\n[bold cyan]{title}[/bold cyan]")
                    console.print(data)
        else:
            # Use export_data for other formats
            output = export_data(data, output_format, output_file, title)

            # If no output file specified, print to console for non-table formats
            if not output_file and output_format != "pdf":
                if output_format == "json":
                    print_json(json.loads(output))
                elif output_format == "markdown":
                    print_markdown(output)
                else:
                    console.print(output)

    except Exception as e:
        print_error(f"Failed to format output: {e}")
        # Fallback to simple text output
        if title:
            console.print(f"\n[bold cyan]{title}[/bold cyan]")
        console.print(str(data))


# ===========================
# 5-LAYER PIPELINE RICH UTILITIES
# ===========================


def create_enrichment_progress_bar(description: str, total: int, show_throughput: bool = True) -> Progress:
    """
    Enhanced progress bar for enrichment operations with ETA + throughput.

    This function creates a comprehensive progress bar for resource enrichment
    operations across the 5-layer pipeline (Discovery, Organizations, Costs,
    Activity, Scoring). Provides real-time feedback on enrichment progress
    with accurate time estimates.

    Args:
        description: Operation description (e.g., "Enriching costs")
        total: Total number of items to process
        show_throughput: Whether to show items/second throughput (default: True)

    Returns:
        Configured Progress object ready for context manager usage

    Example:
        >>> import pandas as pd
        >>> df = pd.DataFrame({'InstanceId': ['i-123', 'i-456', 'i-789']})
        >>> with create_enrichment_progress_bar("Enriching costs", len(df)) as progress:
        ...     task = progress.add_task("Processing", total=len(df))
        ...     for idx, row in df.iterrows():
        ...         # Enrichment work here
        ...         progress.update(task, advance=1)

    Used By:
        - Layer 2: Organizations enrichment (account names)
        - Layer 3: Cost enrichment (pricing data)
        - Layer 4: Activity enrichment (CloudWatch metrics, VPC Flow Logs)
        - Layer 5: Scoring enrichment (decommission tiers)
    """
    columns = [
        SpinnerColumn(spinner_name="dots", style="cyan"),
        TextColumn("[progress.description]{task.description}"),
        BarColumn(bar_width=40, style="cyan", complete_style="green"),
        TaskProgressColumn(),
        MofNCompleteColumn(),
        TimeElapsedColumn(),
        TimeRemainingColumn(),
    ]

    if show_throughput:
        columns.append(TextColumn("[dim]{task.speed:.1f} items/s"))

    return Progress(*columns, console=console, transient=True)


def create_enrichment_summary_table(
    layer_name: str, metrics: Dict[str, Any], highlight_threshold: Optional[float] = None
) -> Table:
    """
    Standardized summary table for enrichment layers.

    Creates a consistent summary table showing key metrics for each enrichment
    layer. Automatically applies color coding for percentage metrics based on
    configurable thresholds.

    Args:
        layer_name: Name of enrichment layer (e.g., "Organizations", "Costs")
        metrics: Dictionary of metrics to display (key-value pairs)
        highlight_threshold: Optional percentage threshold for color coding (default: None)
                           Values >= threshold shown in green, < threshold in yellow

    Returns:
        Rich Table object with formatted metrics

    Example:
        >>> metrics = {
        ...     "Resources Enriched": 137,
        ...     "Metadata Coverage": "95.6%",
        ...     "API Calls": 4,
        ...     "Execution Time": "2.3s"
        ... }
        >>> table = create_enrichment_summary_table("Organizations", metrics, 95.0)
        >>> console.print(table)

    Used By:
        - All 5 layers for consistent summary reporting
        - Notebook outputs for professional presentation
        - CLI outputs for operation transparency
    """
    table = create_table(title=f"{layer_name} Enrichment Summary", box_style=box.ROUNDED)

    table.add_column("Metric", style="bright_blue", no_wrap=True)
    table.add_column("Value", style="white")

    for metric_name, metric_value in metrics.items():
        value_str = str(metric_value)

        # Apply conditional styling for percentage values
        if highlight_threshold is not None and isinstance(metric_value, str) and "%" in metric_value:
            try:
                # Extract numeric percentage
                pct_value = float(metric_value.rstrip("%"))
                if pct_value >= highlight_threshold:
                    value_str = f"[bright_green]{metric_value}[/bright_green]"
                else:
                    value_str = f"[yellow]{metric_value}[/yellow]"
            except ValueError:
                # Not a valid percentage, use default styling
                pass

        table.add_row(metric_name, value_str)

    return table


def create_layer_header(layer_number: int, layer_name: str, emoji: str) -> None:
    """
    Consistent layer header for 5-layer pipeline.

    Prints a standardized header for each layer of the enrichment pipeline,
    providing visual separation and clear progress indication.

    Args:
        layer_number: Layer number (1-5)
        layer_name: Descriptive layer name
        emoji: Emoji representing the layer (for visual identification)

    Example:
        >>> create_layer_header(1, "Discovery", "🔍")
        🔍 Layer 1: Discovery

        >>> create_layer_header(3, "Costs", "💰")
        💰 Layer 3: Costs

    Layer Mapping:
        - Layer 1: Discovery (🔍) - Resource collection
        - Layer 2: Organizations (🏢) - Account name enrichment
        - Layer 3: Costs (💰) - Pricing data enrichment
        - Layer 4: Activity (📊) - Usage metrics enrichment
        - Layer 5: Scoring (🎯) - Decommission tier calculation
    """
    console.print()
    console.print(f"{emoji} [bold bright_cyan]Layer {layer_number}: {layer_name}[/bold bright_cyan]")
    console.print()


def create_tier_distribution_table(scored_df, resource_type: str) -> Table:
    """
    Decommission tier distribution (MUST/SHOULD/COULD/KEEP).

    Creates a visual distribution table showing how scored resources are classified
    into decommission tiers with cost implications. Uses color-coded bars and
    emoji indicators for manager-friendly presentation.

    Args:
        scored_df: pandas DataFrame with 'DecommissionTier' column
        resource_type: Resource type for title (e.g., "ec2", "workspaces", "rds")

    Returns:
        Rich Table with tier distribution and visual indicators

    Example:
        >>> import pandas as pd
        >>> scored_df = pd.DataFrame({
        ...     'DecommissionTier': ['MUST', 'MUST', 'SHOULD', 'COULD', 'KEEP'],
        ...     'MonthlyCost': [100, 150, 80, 60, 200]
        ... })
        >>> table = create_tier_distribution_table(scored_df, "ec2")
        >>> console.print(table)

    Tier Definitions:
        - MUST (🔴): High confidence decommission candidates
        - SHOULD (🟡): Medium confidence decommission candidates
        - COULD (🟢): Low confidence optimization opportunities
        - KEEP (⚪): Active resources, retain
    """
    import pandas as pd

    table = create_table(title=f"{resource_type.upper()} Decommission Tier Distribution", box_style=box.ROUNDED)

    table.add_column("Tier", style="bold", no_wrap=True)
    table.add_column("Count", justify="right", style="cyan")
    table.add_column("Distribution", style="white")

    # Calculate tier distribution (use lowercase column name)
    tier_col = "DecommissionTier" if "DecommissionTier" in scored_df.columns else "decommission_tier"
    tier_counts = scored_df[tier_col].value_counts().to_dict()
    total = len(scored_df)

    # Define tier order and styling
    tier_config = {
        "MUST": {"emoji": "🔴", "style": "bright_red bold"},
        "SHOULD": {"emoji": "🟡", "style": "bright_yellow"},
        "COULD": {"emoji": "🟢", "style": "bright_green"},
        "KEEP": {"emoji": "⚪", "style": "dim white"},
    }

    for tier_name, config in tier_config.items():
        count = tier_counts.get(tier_name, 0)
        percentage = (count / total * 100) if total > 0 else 0

        # Create visual bar
        bar_length = int(percentage / 2)  # Scale to 50 chars max
        bar = "█" * bar_length

        tier_display = f"{config['emoji']} {tier_name}"
        distribution = f"[{config['style']}]{bar}[/{config['style']}] {percentage:.1f}%"

        table.add_row(tier_display, str(count), distribution)

    return table


def create_cost_breakdown_panel(total_monthly: float, total_annual: float, top_accounts: Dict[str, float]) -> Panel:
    """
    Cost intelligence panel for Layer 3.

    Creates a comprehensive cost breakdown panel showing monthly/annual totals
    and top spending accounts. Designed for executive-level cost visibility
    and financial decision-making.

    Args:
        total_monthly: Total monthly cost across all resources
        total_annual: Total annual cost projection
        top_accounts: Dictionary of {account_id: monthly_cost} for top spenders

    Returns:
        Rich Panel with formatted cost breakdown

    Example:
        >>> top_accounts = {
        ...     "123456789012": 5432.10,
        ...     "987654321098": 3210.50,
        ...     "555666777888": 2100.75
        ... }
        >>> panel = create_cost_breakdown_panel(12345.67, 148148.04, top_accounts)
        >>> console.print(panel)

    Used By:
        - Layer 3 cost enrichment summary
        - Executive dashboards and reports
        - FinOps business case presentations
    """
    # Format cost content
    content = Text()
    content.append("💰 Cost Analysis\n\n", style="bright_cyan bold")

    # Monthly and annual totals
    content.append("Monthly Total: ", style="white")
    content.append(format_cost(total_monthly))  # format_cost already returns styled Text
    content.append("\n")

    content.append("Annual Projection: ", style="white")
    content.append(format_cost(total_annual))  # format_cost already returns styled Text
    content.append("\n\n")

    # Top spending accounts
    if top_accounts:
        content.append("Top Spending Accounts:\n", style="bright_yellow")
        for idx, (account_id, monthly_cost) in enumerate(
            sorted(top_accounts.items(), key=lambda x: x[1], reverse=True)[:5], 1
        ):
            content.append(f"  {idx}. ", style="dim")
            content.append(f"{account_id}: ", style="cyan")
            content.append(format_cost(monthly_cost))
            content.append("\n")

    return Panel(
        content, title="[bright_cyan]Cost Intelligence[/bright_cyan]", border_style="bright_green", padding=(1, 2)
    )


def create_signal_heatmap_table(signal_data: Dict[str, int], resource_type: str) -> Table:
    """
    Activity signal heatmap (E1-E7 or W1-W6).

    Creates a color-coded heatmap showing distribution of activity signals
    for decommission analysis. Uses fire emoji intensity to indicate signal
    strength and risk level.

    Args:
        signal_data: Dictionary of {signal_name: count}
        resource_type: Resource type ("ec2", "workspaces", "lambda", "rds")

    Returns:
        Rich Table with signal distribution heatmap

    Example:
        >>> signals = {
        ...     "E1_idle": 23,
        ...     "E2_low_cpu": 45,
        ...     "E3_no_network": 12,
        ...     "E4_stopped": 8,
        ...     "E5_old": 34,
        ...     "E6_untagged": 56,
        ...     "E7_non_prod": 67
        ... }
        >>> table = create_signal_heatmap_table(signals, "ec2")
        >>> console.print(table)

    Signal Types:
        - EC2: E1-E7 (idle, low CPU, no network, stopped, old, untagged, non-prod)
        - WorkSpaces: W1-W6 (zero usage, low hours, disconnected, old, non-prod, auto-stop)
        - Lambda: L1-L6 (zero invocations, errors, deprecated runtime, oversized, timeout, cold start)
        - Snapshots: S1-S7 (orphaned, old, oversized, unencrypted, untagged, compliance, duplicate)
    """
    table = create_table(title=f"{resource_type.upper()} Activity Signal Distribution", box_style=box.ROUNDED)

    table.add_column("Signal", style="bold cyan", no_wrap=True)
    table.add_column("Count", justify="right", style="white")
    table.add_column("Intensity", style="white")

    total = sum(signal_data.values())

    for signal_name, count in sorted(signal_data.items(), key=lambda x: x[1], reverse=True):
        percentage = (count / total * 100) if total > 0 else 0

        # Fire emoji intensity based on percentage
        if percentage >= 20:
            intensity = "🔥🔥🔥"
            style = "bright_red bold"
        elif percentage >= 10:
            intensity = "🔥🔥"
            style = "yellow"
        elif percentage >= 5:
            intensity = "🔥"
            style = "bright_yellow"
        else:
            intensity = "💨"
            style = "dim"

        count_display = f"[{style}]{count}[/{style}]"
        intensity_display = f"[{style}]{intensity}[/{style}] {percentage:.1f}%"

        table.add_row(signal_name, count_display, intensity_display)

    return table


def format_currency(amount: float) -> str:
    """
    Format currency with $ and commas.

    Simple currency formatter for consistent financial display across all
    modules. Provides standard US currency formatting with 2 decimal places.

    Args:
        amount: Currency amount to format

    Returns:
        Formatted currency string

    Example:
        >>> format_currency(12345.67)
        '$12,345.67'

        >>> format_currency(1000000.00)
        '$1,000,000.00'
    """
    return f"${amount:,.2f}"


def format_percentage(value: float, decimal_places: int = 1) -> str:
    """
    Format percentage with % symbol.

    Standardized percentage formatter for metrics, coverage, and variance
    display. Supports configurable decimal precision.

    Args:
        value: Percentage value (0-100)
        decimal_places: Number of decimal places (default: 1)

    Returns:
        Formatted percentage string

    Example:
        >>> format_percentage(95.6789)
        '95.7%'

        >>> format_percentage(99.999, decimal_places=2)
        '100.00%'
    """
    return f"{value:.{decimal_places}f}%"


def _create_category_services_table(
    category_name: str, services: List[Dict[str, Any]], total_monthly_cost: float, show_zero_cost: bool = False
) -> "Table":
    """
    Create services table for a specific category to nest within Rich Tree.

    Args:
        category_name: Name of the category (e.g., "Compute", "Network")
        services: List of service dictionaries with cost and trend data
        total_monthly_cost: Total account monthly spend for percentage calculations

    Returns:
        Rich Table with services in this category

    Example:
        >>> table = _create_category_services_table("Compute", compute_services, 30721)
    """
    from rich.table import Table

    # Calculate category total
    category_total = sum(s["current_cost"] for s in services)

    # Create table with consistent column widths for alignment across categories
    table = Table(show_header=True, header_style="bold cyan", border_style="dim")

    # Add columns with consistent widths (important for cross-category alignment)
    table.add_column("Service", style="cyan", no_wrap=False, width=30)
    table.add_column("Current Month", justify="right", style="bright_green", width=15)
    table.add_column("Previous Month", justify="right", style="green", width=15)
    table.add_column("Change (MTD)", justify="right", width=12)
    table.add_column("% Total", justify="right", style="cyan", width=8)
    table.add_column("Trend", style="yellow", width=7)

    # Sort services by current cost (descending)
    sorted_services = sorted(services, key=lambda x: x["current_cost"], reverse=True)

    # Add service rows
    for idx, service in enumerate(sorted_services):
        service_name = service["service_name"]
        current_cost = service["current_cost"]
        previous_cost = service.get("previous_cost", 0)
        change_pct = service.get("change_pct", 0)

        # Comment #3/#5: Zero-cost filtering (skip if both periods have no cost)
        # Manager requirement: Skip service if both current AND previous are zero/negligible
        # Threshold: <$0.01 (effectively zero, avoids floating point issues)
        if not show_zero_cost and current_cost < 0.01 and previous_cost < 0.01:
            continue

        # Calculate percentage of total account cost
        pct_of_total = (current_cost / total_monthly_cost * 100) if total_monthly_cost > 0 else 0

        # Fix: If both current and previous are effectively $0 (<$0.01), show stable
        # This prevents misleading percentages when both periods have negligible costs
        if current_cost < 0.01 and previous_cost < 0.01:
            change_str = "[dim]→ stable[/dim]"
            change_pct = 0
        # Format change indicator
        elif change_pct > 0:
            change_str = f"[red]↑ +{change_pct:.1f}%[/red]"
        elif change_pct < 0:
            change_str = f"[green]↓ {change_pct:.1f}%[/green]"
        else:
            change_str = "[dim]→ stable[/dim]"

        # Determine trend indicator (arrows only for compact display)
        if abs(change_pct) < 5:
            trend = "→"
            trend_style = "dim"
        elif change_pct > 20:
            trend = "↑↑↑"
            trend_style = "red"
        elif change_pct > 10:
            trend = "↑↑"
            trend_style = "yellow"
        elif change_pct < -20:
            trend = "↓↓↓"
            trend_style = "green"
        elif change_pct < -10:
            trend = "↓↓"
            trend_style = "bright_green"
        else:
            trend = "→"
            trend_style = "dim"

        # Truncate long service names
        service_display = service_name[:28] if len(service_name) > 28 else service_name

        # Highlight top cost driver (first row) with bold style
        row_style = "bold" if idx == 0 else None

        # Add separator before last row (creates visual break before TOTAL)
        is_last_row = idx == len(sorted_services) - 1

        table.add_row(
            service_display,
            f"${current_cost:,.1f}",  # v1.1.20: Show 1 decimal place for clarity
            f"${previous_cost:,.1f}",  # v1.1.20: Show 1 decimal place for clarity
            change_str,
            f"{pct_of_total:.1f}%",
            f"[{trend_style}]{trend}[/{trend_style}]",
            style=row_style,
            end_section=is_last_row,
        )

    # Comment #4: ServiceX TOTAL simplification - TOTAL info now in category header only
    # Removed redundant category TOTAL row (manager feedback: move TOTAL to same line as category node)
    # Category header already shows total: "📊 Other ($445 - 62.4%)"
    # Previously calculated: category_previous, category_change, category_change_pct, category_change_str

    return table


def create_cost_breakdown_tree(
    services_by_category: Dict[str, List[Dict[str, Any]]],
    total_monthly_cost: float,
    optimization_opportunities: Optional[Dict[str, float]] = None,
    previous_services_costs: Optional[Dict[str, float]] = None,
    show_zero_cost: bool = False,
) -> Tree:
    """
    Create hierarchical cost breakdown using Rich Tree with per-category nested tables.

    Args:
        services_by_category: Output from categorize_aws_services()
        total_monthly_cost: Total monthly spend for percentage calculations
        optimization_opportunities: {category: monthly_savings} (optional)
        previous_services_costs: Previous month costs for each service (optional)

    Returns:
        Rich Tree object ready for console.print()

    Example:
        >>> tree = create_cost_breakdown_tree(categorized, 30721, {"Compute": 2931}, prev_costs)
        >>> console.print(tree)
    """
    from rich.tree import Tree
    from rich.text import Text

    # Calculate total previous month cost across all services
    total_previous_cost = sum(s.get("previous_cost", 0) for services in services_by_category.values() for s in services)

    # Create root with Current and Previous totals
    tree = Tree(
        f"💰 [bold bright_cyan]Top AWS Services by Cost[/bold bright_cyan]  "
        f"Current: [bright_green]${total_monthly_cost:,.1f}[/bright_green]  "  # v1.1.20: Show 1 decimal place
        f"Previous: [white]${total_previous_cost:,.1f}[/white]"  # v1.1.20: Show 1 decimal place
    )

    # Category icons
    category_icons = {
        "Compute": "💻",
        "Network": "🌐",
        "Storage": "💾",
        "Database": "🗄️",
        "Other": "📊",
    }

    # Sort categories by total cost (descending)
    sorted_categories = sorted(
        services_by_category.items(), key=lambda x: sum(s["current_cost"] for s in x[1]), reverse=True
    )

    for category, services in sorted_categories:
        # Calculate category total and previous total (Comment #4 Fix)
        category_total = sum(s["current_cost"] for s in services)
        category_previous = sum(s.get("previous_cost", 0) for s in services)
        category_pct = (category_total / total_monthly_cost * 100) if total_monthly_cost > 0 else 0

        # Create category branch with enhanced header format (Comment #4 Fix)
        icon = category_icons.get(category, "📦")
        category_branch = tree.add(
            f"{icon} [bold cyan]{category}[/bold cyan]   "
            f"Current: [bright_green]${category_total:,.1f}[/bright_green]   "  # v1.1.20: Show 1 decimal place
            f"Previous: [white]${category_previous:,.1f}[/white]   "  # v1.1.20: Show 1 decimal place
            f"% Total: [cyan]{category_pct:.1f}%[/cyan]"
        )

        # Add nested table with all services in this category
        category_table = _create_category_services_table(
            category_name=category,
            services=services,
            total_monthly_cost=total_monthly_cost,
            show_zero_cost=show_zero_cost,
        )
        category_branch.add(category_table)

        # v1.1.20: Removed savings display (hardcoded assumptions removed - NATO prevention)
        # optimization_opportunities parameter preserved for backward compatibility

    return tree


# ==============================================================================
# COMPACT PANEL UTILITIES (v1.1.20 UX Improvements)
# ==============================================================================


def create_compact_summary_panel(
    metrics: Dict[str, str], title: str, columns: int = 3, border_style: str = "green"
) -> Panel:
    """
    Create compact multi-column summary panel (target: 3-5 lines max).

    Reduces vertical space by 50%+ compared to traditional single-column panels.
    Uses Rich Columns to display metrics horizontally across 2-3 columns.

    Args:
        metrics: Dict of {label: value} pairs (e.g., {"💰 Cost": "$1,234"})
        title: Panel title with optional emoji
        columns: Number of columns (2-3 recommended, default: 3)
        border_style: Rich color/style for panel border

    Returns:
        Rich Panel with multi-column compact layout

    Example:
        >>> metrics = {
        ...     "💰 Annual Cost": "$7.0K",
        ...     "📊 Savings": "$4.9K",
        ...     "🪣 Buckets": "45/50",
        ...     "🎯 Opportunities": "33",
        ...     "⚡ Time": "3.5s",
        ...     "✅ MCP": "99.5%"
        ... }
        >>> panel = create_compact_summary_panel(metrics, "S3 Optimization", columns=3)
        >>> console.print(panel)

        Output (3 columns, 2 rows):
        ╭─────────────── S3 Optimization ───────────────╮
        │ 💰 Annual Cost: $7.0K   🪣 Buckets: 45/50     │
        │ 📊 Savings: $4.9K       🎯 Opportunities: 33  │
        │ ⚡ Time: 3.5s           ✅ MCP: 99.5%         │
        ╰───────────────────────────────────────────────╯

    Pattern: Proven runbooks Rich usage (see rich_utils.py:2300-2400)
    """
    from rich.columns import Columns
    from rich.text import Text

    # Split metrics into column groups
    items = list(metrics.items())
    rows_per_column = (len(items) + columns - 1) // columns  # Ceiling division

    # Create column renderables
    column_renderables = []
    for col_idx in range(columns):
        col_text = Text()
        start_idx = col_idx * rows_per_column
        end_idx = min(start_idx + rows_per_column, len(items))

        for label, value in items[start_idx:end_idx]:
            col_text.append(f"{label}: ", style="dim")
            col_text.append(f"{value}", style="bright_white")
            if start_idx + items.index((label, value)) < len(items) - 1:
                col_text.append("\n")

        column_renderables.append(col_text)

    # Create columns layout
    columns_layout = Columns(column_renderables, equal=True, expand=True)

    # Wrap in panel
    return Panel(columns_layout, title=f"[bold]{title}[/bold]", border_style=border_style, padding=(0, 1))


def create_inline_metrics(metrics: Dict[str, str], separator: str = " | ", style: str = "dim") -> str:
    """
    Create single-line inline metric display with separators.

    Reduces 5-line strategy breakdown panels to 1 inline line (80% reduction).

    Args:
        metrics: Dict of {label: value} pairs
        separator: String to separate metrics (default: " | ")
        style: Rich style for entire line

    Returns:
        Formatted string ready for console.print()

    Example:
        >>> metrics = {
        ...     "🔄 Intelligent-Tiering": "$0",
        ...     "❄️ Glacier": "$3.3K",
        ...     "🗑️ Expiration": "$1.6K"
        ... }
        >>> line = create_inline_metrics(metrics)
        >>> console.print(line)

        Output (1 line):
        🔄 Intelligent-Tiering: $0 | ❄️ Glacier: $3.3K | 🗑️ Expiration: $1.6K

    Pattern: KISS principle - simplify verbose multi-line displays
    """
    parts = [f"{label}: {value}" for label, value in metrics.items()]
    formatted = separator.join(parts)
    return f"[{style}]{formatted}[/{style}]" if style else formatted


def create_discovery_summary_table(
    discoveries: Dict[str, tuple[int, str]], title: str = "Resource Discovery Summary"
) -> Table:
    """
    Create consolidated discovery summary table (replaces 5+ individual messages).

    Consolidates redundant "Discovered X instances..." messages into single table.
    Achieves 60%+ reduction in discovery message verbosity.

    Args:
        discoveries: Dict of {resource_type: (count, status)} pairs
                    Example: {"EC2 Instances": (13, "✅ Analyzed")}
        title: Table title

    Returns:
        Rich Table with discovery summary

    Example:
        >>> discoveries = {
        ...     "💻 EC2 Instances": (13, "✅ Analyzed"),
        ...     "🗄️ RDS Databases": (0, "⚪ None found"),
        ...     "📦 S3 Buckets": (14, "✅ Analyzed"),
        ...     "⚡ DynamoDB Tables": (6, "✅ Analyzed"),
        ...     "🌐 ALB/NLB": (2, "✅ Analyzed"),
        ...     "⚙️ Auto Scaling Groups": (2, "✅ Analyzed"),
        ...     "🐳 ECS Clusters": (0, "⚪ None found"),
        ...     "🌍 Route53 Zones": (2, "✅ Analyzed"),
        ...     "🔗 Direct Connect": (0, "⚪ None found")
        ... }
        >>> table = create_discovery_summary_table(discoveries)
        >>> console.print(table)

        Output (single compact table):
        ┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┓
        ┃ Resource Type         ┃ Count ┃ Status      ┃
        ┡━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━┩
        │ 💻 EC2 Instances      │    13 │ ✅ Analyzed │
        │ 📦 S3 Buckets         │    14 │ ✅ Analyzed │
        │ ⚡ DynamoDB Tables    │     6 │ ✅ Analyzed │
        │ 🌐 ALB/NLB            │     2 │ ✅ Analyzed │
        │ ⚙️ Auto Scaling Groups│     2 │ ✅ Analyzed │
        │ 🌍 Route53 Zones      │     2 │ ✅ Analyzed │
        │ 🗄️ RDS Databases      │     0 │ ⚪ None     │
        │ 🐳 ECS Clusters       │     0 │ ⚪ None     │
        │ 🔗 Direct Connect     │     0 │ ⚪ None     │
        └───────────────────────┴───────┴─────────────┘

    Pattern: Consolidation over repetition (runbooks UX principle)
    """
    table = Table(title=title, show_header=True, header_style="bold cyan", border_style="dim")

    table.add_column("Resource Type", style="white", no_wrap=True)
    table.add_column("Count", justify="right", style="bright_green")
    table.add_column("Status", style="dim")

    # Sort by count (descending) to show resources with data first
    sorted_discoveries = sorted(discoveries.items(), key=lambda x: x[1][0], reverse=True)

    for resource_type, (count, status) in sorted_discoveries:
        # Color code based on count
        count_style = "bright_green" if count > 0 else "dim"
        table.add_row(resource_type, f"[{count_style}]{count}[/{count_style}]", status)

    return table


# ============================================================================
# Executive Dashboard Helpers for Jupyter Notebooks (v1.1.21)
# ============================================================================
# Visual parity with CLI dashboard for CEO/CTO/CloudOps personas


def create_financial_summary_table(financial_impact: Dict[str, Any], title: str = "Financial Impact Summary") -> Table:
    """
    Create Rich table for executive financial summaries (CEO/CTO notebooks).

    Provides visual parity with CLI dashboard --executive output format.
    Used in FinOps notebooks for board-ready financial presentations.

    Args:
        financial_impact: Dictionary with financial metrics
            - annual_cost_reduction: Annual savings amount
            - monthly_savings: Monthly savings amount
            - quarterly_savings: Quarterly savings amount
            - implementation_investment: Upfront investment required
            - net_annual_benefit: Year 1 net benefit
            - roi_percentage: ROI percentage
            - payback_period_months: Payback period in months
            - three_year_value: 3-year cumulative value
        title: Table title (default: "Financial Impact Summary")

    Returns:
        Rich Table with formatted financial metrics

    Example:
        >>> from runbooks.common.rich_utils import console, create_financial_summary_table, format_cost
        >>> financial_impact = {
        ...     'annual_cost_reduction': 300000,
        ...     'roi_percentage': 600,
        ...     'payback_period_months': 3.5
        ... }
        >>> table = create_financial_summary_table(financial_impact)
        >>> console.print(table)

        Output:
        ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
        ┃ Metric                     ┃ Value           ┃
        ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
        │ Annual Savings             │ $300,000        │
        │ Monthly Savings            │ $25,000         │
        │ Implementation Investment  │ $50,000         │
        │ ROI                        │ 600.0%          │
        │ Payback Period             │ 3.5 months      │
        │ 3-Year Value               │ $900,000        │
        └────────────────────────────┴─────────────────┘

    Pattern: Consistent Rich table styling across CLI and notebooks
    """
    table = Table(title=title, show_header=True, header_style="bold cyan", border_style="bright_green", box=box.ROUNDED)

    table.add_column("Metric", style="cyan", no_wrap=True)
    table.add_column("Value", justify="right", style="bright_green")

    # Annual metrics
    if "annual_cost_reduction" in financial_impact:
        table.add_row("Annual Savings", format_cost(financial_impact["annual_cost_reduction"]))
    if "monthly_savings" in financial_impact:
        table.add_row("Monthly Savings", format_cost(financial_impact["monthly_savings"]))
    if "quarterly_savings" in financial_impact:
        table.add_row("Quarterly Savings", format_cost(financial_impact["quarterly_savings"]))

    # Investment metrics
    if "implementation_investment" in financial_impact:
        table.add_row("Implementation Investment", format_cost(financial_impact["implementation_investment"]))
    if "net_annual_benefit" in financial_impact:
        table.add_row("Net Annual Benefit (Year 1)", format_cost(financial_impact["net_annual_benefit"]))

    # ROI metrics
    if "roi_percentage" in financial_impact:
        table.add_row("ROI", f"{financial_impact['roi_percentage']:.1f}%")
    if "payback_period_months" in financial_impact:
        table.add_row("Payback Period", f"{financial_impact['payback_period_months']:.1f} months")
    if "three_year_value" in financial_impact:
        table.add_row("3-Year Value", format_cost(financial_impact["three_year_value"]))

    return table


def create_board_summary_panel(
    financial_impact: Dict[str, Any], risk_assessment: Dict[str, Any], title: str = "Board-Ready Summary"
) -> Panel:
    """
    Create Rich panel for CEO/CFO board summary (executive notebook).

    Provides visual emphasis for key decision metrics in board presentations.
    Consistent with CLI dashboard --executive --board-summary output.

    Args:
        financial_impact: Financial metrics dictionary
        risk_assessment: Risk assessment dictionary
        title: Panel title (default: "Board-Ready Summary")

    Returns:
        Rich Panel with formatted board summary

    Example:
        >>> from runbooks.common.rich_utils import console, create_board_summary_panel
        >>> financial_impact = {'annual_cost_reduction': 300000, 'roi_percentage': 600}
        >>> risk_assessment = {'overall_risk_rating': 'Low', 'executive_recommendation': 'APPROVE'}
        >>> panel = create_board_summary_panel(financial_impact, risk_assessment)
        >>> console.print(panel)

        Output:
        ╭──────────────── Board-Ready Summary ────────────────╮
        │ 💰 Annual Savings: $300,000                         │
        │ 📊 ROI: 600.0%                                      │
        │ ⚠️  Risk: Low                                       │
        │ 🎯 Recommendation: APPROVE                          │
        ╰─────────────────────────────────────────────────────╯

    Pattern: Panel emphasis for executive decision points
    """
    from rich.text import Text

    summary = Text()
    summary.append("💰 Annual Savings: ", style="bold")
    summary.append(format_cost(financial_impact.get("annual_cost_reduction", 0)))
    summary.append("\n")

    summary.append("📊 ROI: ", style="bold")
    summary.append(f"{financial_impact.get('roi_percentage', 0):.1f}%", style="bright_green")
    summary.append("\n")

    summary.append("⏱️  Payback: ", style="bold")
    summary.append(f"{financial_impact.get('payback_period_months', 0):.1f} months", style="cyan")
    summary.append("\n")

    summary.append("⚠️  Risk: ", style="bold")
    risk_level = risk_assessment.get("overall_risk_rating", "Unknown")
    risk_style = "green" if risk_level == "Low" else "yellow" if risk_level == "Medium" else "red"
    summary.append(risk_level, style=risk_style)
    summary.append("\n")

    summary.append("🎯 Recommendation: ", style="bold")
    recommendation = risk_assessment.get("executive_recommendation", "REVIEW")
    rec_style = "bright_green" if "APPROVE" in recommendation.upper() else "yellow"
    summary.append(recommendation, style=rec_style)

    return Panel(summary, title=title, border_style="bright_green", padding=(1, 2))


def create_account_tree(df: Any, title: str = "Multi-Account Resource Discovery") -> Tree:
    """
    Create tree visualization of account/region/resource hierarchy.

    Provides hierarchical view of multi-account AWS resource discovery.
    Consistent with CLI dashboard --output-format tree.

    Args:
        df: pandas DataFrame with columns: account_id, region, resource_type
        title: Tree root label (default: "Multi-Account Resource Discovery")

    Returns:
        Rich Tree with account/region/resource hierarchy

    Example:
        >>> import pandas as pd
        >>> from runbooks.common.rich_utils import console, create_account_tree
        >>> df = pd.DataFrame({
        ...     'account_id': ['111111111111', '111111111111', '222222222222'],
        ...     'region': ['ap-southeast-2', 'ap-southeast-6', 'ap-southeast-2'],
        ...     'resource_type': ['EC2', 'EC2', 'RDS']
        ... })
        >>> tree = create_account_tree(df)
        >>> console.print(tree)

        Output:
        📊 Multi-Account Resource Discovery
        ├── 🏢 Account: 111111111111
        │   ├── 🌍 Region: ap-southeast-2
        │   │   └── 📦 EC2: 1 resources
        │   └── 🌍 Region: ap-southeast-6
        │       └── 📦 EC2: 1 resources
        └── 🏢 Account: 222222222222
            └── 🌍 Region: ap-southeast-2
                └── 📦 RDS: 1 resources

    Pattern: Tree visualization for hierarchical multi-account data
    """
    tree = Tree(f"📊 {title}", style="bold cyan")

    # Group by account
    for account_id in df["account_id"].unique():
        account_node = tree.add(f"🏢 Account: {account_id}", style="bright_green")
        account_df = df[df["account_id"] == account_id]

        # Group by region within account
        for region in account_df["region"].unique():
            region_node = account_node.add(f"🌍 Region: {region}", style="cyan")
            region_df = account_df[account_df["region"] == region]

            # Count resources by type
            if "resource_type" in region_df.columns:
                resource_counts = region_df["resource_type"].value_counts()
                for resource_type, count in resource_counts.items():
                    region_node.add(f"📦 {resource_type}: {count} resources", style="dim")
            else:
                # Fallback: just show total count
                region_node.add(f"📦 Resources: {len(region_df)}", style="dim")

    return tree


def create_validation_summary_table(
    mcp_validation: Dict[str, Any], production_score: Optional[Dict[str, Any]] = None, title: str = "Validation Summary"
) -> Table:
    """
    Create Rich table for 4-way validation results summary.

    Displays MCP validation accuracy and production-ready score.
    Used in CloudOps/SRE technical notebook for validation evidence.

    Args:
        mcp_validation: MCP validation results dictionary
            - accuracy: Validation accuracy (0.0-1.0)
            - method: Validation method description
            - matches: Number of matching accounts
            - mismatches: Number of mismatched accounts
        production_score: Optional production score dictionary
            - total: Total score (0-100)
            - data_availability: Data score (0-40)
            - workflow_execution: Workflow score (0-30)
            - technical_credibility: Technical score (0-30)
            - status: PASS/FAIL
        title: Table title (default: "Validation Summary")

    Returns:
        Rich Table with validation results

    Example:
        >>> from runbooks.common.rich_utils import console, create_validation_summary_table
        >>> mcp_validation = {
        ...     'accuracy': 0.9987,
        ...     'method': 'Account-level aggregation',
        ...     'matches': 10,
        ...     'mismatches': 0
        ... }
        >>> production_score = {'total': 93, 'status': 'PASS'}
        >>> table = create_validation_summary_table(mcp_validation, production_score)
        >>> console.print(table)

        Output:
        ┏━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
        ┃ Validation Metric      ┃ Result        ┃
        ┡━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
        │ MCP Accuracy           │ 99.87% ✅     │
        │ Validation Method      │ Account-level │
        │ Matches                │ 10            │
        │ Mismatches             │ 0             │
        │ Production Score       │ 93/100 ✅     │
        │ Status                 │ PASS          │
        └────────────────────────┴───────────────┘

    Pattern: Evidence-based validation reporting for audit compliance
    """
    table = Table(title=title, show_header=True, header_style="bold cyan", border_style="dim", box=box.ROUNDED)

    table.add_column("Validation Metric", style="cyan", no_wrap=True)
    table.add_column("Result", justify="right", style="bright_green")

    # MCP validation metrics
    if mcp_validation:
        accuracy = mcp_validation.get("accuracy", 0)
        accuracy_pct = accuracy * 100
        accuracy_status = "✅" if accuracy >= 0.995 else "⚠️" if accuracy >= 0.95 else "❌"
        table.add_row("MCP Accuracy", f"{accuracy_pct:.2f}% {accuracy_status}")

        if "method" in mcp_validation:
            table.add_row("Validation Method", mcp_validation["method"])
        if "matches" in mcp_validation:
            table.add_row("Matches", str(mcp_validation["matches"]))
        if "mismatches" in mcp_validation:
            mismatch_style = "dim" if mcp_validation["mismatches"] == 0 else "yellow"
            table.add_row("Mismatches", f"[{mismatch_style}]{mcp_validation['mismatches']}[/{mismatch_style}]")

    # Production score metrics
    if production_score:
        total_score = production_score.get("total", 0)
        score_status = "✅" if total_score >= 70 else "⚠️" if total_score >= 50 else "❌"
        table.add_row("Production Score", f"{total_score}/100 {score_status}")

        if "status" in production_score:
            status = production_score["status"]
            status_style = "bright_green" if status == "PASS" else "yellow"
            table.add_row("Status", f"[{status_style}]{status}[/{status_style}]")

    return table


# ═══════════════════════════════════════════════════════════════════════════
# HTML EXPORT UTILITIES (v1.1.24 + v1.1.29 Phase 5)
# ═══════════════════════════════════════════════════════════════════════════


def _strip_spinner_frames(html_content: str) -> str:
    """
    Strip Rich spinner animation frames from HTML export.

    v1.1.29 Phase 5 Issue #16: Rich's Console(record=True) captures all output
    including progress spinners, causing massive file bloat (~50% of HTML).

    Spinner patterns to strip:
    - Braille spinner chars: ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏
    - Status messages: "Fetching cost data...", "Initializing...", "Processing..."
    - Time stamps: "0:00:00", "0:00:01", etc.

    Args:
        html_content: Raw HTML from console.export_html()

    Returns:
        Cleaned HTML with spinner frames removed

    Example:
        Before: 51K tokens with 704 spinner frames
        After: ~25K tokens with actual dashboard content only
    """
    import re

    # Braille spinner characters used by Rich progress spinners
    spinner_chars = r"[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]"

    # v1.1.29 Enhanced: Pattern 0 - Strip spinner frame triplets (CRITICAL)
    # Rich concatenates spinner frames without newlines in HTML export:
    # <span...>⠋</span> <span...>Fetching cost data...</span> <span...>0:00:00</span>
    # This pattern matches spinner + optional status message + timestamp as a unit
    spinner_frame_pattern = (
        r"<span[^>]*>" + spinner_chars + r"</span>"  # Spinner char span
        r"\s*"  # Optional whitespace
        r"(?:<span[^>]*>[^<]*(?:Fetching|Initializing|Processing|Loading|Discovering|"
        r"Analyzing|Enriching|Collecting|Building|complete|data\.\.\.)[^<]*</span>\s*)*"  # Status spans
        r"(?:<span[^>]*>\d+:\d+:\d+</span>)?"  # Optional timestamp
        r"\s*"  # Trailing whitespace
    )
    html_clean = re.sub(spinner_frame_pattern, "", html_content)

    # Pattern 1: Strip entire lines containing spinner chars (backup for line-based HTML)
    # Rich outputs each spinner frame as a full line with spinner + message + timestamp
    # Example: "<span>⠋</span> <span>Fetching data...</span> <span>0:00:01</span>\n"
    full_line_with_spinner = r"^.*" + spinner_chars + r".*$\n?"
    html_clean = re.sub(full_line_with_spinner, "", html_clean, flags=re.MULTILINE)

    # Pattern 2: Strip lines with status messages + timestamps (orphaned from pattern 1)
    # These are progress status lines that may not have spinner char on same line
    status_keywords = (
        r"(?:Fetching|Initializing|Processing|Loading|Discovering|Analyzing|Enriching|Collecting|Building)"
    )
    status_line_pattern = (
        r"^.*<span[^>]*>" + status_keywords + r"[^<]*</span>"  # Status message
        r".*<span[^>]*>\d+:\d+:\d+</span>.*$\n?"  # Timestamp
    )
    html_clean = re.sub(status_line_pattern, "", html_clean, flags=re.MULTILINE)

    # Pattern 3: Clean up any remaining orphaned spinner chars in spans
    orphan_spinner = r"<span[^>]*>" + spinner_chars + r"</span>\s*"
    html_clean = re.sub(orphan_spinner, "", html_clean)

    # v1.1.29: Pattern 3B - Remove progress bar remnants (━╸╺ chars with timestamps)
    progress_bar_pattern = r"<span[^>]*>[━╸╺░▒▓█]+</span>"
    html_clean = re.sub(progress_bar_pattern, "", html_clean)

    # v1.1.29: Pattern 3C - Remove orphaned timestamp spans without context
    orphan_timestamp = r"<span[^>]*>\s*\d+:\d+:\d+\s*</span>"
    html_clean = re.sub(orphan_timestamp, "", html_clean)

    # v1.1.29: Pattern 3D - Remove orphaned percentage spans (e.g., "78%", "89%")
    orphan_percentage = r"<span[^>]*>\s*\d+%\s*</span>"
    html_clean = re.sub(orphan_percentage, "", html_clean)

    # Pattern 4: Remove consecutive empty lines that result from stripping
    html_clean = re.sub(r"\n{3,}", "\n\n", html_clean)

    # Pattern 5: Strip lines that are just whitespace + timestamp (orphaned timestamps)
    timestamp_only = r"^\s*<span[^>]*>\d+:\d+:\d+</span>\s*$"
    html_clean = re.sub(timestamp_only, "", html_clean, flags=re.MULTILINE)

    # v1.1.29: Pattern 6 - Remove empty span tags left after cleaning
    empty_spans = r"<span[^>]*>\s*</span>"
    html_clean = re.sub(empty_spans, "", html_clean)

    # v1.1.29: Pattern 7 - Strip ANSI cursor control sequences
    # [1A = cursor up 1 line, [2K = clear entire line (Rich progress bars)
    # These escape codes appear in HTML when console.record=True captures progress output
    cursor_control_pattern = r"\[\d*[AJK]"  # Matches [1A, [2K, [2J, etc.
    html_clean = re.sub(cursor_control_pattern, "", html_clean)

    return html_clean


def create_recording_console(width: int = 160, force_terminal: bool = True) -> Console:
    """
    Create Rich console with HTML export capability (record=True).

    Enables HTML export via console.export_html() for stakeholder distribution
    (executive board meetings, architect design reviews, SRE incident reports).

    Args:
        width: Console width for fixed-width formatting (default: 160 chars)
        force_terminal: Enable colors even when not in terminal (default: True)

    Returns:
        Console instance with recording enabled for HTML export

    Example:
        >>> console = create_recording_console()
        >>> console.print("[bold green]Cost Dashboard[/bold green]")
        >>> html = console.export_html(theme="monokai", inline_styles=True)
        >>> Path("dashboard.html").write_text(html)

    Design:
        - record=True enables console output capture for export
        - width=160 provides optimal readability (v1.1.30: Trend column visible)
        - force_terminal=True preserves colors in HTML export
        - Compatible with existing OutputController pattern

    Pattern: v1.1.24 HTML/PDF export enhancement for 3 personas
    """
    if not USE_RICH:
        # Test mode: Return MockConsole (no HTML export capability)
        return MockConsole()

    return Console(
        theme=CLOUDOPS_THEME,
        record=True,  # Enable HTML export
        width=width,
        force_terminal=force_terminal,
        color_system="truecolor",  # Full color spectrum for HTML
    )


def export_console_html(
    console: Console,
    output_path: str,
    mode: str = "architect",
    theme: Optional[str] = None,
    metadata: Optional[Dict[str, Any]] = None,
) -> Optional[str]:
    """
    Export Rich console output to HTML with persona-specific styling.

    Supports 3 dashboard modes with CSS customization:
    - executive: Large fonts, summary focus, high contrast (CFO/CTO reports)
    - architect: Standard view, all sections visible (design reviews)
    - sre: Monospace emphasis, metrics highlighted (incident reports)

    Args:
        console: Rich Console instance with record=True
        output_path: Path to HTML output file
        mode: Dashboard mode (executive/architect/sre, default: architect)
        theme: Rich theme name (default: None for CloudOps theme)
        metadata: Optional metadata dict (profile, timestamp, version)

    Returns:
        HTML file path on success, None on failure

    Example:
        >>> console = create_recording_console()
        >>> # ... render dashboard ...
        >>> export_console_html(
        ...     console,
        ...     "/tmp/executive-dashboard.html",
        ...     mode="executive",
        ...     metadata={"profile": "cfo-billing", "timestamp": "2025-11-17"}
        ... )
        '/tmp/executive-dashboard.html'

    Persona CSS:
        - Executive: 16px font, hide detail sections, border emphasis
        - Architect: 14px font, show all sections, standard layout
        - SRE: 12px monospace, metrics border highlight

    Pattern: Mode-conditional rendering for stakeholder distribution
    """
    try:
        # Check if console supports HTML export
        if not hasattr(console, "export_html"):
            print_warning(
                "HTML export unavailable - console not initialized with record=True. "
                "Use create_recording_console() instead of get_console()."
            )
            # Fallback: Export as plain text in <pre> tag
            if hasattr(console, "export_text"):
                text_output = console.export_text()
            else:
                text_output = "Console output not available (test mode or recording disabled)"

            html_content = f"""<html>
<head>
<title>Dashboard Export (Fallback)</title>
<style type="text/css">
    body {{ font-family: monospace; padding: 20px; background: #1e1e1e; color: #d4d4d4; }}
    pre {{ white-space: pre-wrap; word-wrap: break-word; }}
</style>
</head>
<body><pre>{text_output}</pre></body>
</html>"""

            # Gap 4 Fix: Inject validation badge even in fallback mode
            persona_css = _get_persona_css(mode)
            html_with_badge = _inject_persona_css(html_content, persona_css, mode)

            from pathlib import Path

            Path(output_path).write_text(html_with_badge, encoding="utf-8")
            return output_path

        # Export Rich console to HTML with inline styles
        html_fragment = console.export_html(
            theme=theme,
            clear=False,
            inline_styles=True,  # Standalone HTML (no external CSS)
            code_format="<pre style=\"font-family:Menlo,'Courier New',monospace;\">{code}</pre>",
        )

        # v1.1.29 Phase 5: Strip spinner animation frames from HTML export
        # Issue #16: Rich's record=True captures progress spinners (704+ frames = 50% bloat)
        html_fragment = _strip_spinner_frames(html_fragment)

        # Wrap Rich HTML fragment in proper HTML document structure
        html_content = f"""<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CloudOps Runbooks FinOps Dashboard - {mode.title()} Mode</title>
</head>
<body>
{html_fragment}
</body>
</html>"""

        # Inject persona-specific CSS
        persona_css = _get_persona_css(mode)
        html_with_css = _inject_persona_css(html_content, persona_css, mode)

        # Add metadata wrapper (timestamp, version, profile)
        if metadata:
            html_final = _add_html_metadata(html_with_css, metadata)
        else:
            html_final = html_with_css

        # Write to output file
        from pathlib import Path

        Path(output_path).write_text(html_final, encoding="utf-8")

        return output_path

    except Exception as e:
        print_error(f"HTML export failed: {str(e)}")
        return None


def _get_persona_css(mode: str) -> str:
    """
    Get CSS overrides for persona-specific dashboard layouts.

    Args:
        mode: Dashboard mode (executive/architect/sre)

    Returns:
        CSS string for injection into HTML export
    """
    if mode == "executive":
        return """
        /* Executive Mode: Large fonts, summary focus, high contrast */
        body {
            font-size: 16px !important;
            line-height: 1.6 !important;
            max-width: 1400px !important;
            margin: 20px auto !important;
        }
        pre {
            font-size: 14px !important;
            background-color: #f8f9fa !important;
            padding: 15px !important;
            border-radius: 5px !important;
            overflow-x: auto !important;  /* v1.1.30: Horizontal scroll for narrow viewports */
        }
        /* Emphasize summary sections */
        .r1 { border-left: 4px solid #667eea !important; padding-left: 10px !important; }
        """
    elif mode == "sre":
        return """
        /* SRE Mode: Monospace emphasis, metrics highlighted */
        body {
            font-family: 'Menlo', 'Monaco', 'Courier New', monospace !important;
            font-size: 12px !important;
            background-color: #1e1e1e !important;
            color: #d4d4d4 !important;
        }
        pre {
            background-color: #2d2d2d !important;
            border-left: 4px solid #28a745 !important;
            overflow-x: auto !important;  /* v1.1.30: Horizontal scroll for narrow viewports */
        }
        /* Highlight metrics */
        .r1 { color: #4ec9b0 !important; }
        """
    else:  # architect (default)
        return """
        /* Architect Mode: Standard view, all sections visible */
        body {
            font-size: 14px !important;
            line-height: 1.5 !important;
        }
        pre {
            font-size: 13px !important;
            background-color: #f5f5f5 !important;
            padding: 12px !important;
            overflow-x: auto !important;  /* v1.1.30: Horizontal scroll for narrow viewports */
        }
        """


def _inject_persona_css(html_content: str, persona_css: str, mode: str) -> str:
    """
    Inject persona-specific CSS into HTML export.

    Args:
        html_content: Base HTML from console.export_html()
        persona_css: CSS overrides for persona
        mode: Dashboard mode for title customization

    Returns:
        HTML with injected CSS and validation badge
    """
    # Gap 4 (v1.1.29): Add validation badge CSS
    validation_badge_css = """
        /* Gap 4: Validation Badge Styles (v1.1.29) */
        .validation-badge {
            position: sticky;
            top: 0;
            z-index: 1000;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 12px 20px;
            margin: 0 0 20px 0;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 600;
            text-align: center;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
        }
        .validation-badge.level-strict {
            background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
        }
        .validation-badge.level-business {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }
        .validation-badge.level-operational {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
        }
        .validation-badge.confidence-high {
            border: 3px solid #28a745;
        }
        .validation-badge.confidence-medium {
            border: 3px solid #ffc107;
        }
        .validation-badge.confidence-low {
            border: 3px solid #dc3545;
        }
        .validation-icon {
            font-size: 20px;
        }
    """

    # Inject CSS and validation badge CSS before </head> tag
    css_block = f"""
    <style type="text/css">
        /* CloudOps Runbooks FinOps Dashboard - {mode.title()} Mode */
        {persona_css}
        {validation_badge_css}
    </style>
    </head>"""

    html_with_css = html_content.replace("</head>", css_block)

    # Gap 4 (v1.1.29): Inject validation badge after <body> tag
    html_with_badge = _inject_validation_badge(html_with_css, mode)

    return html_with_badge


def _inject_validation_badge(html_content: str, mode: str) -> str:
    """
    Inject validation transparency badge at top of HTML dashboard.

    Gap 4 (v1.1.29): Display validation level and confidence for executives.

    Args:
        html_content: HTML with CSS injected
        mode: Dashboard mode (executive/architect/sre)

    Returns:
        HTML with validation badge injected after <body> tag
    """
    # Get persona-specific validation configuration
    from runbooks.finops.persona_formatter import PersonaFormatter

    formatter = PersonaFormatter(persona=mode)
    config = formatter.config

    # Calculate confidence level class
    confidence_pct = config.confidence_threshold * 100
    if confidence_pct >= 99.9:
        confidence_class = "confidence-high"
        confidence_icon = "✅"
        confidence_label = "Strict Validation"
    elif confidence_pct >= 99.5:
        confidence_class = "confidence-high"
        confidence_icon = "✅"
        confidence_label = "Business Validation"
    elif confidence_pct >= 95.0:
        confidence_class = "confidence-medium"
        confidence_icon = "⚠️"
        confidence_label = "Operational Validation"
    else:
        confidence_class = "confidence-low"
        confidence_icon = "⚠️"
        confidence_label = "Basic Validation"

    # Determine validation level class
    level_class = f"level-{config.validation_level}"

    # v1.1.29 Phase 3: Business-driven badge title (Manager decision)
    # Build validation badge HTML with enterprise branding
    badge_html = f"""
    <div class="validation-badge {level_class} {confidence_class}">
        <span class="validation-icon">{confidence_icon}</span>
        <span>AWS FinOps Dashboard - Cost & Activity Analysis</span>
        <span style="opacity: 0.8; font-size: 12px;">
            ({config.validation_level.title()} Mode | {confidence_pct:.1f}% Accuracy)
        </span>
    </div>
    """

    # Inject badge after <body> tag
    html_with_badge = html_content.replace("<body>", f"<body>\n{badge_html}")

    return html_with_badge


def _add_html_metadata(html_content: str, metadata: Dict[str, Any]) -> str:
    """
    Add metadata footer to HTML export (timestamp, version, profile).

    Args:
        html_content: HTML content
        metadata: Metadata dict (profile, timestamp, version)

    Returns:
        HTML with metadata footer
    """
    profile = metadata.get("profile", "unknown")
    timestamp = metadata.get("timestamp", datetime.now().isoformat())
    version = metadata.get("version", "1.1.24")

    footer = f"""
    <hr style="margin-top: 40px; border: 1px solid #ccc;">
    <footer style="font-size: 11px; color: #666; text-align: center; padding: 20px;">
        <p>Generated by <strong>CloudOps Runbooks v{version}</strong></p>
        <p>Profile: {profile} | Timestamp: {timestamp}</p>
    </footer>
    </body>"""

    html_with_footer = html_content.replace("</body>", footer)

    return html_with_footer
