# Copyright (c) CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com
#

"""
Auto-link API V4 Client

This module provides client functions for auto-link management using the v4 API
with Okta authentication and Pydantic models for clean data contracts.
"""

import httpx

from src.api.authenticated import get_authenticated, post_authenticated
from src.exceptions import APIError, CzOpsApiException
from src.schemas.autolink import (
    AutoLinkActionResponse,
    AutoLinkConfiguration,
    ConnectionAutoLinkStatus,
    EnableAutoLinkRequest,
    OrganizationAutoLinkStatus,
    OrganizationFailureAnalysis,
    TriageFailuresRequest,
    UpdateIgnoreListRequest,
    VerifyAutoLinkRoleRequest,
)


def get_connection_auto_link_status_v4(connection_id: str, debug: bool = False) -> ConnectionAutoLinkStatus:
    """
    Get auto-link status for a specific connection using V4 API.

    Args:
        connection_id (str): Connection ID to check
        debug (bool): Whether to print debug information

    Returns:
        ConnectionAutoLinkStatus: Auto-link status information

    Raises:
        CzOpsApiException: If the API request fails
    """
    endpoint = f"auto-link/connection/{connection_id}/status"

    try:
        response = get_authenticated(endpoint, debug=debug)
        return ConnectionAutoLinkStatus(**response)
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to get connection auto-link status: {str(e)}") from e


def get_organization_auto_link_status_v4(organization_id: str, debug: bool = False) -> OrganizationAutoLinkStatus:
    """
    Get auto-link status for all connections in an organization using V4 API.

    Args:
        organization_id (str): Organization ID to check
        debug (bool): Whether to print debug information

    Returns:
        OrganizationAutoLinkStatus: Auto-link status for all connections

    Raises:
        CzOpsApiException: If the API request fails
    """

    endpoint = f"auto-link/organization/{organization_id}/status"

    try:
        response = get_authenticated(endpoint, debug=debug)

        # Check if the response contains an error field
        if isinstance(response, dict) and "error" in response:
            error_msg = response.get("error", "Unknown error")
            details = response.get("details", "")
            raise CzOpsApiException(f"Auto-link status API error: {error_msg}. {details}")

        return OrganizationAutoLinkStatus(**response)
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to get organization auto-link status: {str(e)}") from e
    except CzOpsApiException:
        # Re-raise our own exceptions
        raise
    except Exception as e:
        # Handle validation errors and other unexpected errors
        raise CzOpsApiException(f"Failed to parse organization auto-link status: {str(e)}") from e


def enable_connection_auto_link_v4(connection_id: str, role_name: str) -> dict | None:
    """
    Enable auto-link for a connection using V4 API.

    The endpoint is idempotent and always applies the supplied role_name; if the role
    changes, the backend resets the connection's link_failure list for retry.

    Args:
        connection_id (str): Connection ID to enable auto-link for
        role_name (str): Path-qualified IAM role name for auto-link

    Returns:
        AutoLinkActionResponse: Response with outcome fields and updated configuration

    Raises:
        CzOpsApiException: If the API request fails
    """
    endpoint = f"auto-link/connection/{connection_id}/enable"
    request_data = EnableAutoLinkRequest(role_name=role_name)

    try:
        response = post_authenticated(endpoint, json_data=request_data.model_dump())
        return response
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to enable auto-link: {str(e)}") from e


def verify_auto_link_role_v4(cz_organization_id: str, role_arn: str, debug: bool = False) -> dict:
    """
    Verify that an IAM role ARN can be assumed for an organization using V4 API.

    The backend assumes the role via the reactor role chain using the organization's
    external_id. A successful assume proves the role is present in the ARN's account
    and that the account belongs to this org.

    Args:
        cz_organization_id (str): Organization to verify the role for
        role_arn (str): Full IAM role ARN to verify (the account is embedded in the ARN)
        debug (bool): Whether to print debug information

    Returns:
        dict: VerifyAutoLinkRoleResponse payload

    Raises:
        CzOpsApiException: If the API request fails
    """
    endpoint = f"auto-link/organization/{cz_organization_id}/verify-role"
    request_data = VerifyAutoLinkRoleRequest(role_arn=role_arn)

    try:
        return post_authenticated(endpoint, json_data=request_data.model_dump(), debug=debug)
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to verify auto-link role: {str(e)}") from e


def disable_connection_auto_link_v4(connection_id: str) -> dict | None:
    """
    Disable auto-link for a connection using V4 API.

    The endpoint is idempotent and takes no request body; existing
    linked/failed/ignored lists are preserved.

    Args:
        connection_id (str): Connection ID to disable auto-link for

    Returns:
        AutoLinkActionResponse: Response with updated configuration

    Raises:
        CzOpsApiException: If the API request fails
    """
    endpoint = f"auto-link/connection/{connection_id}/disable"

    try:
        response = post_authenticated(endpoint, json_data={})
        return response
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to disable auto-link: {str(e)}") from e


def update_connection_auto_link_ignore_list_v4(
    connection_id: str, ignore_list: list[str], action: str = "add"
) -> dict | None:
    """
    Update auto-link ignore list for a connection using V4 API.

    Args:
        connection_id (str): Connection ID to update ignore list for
        ignore_list (List[str]): Account IDs to ignore
        action (str): Action to perform ('add', 'remove', 'replace')

    Returns:
        AutoLinkActionResponse: Response with updated configuration

    Raises:
        CzOpsApiException: If the API request fails
    """
    endpoint = f"auto-link/connection/{connection_id}/ignore-list"

    request_data = UpdateIgnoreListRequest(ignore_list=ignore_list, action=action)

    try:
        response = post_authenticated(endpoint, json_data=request_data.model_dump())
        return response
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to update auto-link ignore list: {str(e)}") from e


def get_organization_failure_analysis_v4(organization_id: str, debug: bool = False) -> OrganizationFailureAnalysis:
    """
    Get failure analysis for all auto-link connections in an organization using V4 API.

    Args:
        organization_id: Organization ID to analyze
        debug: Whether to print debug information

    Returns:
        OrganizationFailureAnalysis: Cross-connection failure analysis

    Raises:
        CzOpsApiException: If the API request fails
    """
    endpoint = f"auto-link/organization/{organization_id}/failures"

    try:
        response = get_authenticated(endpoint, debug=debug)

        if isinstance(response, dict) and "error" in response:
            error_msg = response.get("error", "Unknown error")
            details = response.get("details", "")
            raise CzOpsApiException(f"Failure analysis API error: {error_msg}. {details}")

        return OrganizationFailureAnalysis(**response)
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to get organization failure analysis: {str(e)}") from e
    except CzOpsApiException:
        raise
    except Exception as e:
        raise CzOpsApiException(f"Failed to parse organization failure analysis: {str(e)}") from e


def triage_connection_failures_v4(
    connection_id: str, request: TriageFailuresRequest, debug: bool = False
) -> AutoLinkActionResponse:
    """
    Triage auto-link failures for a connection using V4 API.

    Args:
        connection_id: Connection ID to triage
        request: Triage request with action and optional account IDs
        debug: Whether to print debug information

    Returns:
        AutoLinkActionResponse: Response with triage result

    Raises:
        CzOpsApiException: If the API request fails
    """
    endpoint = f"auto-link/connection/{connection_id}/triage"

    try:
        response = post_authenticated(endpoint, json_data=request.model_dump(exclude_none=True), debug=debug)
        return AutoLinkActionResponse(**response)
    except (httpx.HTTPError, APIError) as e:
        raise CzOpsApiException(f"Failed to triage connection failures: {str(e)}") from e
    except CzOpsApiException:
        raise
    except Exception as e:
        raise CzOpsApiException(f"Failed to parse triage response: {str(e)}") from e


# Utility functions for CLI integration


def display_auto_link_configuration(config: AutoLinkConfiguration | None) -> str:
    """
    Format auto-link configuration for display.

    Args:
        config (Optional[AutoLinkConfiguration]): Auto-link configuration to display

    Returns:
        str: Formatted configuration display
    """
    if not config:
        return "Auto-link: Not configured"

    status = "Enabled" if config.enabled else "Disabled"
    lines = [f"Auto-link: {status}", f"Role Name: {config.role_name}"]

    if config.last_run:
        lines.append(f"Last Run: {config.last_run.isoformat()}")

    if config.next_run:
        lines.append(f"Next Run: {config.next_run.isoformat()}")

    # Statistics
    if (config.total_discovered or 0) > 0 or config.total_linked > 0 or config.total_failed > 0:
        lines.extend(
            [
                "",
                "Statistics:",
                f"  Discovered: {config.total_discovered}",
                f"  Linked: {config.total_linked}",
                f"  Failed: {config.total_failed}",
                f"  Ignored: {len(config.ignored_accounts)}",
            ]
        )

    # Account lists (only show first few for brevity)
    if config.linked_accounts:
        display_linked = config.linked_accounts[:3]
        suffix = f" (+{len(config.linked_accounts) - 3} more)" if len(config.linked_accounts) > 3 else ""
        lines.append(f"Linked Accounts: {', '.join(display_linked)}{suffix}")

    if config.failed_accounts:
        display_failed = config.failed_accounts[:3]
        suffix = f" (+{len(config.failed_accounts) - 3} more)" if len(config.failed_accounts) > 3 else ""
        lines.append(f"Failed Accounts: {', '.join(display_failed)}{suffix}")

    if config.ignored_accounts:
        display_ignored = config.ignored_accounts[:3]
        suffix = f" (+{len(config.ignored_accounts) - 3} more)" if len(config.ignored_accounts) > 3 else ""
        lines.append(f"Ignored Accounts: {', '.join(display_ignored)}{suffix}")

    return "\n".join(lines)


def display_connection_auto_link_status(status: ConnectionAutoLinkStatus) -> str:
    """
    Format connection auto-link status for display.

    Args:
        status (ConnectionAutoLinkStatus): Connection auto-link status to display

    Returns:
        str: Formatted status display
    """
    lines = [
        f"Connection: {status.connection_name or status.connection_id}",
        f"Provider: {status.provider}",
        f"Organization: {status.cz_organization_id}",
    ]

    if status.account_type:
        lines.append(f"Account Type: {status.account_type}")

    lines.append(f"Health Status: {'Healthy' if status.is_healthy else 'Unhealthy'}")
    lines.append("")
    lines.append(display_auto_link_configuration(status.auto_link))

    return "\n".join(lines)


def _format_account_cell(accounts: list[str], show_all: bool, max_display_rows: int = 15, sub_columns: int = 3) -> str:
    """Format a list of account IDs into a multi-sub-column cell string.

    Arranges account IDs in up to `sub_columns` columns per row to use horizontal
    space efficiently. Truncates to `max_display_rows` rows unless `show_all` is True.

    Args:
        accounts: Sorted list of account IDs.
        show_all: If True, display all accounts without truncation.
        max_display_rows: Maximum number of rows to display when truncating.
        sub_columns: Number of sub-columns to arrange accounts into.

    Returns:
        Formatted string of account IDs for use inside a Rich table cell.
    """
    if not accounts:
        return ""

    total = len(accounts)
    max_visible = max_display_rows * sub_columns if not show_all else total
    visible = accounts[:max_visible]
    truncated_count = total - len(visible)

    # Build rows of sub_columns accounts each
    lines = []
    for i in range(0, len(visible), sub_columns):
        chunk = visible[i : i + sub_columns]
        lines.append("  ".join(f"{a:<14}" for a in chunk))

    if truncated_count > 0:
        lines.append(f"... and {truncated_count} more (use --show-all to see all)")

    return "\n".join(lines)


def _extract_auto_link_config(conn: dict) -> AutoLinkConfiguration | None:
    """Extract AutoLinkConfiguration from a connection's types.COST.data.auto_link.

    The connection record stores auto-link data with different field names than
    the AutoLinkConfiguration schema:
        linked -> linked_accounts
        link_failure -> failed_accounts
        ignore -> ignored_accounts

    The auto_link_enabled flag is stored at the root of types.COST.data,
    NOT inside the auto_link dict.
    """
    cost_data = (conn.get("types") or {}).get("COST", {}).get("data", {})
    al_raw = cost_data.get("auto_link")
    if not al_raw or not isinstance(al_raw, dict):
        return None

    linked = al_raw.get("linked", [])
    failed = al_raw.get("link_failure", [])
    ignored = al_raw.get("ignore", [])

    # auto_link_enabled is at root of types.COST.data, not inside auto_link dict
    enabled = cost_data.get("auto_link_enabled", False)

    # Prefer stored total_discovered when available, fall back to computed
    raw_discovered = al_raw.get("total_discovered")
    total_discovered = raw_discovered if raw_discovered is not None else len(linked) + len(failed) + len(ignored)

    return AutoLinkConfiguration(
        role_name=al_raw.get("role_name"),
        enabled=enabled,
        last_run=al_raw.get("last_run"),
        next_run=al_raw.get("next_run"),
        created=al_raw.get("created"),
        updated_at=al_raw.get("updated_at"),
        linked_accounts=linked,
        failed_accounts=failed,
        ignored_accounts=ignored,
        total_discovered=total_discovered,
        total_linked=len(linked),
        total_failed=len(failed),
    )


def display_organization_auto_link_status(
    status: OrganizationAutoLinkStatus,
    show_all: bool = False,
    org_details: object | None = None,
    cost_connections: list[dict] | None = None,
) -> None:
    """
    Display organization auto-link status using Rich multi-column layout.

    Args:
        status: Organization auto-link status to display
        show_all: If True, show all account IDs without truncation
        org_details: Optional OrganizationDetails for enriched org summary
        cost_connections: Optional list of COST connection dicts for the org
    """
    from rich import box
    from rich.console import Console
    from rich.panel import Panel
    from rich.table import Table

    console = Console(width=None, highlight=False)

    def fmt_status(val, true_text, false_text):
        return f"[green]{true_text}[/]" if val else f"[red]{false_text}[/]"

    def fmt_ts(val):
        if not val:
            return "[dim]--[/]"
        s = str(val)
        if "T" in s:
            date_part = s.split("T")[0]
            time_part = s.split("T")[1].split(".")[0].split("+")[0]
            return f"{date_part} {time_part} UTC"
        return s

    def make_panel(title, rows, border="dim"):
        tbl = Table(box=None, show_header=False, padding=(0, 1), expand=True)
        tbl.add_column("K", style="bold blue", justify="right", no_wrap=True, width=18)
        tbl.add_column("V", style="white", overflow="fold", no_wrap=False)
        for label, value in rows:
            tbl.add_row(label, value)
        return Panel(tbl, title=f"[bold]{title}[/]", title_align="left", border_style=border, expand=True)

    def two_column_row(left, right):
        grid = Table.grid(expand=True)
        grid.add_column(ratio=1)
        grid.add_column(ratio=1)
        grid.add_row(left, right)
        console.print(grid)

    org_name = status.cz_organization_name or status.cz_organization_id

    # --- Organization Summary (enriched from org details when available) ---
    if org_details:
        from src.features.info import display_organization_summary

        display_organization_summary(status.cz_organization_id, org_details)
        console.print()
    else:
        # Minimal header when org details not available
        console.print(
            Panel(
                f"[bold cyan]{org_name}[/] ([cyan]{status.cz_organization_id}[/])",
                title="Auto-Link Status",
                title_align="left",
                border_style="cyan",
            )
        )

    # --- Row 1: Auto-Link Summary + Totals ---
    summary_rows = [
        ("Total Connections", str(status.total_connections)),
        ("Auto-Link Enabled", str(status.total_auto_link_enabled)),
        ("Last Updated", fmt_ts(status.last_updated)),
    ]

    totals_rows = [
        ("Linked Accounts", f"[green]{status.total_linked_accounts}[/]"),
        (
            "Failed Accounts",
            f"[red]{status.total_failed_accounts}[/]" if status.total_failed_accounts else "[green]0[/]",
        ),
        ("Ignored Accounts", str(status.total_ignored_accounts)),
    ]

    two_column_row(make_panel("Auto-Link Summary", summary_rows), make_panel("Totals", totals_rows))

    # --- COST Connections Overview (all COST connections with auto-link status) ---
    if cost_connections:
        # Build lookup of auto-link enabled connections for enrichment
        enabled_conns = status.auto_link_enabled_connections or {}

        conn_table = Table(
            box=box.SIMPLE_HEAVY,
            show_header=True,
            header_style="bold cyan",
            expand=True,
            padding=(0, 1),
        )
        conn_table.add_column("Connection ID", style="yellow", width=16)
        conn_table.add_column("Name", style="white")
        conn_table.add_column("Enabled", width=10)
        conn_table.add_column("Auto-Link", width=12)
        conn_table.add_column("Healthy", width=10)
        conn_table.add_column("Linked", justify="right", width=8)
        conn_table.add_column("Failed", justify="right", width=8)
        conn_table.add_column("Ignored", justify="right", width=8)

        for conn in sorted(cost_connections, key=lambda c: c.get("connection_name") or c.get("connection_id", "")):
            conn_id = conn.get("connection_id", "")
            conn_name_display = conn.get("connection_name") or conn_id

            # Get auto-link config: prefer status API, fall back to connection record
            al_status = enabled_conns.get(conn_id)
            al_config = al_status.auto_link if al_status else _extract_auto_link_config(conn)

            is_al_enabled = (al_status.auto_link_enabled if al_status else False) or conn.get(
                "auto_link_enabled", False
            )
            is_conn_enabled = conn.get("enabled", True)

            linked_count = str(len(al_config.linked_accounts)) if al_config else "[dim]--[/]"
            failed_count = (
                f"[red]{len(al_config.failed_accounts)}[/]"
                if al_config and al_config.failed_accounts
                else "[green]0[/]"
                if al_config
                else "[dim]--[/]"
            )
            ignored_count = str(len(al_config.ignored_accounts)) if al_config else "[dim]--[/]"

            conn_table.add_row(
                conn_id,
                conn_name_display,
                fmt_status(is_conn_enabled, "Yes", "No"),
                fmt_status(is_al_enabled, "Enabled", "Disabled"),
                fmt_status(conn.get("is_healthy", False), "Healthy", "Unhealthy"),
                linked_count,
                failed_count,
                ignored_count,
            )

        console.print(
            Panel(
                conn_table,
                title="[bold]COST Connections — Auto-Link Status[/]",
                title_align="left",
                border_style="dim",
                expand=True,
            )
        )

    # --- Per-Connection Details (all COST connections, not just auto-link enabled) ---
    enabled_conns = status.auto_link_enabled_connections or {}

    # Build unified list: all cost connections enriched with auto-link data
    detail_connections: list[tuple[str, str, str, bool, bool, AutoLinkConfiguration | None]] = []
    if cost_connections:
        for conn in cost_connections:
            conn_id = conn.get("connection_id", "")
            conn_name = conn.get("connection_name") or conn_id
            provider = conn.get("provider", "Unknown")
            is_healthy = conn.get("is_healthy", False)
            al_status = enabled_conns.get(conn_id)
            is_al_enabled = (al_status.auto_link_enabled if al_status else False) or conn.get(
                "auto_link_enabled", False
            )
            al_config = al_status.auto_link if al_status else _extract_auto_link_config(conn)
            detail_connections.append((conn_id, conn_name, provider, is_healthy, is_al_enabled, al_config))
    else:
        # Fallback: show only auto-link enabled connections if cost_connections not available
        for conn_id, conn_status in enabled_conns.items():
            al_config = conn_status.auto_link
            detail_connections.append(
                (
                    conn_id,
                    conn_status.connection_name or conn_id,
                    conn_status.provider,
                    conn_status.is_healthy,
                    conn_status.auto_link_enabled,
                    al_config,
                )
            )

    for conn_id, conn_name, provider, is_healthy, is_al_enabled, al in sorted(detail_connections, key=lambda c: c[1]):
        # Connection info panel
        info_rows = [
            ("Connection", f"{conn_name} ({conn_id})"),
            ("Provider", provider),
            ("Auto-Link", fmt_status(is_al_enabled, "Enabled", "Disabled")),
            ("Healthy", fmt_status(is_healthy, "Yes", "No")),
        ]
        if al:
            info_rows.append(("Role Name", al.role_name or "[dim]--[/]"))
            info_rows.append(("Created", fmt_ts(al.created)))
            info_rows.append(("Updated", fmt_ts(al.updated_at)))
            info_rows.append(("Last Run", fmt_ts(al.last_run)))
            info_rows.append(("Next Run", fmt_ts(al.next_run)))

        # Account counts panel
        counts_rows = []
        if al:
            counts_rows.append(("Linked", f"[green]{len(al.linked_accounts)}[/]"))
            counts_rows.append(
                ("Failed", f"[red]{len(al.failed_accounts)}[/]" if al.failed_accounts else "[green]0[/]")
            )
            counts_rows.append(("Ignored", str(len(al.ignored_accounts))))
            if al.total_discovered:
                counts_rows.append(("Discovered", str(al.total_discovered)))
        else:
            counts_rows.append(("Status", "[dim]No auto-link configuration[/]"))

        two_column_row(make_panel(f"Connection: {conn_name}", info_rows), make_panel("Accounts", counts_rows))

        # --- Account lists (compact multi-column cells with truncation) ---
        if not al:
            continue

        linked = sorted(al.linked_accounts)
        failed = sorted(al.failed_accounts)
        ignored = sorted(al.ignored_accounts)

        max_rows = max(len(linked), len(failed), len(ignored))

        if max_rows > 0:
            acct_table = Table(
                box=box.SIMPLE_HEAVY,
                show_header=True,
                header_style="bold cyan",
                expand=True,
                padding=(0, 2),
            )
            acct_table.add_column(f"Linked ({len(linked)})", style="green")
            acct_table.add_column(f"Failed ({len(failed)})", style="red")
            acct_table.add_column(f"Ignored ({len(ignored)})", style="yellow")

            acct_table.add_row(
                _format_account_cell(linked, show_all),
                _format_account_cell(failed, show_all),
                _format_account_cell(ignored, show_all),
            )

            console.print(
                Panel(acct_table, title="[bold]Account Details[/]", title_align="left", border_style="dim", expand=True)
            )


def display_organization_failure_analysis(
    analysis: OrganizationFailureAnalysis, org_name: str | None = None, show_all: bool = False
) -> None:
    """
    Display organization failure analysis using Rich layout.

    Args:
        analysis: Organization failure analysis data
        org_name: Optional organization name for display
        show_all: If True, show all account IDs without truncation
    """
    from rich import box
    from rich.console import Console
    from rich.panel import Panel
    from rich.table import Table

    console = Console(width=None, highlight=False)

    display_name = org_name or analysis.cz_organization_id

    # --- Header ---
    console.print(
        Panel(
            f"[bold cyan]{display_name}[/] ([cyan]{analysis.cz_organization_id}[/])",
            title="Auto-Link Failure Analysis",
            title_align="left",
            border_style="cyan",
        )
    )

    # --- Summary ---
    failing_all_count = len(analysis.accounts_failing_all_connections)
    failing_multiple_count = len(analysis.accounts_failing_multiple)

    summary_table = Table(box=None, show_header=False, padding=(0, 1), expand=True)
    summary_table.add_column("K", style="bold blue", justify="right", no_wrap=True, width=30)
    summary_table.add_column("V", style="white")
    summary_table.add_row("Connections Analyzed", str(analysis.total_connections_analyzed))
    summary_table.add_row("Unique Failed Accounts", str(analysis.total_unique_failed_accounts))
    summary_table.add_row(
        "Failing ALL Connections",
        f"[red]{failing_all_count}[/]" if failing_all_count else "[green]0[/]",
    )
    summary_table.add_row(
        "Failing Multiple Connections",
        f"[yellow]{failing_multiple_count}[/]" if failing_multiple_count else "[green]0[/]",
    )

    console.print(Panel(summary_table, title="[bold]Summary[/]", title_align="left", border_style="dim", expand=True))

    # Check if there are no failures at all
    if (
        analysis.total_unique_failed_accounts == 0
        and not analysis.accounts_failing_all_connections
        and not analysis.accounts_failing_multiple
        and all(cs.total_failed == 0 for cs in analysis.per_connection_summary)
    ):
        console.print(Panel("[bold green]No failures detected across any connections.[/]", border_style="green"))
        return

    # --- Accounts Failing ALL Connections ---
    if analysis.accounts_failing_all_connections:
        all_table = Table(
            box=box.SIMPLE_HEAVY,
            show_header=True,
            header_style="bold red",
            expand=True,
        )
        all_table.add_column("Account ID", style="red")
        all_table.add_column("Failure Count", justify="right")
        all_table.add_column("Connections")

        for detail in analysis.accounts_failing_all_connections:
            all_table.add_row(
                detail.account_id,
                str(detail.failure_count),
                ", ".join(detail.connections),
            )

        console.print(
            Panel(
                all_table,
                title="[bold red]Accounts Failing ALL Connections (systemic issues)[/]",
                title_align="left",
                border_style="red",
                expand=True,
            )
        )

    # --- Accounts Failing Multiple Connections ---
    if analysis.accounts_failing_multiple:
        multi_table = Table(
            box=box.SIMPLE_HEAVY,
            show_header=True,
            header_style="bold yellow",
            expand=True,
        )
        multi_table.add_column("Account ID", style="yellow")
        multi_table.add_column("Failure Count", justify="right")
        multi_table.add_column("Connections")

        for detail in analysis.accounts_failing_multiple:
            multi_table.add_row(
                detail.account_id,
                str(detail.failure_count),
                ", ".join(detail.connections),
            )

        console.print(
            Panel(
                multi_table,
                title="[bold yellow]Accounts Failing Multiple Connections[/]",
                title_align="left",
                border_style="yellow",
                expand=True,
            )
        )

    # --- Per-Connection Summary ---
    if analysis.per_connection_summary:
        conn_table = Table(
            box=box.SIMPLE_HEAVY,
            show_header=True,
            header_style="bold cyan",
            expand=True,
        )
        conn_table.add_column("Connection", style="white")
        conn_table.add_column("Linked", justify="right", style="green")
        conn_table.add_column("Failed", justify="right", style="red")
        conn_table.add_column("Ignored", justify="right", style="yellow")
        conn_table.add_column("Failed Account IDs")

        for cs in analysis.per_connection_summary:
            conn_name = cs.connection_name or cs.connection_id
            failed_ids = _format_account_cell(sorted(cs.failed_account_ids), show_all)
            conn_table.add_row(
                conn_name,
                str(cs.total_linked),
                str(cs.total_failed),
                str(cs.total_ignored),
                failed_ids,
            )

        console.print(
            Panel(
                conn_table,
                title="[bold]Per-Connection Summary[/]",
                title_align="left",
                border_style="dim",
                expand=True,
            )
        )
