# 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
#

import csv
import sys
from collections import Counter
from dataclasses import dataclass
from pathlib import Path
from shutil import get_terminal_size
from typing import Any

from rich import box
from rich.console import Console
from rich.panel import Panel
from rich.pretty import pprint
from rich.table import Table

from src.constants import CONNECTION_PROPERTIES, CONNECTION_TYPES, NO_ACCOUNT_NAME, NO_ORG_NAME
from src.utils.connections import ConnectionUtils
from src.utils.ui import HealthStatus, PanelFactory, ProgressFactory, TableFactory

# CUR Feature mappings - map AdditionalSchemaElements to single-character abbreviations
CUR_FEATURE_MAPPINGS = {
    "RESOURCES": "R",  # Resource IDs
    "RESOURCE_TAGS": "T",  # Tags
    "SPLIT_COST_ALLOCATION_DATA": "S",  # Split cost allocation
    "MANUAL_DISCOUNT_COMPATIBILITY": "M",  # Manual discount compatibility
    "AUTOMATION": "A",  # Automation
    "ENHANCED_LINEITEM_PRICING": "E",  # Enhanced line item pricing
}

# Get actual terminal width or default to 180 if detection fails
try:
    terminal_width = get_terminal_size().columns
except OSError:
    # Fallback in case terminal size detection fails
    terminal_width = 180

# Use the full terminal width
# Initialize console with dynamic width
console = Console(width=None, highlight=False)


def _get_connection_composite_key(connection: dict[str, Any]) -> str:
    """
    Generate a composite key for a connection to handle cases where
    the same connection_id might appear with different types.

    Uses connection_id|connection_type as the composite key.
    This prevents duplicate connection_ids from overwriting each other.
    """
    conn_id = connection.get("connection_id")
    conn_type = connection.get("connection_type")

    if not conn_id or not conn_type:
        raise ValueError(
            f"Missing required fields for composite key: connection_id={conn_id}, connection_type={conn_type}"
        )

    return f"{conn_id}|{conn_type}"


@dataclass
class ConnectionMetrics:
    total_connections: int = 0
    malformed: int = 0
    enabled_orgs: int = 0
    disabled_orgs: int = 0
    cloud_providers: set[str] = None
    stages: Counter = None

    def __post_init__(self):
        self.cloud_providers = set()
        self.stages = Counter()


class ConnectionManager:
    def __init__(self, organization_data=None):
        self.metrics = ConnectionMetrics()
        self.organization_data = organization_data or {}

    def _create_organization_header(
        self, org_name: str, org_id: str, org_stage: str, org_enabled: bool = True
    ) -> Panel:
        """Create organization header panel."""
        return PanelFactory.create_organization_header_panel(
            org_name=org_name, org_id=org_id, org_stage=org_stage, org_enabled=org_enabled
        )

    def _create_connection_table(self, org_title: str) -> Table:
        """Create main connection details table."""
        columns = [
            ("", {"justify": "center"}),  # Status icon only
            ("Connection Name", {"justify": "left", "no_wrap": False, "overflow": "fold"}),
            ("Connection ID", {"justify": "left", "no_wrap": False, "overflow": "fold"}),
            ("Provider", {"justify": "left", "no_wrap": False, "overflow": "fold"}),
            ("Connection Type", {"justify": "left", "no_wrap": False, "overflow": "fold"}),
            ("Health", {"justify": "left", "no_wrap": False, "overflow": "fold"}),
            ("First Seen", {"justify": "left", "no_wrap": False, "overflow": "fold"}),
            ("30d Spend", {"justify": "right", "no_wrap": False, "overflow": "fold"}),
        ]

        return TableFactory.create_connections_table(
            title=org_title,
            columns=columns,
            width=None,  # Use full terminal width
        )

    def _get_connection_status(self, connection: dict[str, Any]) -> tuple[list[dict], list[dict]]:
        """Process and return connection status."""
        try:
            if connection.get("malformed"):
                return (
                    [
                        {
                            "code": "BAD",
                            "name": "BAD",
                            "healthy": False,
                            "display": "⚠️ [bright_red]BAD[/]",
                            "description": "Malformed connection record",
                        }
                    ],
                    [],
                )

            connection_type_status = []
            connection_prop_status = []

            # Process connection types
            for _k, v in CONNECTION_TYPES.items():
                if v["type_key"] in connection.get("connection_types", []):
                    healthy = (
                        connection.get("is_healthy")
                        if connection["connection_type"] != "AWS"
                        else connection.get("link_health", {}).get(v["health_key"], {}).get("is_healthy")
                    )

                    connection_type_status.append(
                        {
                            "code": v["code"],
                            "name": v["name"],
                            "healthy": healthy,
                            "display": f"[bright_green]{v['code']}[/]" if healthy else f"[bright_red]{v['code']}[/]",
                            "description": v["description"],
                        }
                    )

            # Process connection properties
            for _k, v in CONNECTION_PROPERTIES.items():
                if self._should_add_property(connection, v):
                    connection_prop_status.append(
                        {
                            "code": v["code"],
                            "name": v["name"],
                            "healthy": True,
                            "display": f"[deep_sky_blue1]{v['code']}[/]",
                            "description": v["description"],
                        }
                    )

            return connection_type_status, connection_prop_status

        except Exception as error:
            console.print(
                f"[red]Error processing status for '{connection.get('connection_name')} ({connection.get('connection_id')})':[/] {error}"
            )
            return [], []

    def _should_add_property(self, account: dict[str, Any], property_config: dict[str, Any]) -> bool:
        """Determine if a property should be added to the status."""
        if account.get(property_config["enabled_key"]):
            return True

        if provisioning_keywords := property_config.get("provisioning_keywords"):
            if provisioning_keywords[0] == "!" and account.get("provisioning_process") not in provisioning_keywords[1:]:
                return True

        return False

    def export_to_csv(
        self, output_file: str, connection_data: dict[str, Any], organization_data: dict[str, Any]
    ) -> None:
        """Export connection data to CSV file."""
        headers = [
            "cz_organization_id",
            "organization_name",
            "connection_name",
            "connected_date",
            "status",
            "account_type",
            "connections",
            "role_healthy",
            "ingest_healthy",
            "provisioned",
            "connection_type",
            "connection_id",
        ]

        try:
            output_path = Path(output_file)
            record_count = 0

            with ProgressFactory.create_export_progress() as progress:
                export_task = progress.add_task(
                    f"[cyan]Exporting to {output_file}...",
                    total=sum(len(conns) for conns in connection_data.values() if conns),
                )

                with output_path.open("w", newline="") as csv_fp:
                    writer = csv.DictWriter(csv_fp, fieldnames=headers, dialect="excel", quoting=csv.QUOTE_ALL)
                    writer.writeheader()

                    for org_id, connections_dict in connection_data.items():
                        if not connections_dict:
                            continue

                        org_name = organization_data.get(org_id, {}).get("cz_organization_name", NO_ORG_NAME)

                        for connection in connections_dict.values():
                            row = self._prepare_csv_row(org_id, org_name, connection)
                            writer.writerow(row)
                            record_count += 1
                            progress.advance(export_task)

            console.print(f"[green]Export complete: {record_count} records written to {output_file}")

        except OSError as error:
            console.print(f"[red]Error writing to CSV file: {error}")
            sys.exit(1)
        except Exception as error:
            console.print(f"[red]Unexpected error during CSV export: {error}")
            sys.exit(1)

    def _prepare_csv_row(self, org_id: str, org_name: str, connection: dict[str, Any]) -> dict[str, Any]:
        """Prepare a single row for CSV export with plain text (no formatting, colors, or emojis)."""
        c_type_objs, c_props_objs = self._get_connection_status(connection)

        # Extract plain text codes for types and properties without rich formatting
        c_type = [obj["code"] for obj in c_type_objs]
        c_props = [obj["code"] for obj in c_props_objs]

        return {
            "cz_organization_id": org_id,
            "cz_organization_name": org_name,
            "connection_name": f'="{connection.get("connection_name", NO_ACCOUNT_NAME)}"',
            "connected_date": connection.get("created"),
            "status": "connected" if connection["is_connected"] else "disconnected",
            "account_type": ",".join(c_type) if c_type else "",
            "properties": ",".join(c_props) if c_props else "",
            "role_healthy": connection.get("is_healthy"),
            "ingest_healthy": not bool(connection.get("resource_ingest_permissions_errors")),
            "provisioned": connection.get("provisioning_process") or "legacy",
            "provider": connection.get("provider"),
            "connection_id": f'="{connection["connection_id"]}"',
        }

    def print_connection_list(self, organization_data: dict[str, Any], connection_data: dict[str, Any]):
        """Print formatted connection list with improved organization display."""
        # Reset metrics for accurate counting
        self.metrics = ConnectionMetrics()

        # Store organization data for use in other methods
        self.organization_data = organization_data or {}

        # Process each organization
        with ProgressFactory.create_spinner_progress(
            "Processing organizations", total=len(connection_data), console=console
        ) as progress:
            task = progress.add_task("Processing...", total=len(connection_data))

            # Sort organizations by name for consistent display
            sorted_orgs = sorted(
                [(cz_org_id, organization_data.get(cz_org_id, {})) for cz_org_id in connection_data],
                key=lambda x: x[1].get("cz_organization_name", "").lower(),
            )

            for cz_org_id, org_info in sorted_orgs:
                connections_dict = connection_data.get(cz_org_id, {})
                if not connections_dict:
                    continue

                # Process organization information
                org_name = org_info.get("cz_organization_name", "").capitalize() or cz_org_id
                org_enabled = org_info.get("organization_enabled", True)
                org_stage = org_info.get("organization_stage", "unknown")

                self._update_metrics(org_enabled, org_stage)

                # Process connections (organization header is now created within this method)
                self._process_connections(connections_dict, org_name, cz_org_id, org_stage)

                progress.advance(task)

        # Print summary after all organizations have been processed
        self._print_summary()

    def _update_metrics(self, org_enabled: bool, org_stage: str):
        """Update metrics based on organization information."""
        self.metrics.stages[f"stage-{org_stage}"] += 1
        if org_enabled:
            self.metrics.enabled_orgs += 1
        else:
            self.metrics.disabled_orgs += 1

    def _process_connections(self, connections_dict: dict[str, Any], org_name: str, org_id: str, org_stage: str):
        """Process and display connections for an organization with improved formatting."""
        connections = list(connections_dict.values()) if connections_dict else []

        # Get organization enabled status from the organization data
        org_info = self.organization_data.get(org_id, {})
        # Handle both Pydantic models and dicts
        if hasattr(org_info, "model_dump"):
            org_info = org_info.model_dump()
        org_enabled = org_info.get("organization_enabled", True)

        # Sort connections by status (Connected first, then Partial, then Disconnected)
        sorted_connections = sorted(
            connections, key=lambda c: (not c.get("is_connected", False), bool(c.get("permission_errors", False)))
        )

        # Create a more descriptive table title
        connection_count = len(connections)

        # Simplified title
        table_title = f"{connection_count} Connections"

        # Create and populate the connection table
        connection_table = self._create_connection_table(table_title)

        if not connections:
            connection_table.add_row("[yellow]No connections found[/]", "", "", "", "", "", "", "")
        else:
            for connection in sorted_connections:
                self._add_connection_row(connection_table, connection)

        # Print the organization information and connections table
        console.print()
        console.print(self._create_organization_header(org_name, org_id, org_stage, org_enabled))

        console.print(connection_table)
        console.print()

    def _add_connection_row(self, table: Table, connection: dict[str, Any]):
        """Add a connection row to the table with improved formatting."""
        # Compute is_connected if not present
        if "is_connected" not in connection:
            connection["is_connected"] = connection.get("enabled", True) and connection.get("is_healthy", True)
        self.metrics.total_connections += 1
        self.metrics.cloud_providers.add(connection.get("provider", "unknown"))

        # Connection Name
        connection_name_display = connection.get("connection_name") or "[dim]-[/]"

        # Connection ID
        account_id_display = connection.get("connection_id", "")

        # Connection Type - show all types from connection_types list
        connection_types = connection.get("connection_types", [])
        connection_type_display = ", ".join(connection_types) if connection_types else "[dim]-[/]"

        # Health status - simplified
        health_display = self._get_health_summary(connection)

        # Provider - show the actual cloud provider with color coding
        provider_raw = connection.get("provider") or connection.get("cloud_provider", "")
        if provider_raw:
            provider_upper = provider_raw.upper()
            if provider_upper == "AWS":
                provider_display = "[orange1]AWS[/]"
            elif provider_upper == "GCP":
                # Rainbow effect for GCP - using different colors for each letter
                provider_display = "[red]G[/][yellow]C[/][blue]P[/]"
            elif provider_upper == "AZURE":
                provider_display = "[blue]AZURE[/]"
            elif provider_upper == "SNOWFLAKE":
                provider_display = "[cornflower_blue]SNOWFLAKE[/]"
            else:
                provider_display = provider_raw
        else:
            provider_display = "[dim]-[/]"

        # First Seen - format as YYYY-MM-DD HH:MM TZ
        first_seen = connection.get("first_seen")
        if first_seen and first_seen != "None":
            try:
                # Parse ISO8601 datetime and format as requested
                from datetime import datetime

                if isinstance(first_seen, str):
                    # Try to parse the ISO format
                    dt = datetime.fromisoformat(first_seen.replace("Z", "+00:00"))
                    first_seen_display = dt.strftime("%Y-%m-%d %H:%M %Z") or dt.strftime("%Y-%m-%d %H:%M UTC")
                else:
                    first_seen_display = str(first_seen)
            except Exception:
                # If parsing fails, just use the raw value
                first_seen_display = str(first_seen)
        else:
            first_seen_display = "[dim]-[/]"

        # 30-day spend - stored in types.COST.data.last_30_days_billed_spend
        cost_data = (connection.get("types") or {}).get("COST", {}).get("data", {})
        spend_value = cost_data.get("last_30_days_billed_spend")

        if spend_value:
            try:
                spend_float = float(spend_value) if isinstance(spend_value, str) else spend_value
                spend_display = f"${spend_float:,.2f}"
            except (ValueError, TypeError):
                spend_display = "[dim]$0.00[/]"
        else:
            spend_display = "[dim]$0.00[/]"

        # Build row data (Provider before Connection Type)
        row_data = [
            self._get_connection_status_display(connection),
            connection_name_display,
            account_id_display,
            provider_display,
            connection_type_display,
            health_display,
            first_seen_display,
            spend_display,
        ]

        table.add_row(*row_data)

    def _get_connection_status_display(self, connection: dict[str, Any]) -> str:
        """Get the display status icon only for a connection."""
        return HealthStatus.get_connection_status_icon(connection)

    def _get_health_summary(self, connection: dict[str, Any]) -> str:
        """Get a simplified health summary for the connection."""
        is_healthy = connection.get("is_healthy", False)
        is_connected = connection.get("is_connected")
        if is_connected is None:
            is_connected = connection.get("enabled", True) and is_healthy
        has_permission_errors = connection.get("permission_errors", False)

        if not is_connected:
            return "[red]Disconnected[/]"
        elif not is_healthy:
            return "[yellow]Unhealthy[/]"
        elif has_permission_errors:
            return "[yellow]Partial[/]"
        else:
            return "[green]Healthy[/]"

    def _get_simplified_resource_display(self, connection: dict[str, Any]) -> str:
        """Get simplified resource collection display."""
        rc_configs = connection.get("resource_collector_configurations")
        if not rc_configs:
            return "[dim]-[/]"

        parts = []
        if rc_configs.get("cwm", False):
            parts.append("[green]CWM[/]")

        if probes := rc_configs.get("probes", []):
            # Show count and first few probe names
            if len(probes) <= 3:
                parts.append(f"[blue]{','.join(probes)}[/]")
            else:
                parts.append(f"[blue]{len(probes)} probes[/]")

        return " ".join(parts) if parts else "[dim]Enabled[/]"

    def _print_summary(self):
        """Print compact summary information."""
        if self.metrics.total_connections > 0:
            # Simple one-line summary
            providers = sorted(self.metrics.cloud_providers)
            provider_text = ", ".join(providers)

            summary_text = f"[cyan]{self.metrics.total_connections}[/cyan] connections from {len(self.metrics.cloud_providers)} providers ({provider_text})"

            legend_text = "🟢=Healthy 🟡=Partial 🔴=Disconnected | "

            console.print(f"\n{summary_text}")
            console.print(f"[dim]{legend_text}[/dim]\n")


def create_organization_summary(organization_data: dict[str, Any], connection_data: dict[str, dict[str, Any]]):
    """Create a summary table of organizations and their connections with dynamic column widths."""
    from shutil import get_terminal_size

    from rich.table import Table

    # Detect terminal width with a safe fallback
    try:
        term_width = get_terminal_size().columns
    except OSError:
        term_width = 120

    # Establish a minimum usable width and guardrails for very narrow terminals
    min_width = 80
    effective_width = max(term_width, min_width)

    # Compute dynamic column widths proportionally
    # Fixed columns: Org ID (no_wrap), Total (5, no_wrap)
    # The rest are proportional to remaining space
    fixed_total_col_width = 5
    # Reserve some space (padding/borders). Rich will handle borders, but keep a safety margin
    safety_padding = 6

    remaining = max(effective_width - fixed_total_col_width - safety_padding, 40)

    # Proportions for each column (must sum to 1.0)
    # Organization, Payer Connections, CUR Features, [30 Day Spend], Resource Collection
    org_ratio = 0.22
    payer_ratio = 0.38
    cur_ratio = 0.10
    spend_ratio = 0.15
    rc_ratio = 0.15

    # Calculate widths
    org_w = max(18, int(remaining * org_ratio))
    payer_w = max(24, int(remaining * payer_ratio))
    cur_w = max(10, int(remaining * cur_ratio))
    spend_w = max(12, int(remaining * spend_ratio))
    rc_w = max(14, int(remaining * rc_ratio))

    # Create summary table
    summary_table = Table(
        title="Organization Connections Summary",
        title_style="bold cyan",
        title_justify="left",
        box=box.ROUNDED,
        header_style="bold white",
        width=None,  # Use full terminal width
    )

    # Define columns with dynamic widths
    summary_table.add_column("Org ID", no_wrap=True)
    summary_table.add_column("Organization", width=org_w, overflow="fold")
    summary_table.add_column("Payer Connections", width=payer_w, overflow="fold")
    summary_table.add_column("CUR Features", width=cur_w, overflow="fold")
    summary_table.add_column("30 Day Cloud Spend", justify="right", width=spend_w, overflow="fold")
    summary_table.add_column("Resource Collection", width=rc_w, overflow="fold")
    summary_table.add_column("Total", justify="right", width=fixed_total_col_width, no_wrap=True)

    # Sort organizations by name
    sorted_orgs = sorted(organization_data.items(), key=lambda x: x[1].get("organization_name", "").lower())

    # Process each organization (now sorted by name)
    for org_id, org_info in sorted_orgs:
        org_connections = connection_data.get(org_id, {})

        if not org_connections:
            continue

        # Organization name and ID
        org_name = org_info.get("organization_name", "Unknown")
        org_info.get("organization_enabled", True)

        # Format org status display

        # Connection counts
        connection_count = len(org_connections)

        # Identify payer accounts and connection types
        payer_accounts = []
        connection_types = set()

        # 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
        #

        # Variables for health calculations
        healthy_count = 0
        unhealthy_count = 0

        # Variables for resource collection tracking
        resource_collection_enabled_count = 0
        cwm_enabled_count = 0
        probes_enabled_count = 0
        probe_types = set()

        # Variables for CUR features tracking
        cur_features = set()  # Track all CUR features across payer accounts

        # Variable for 30 day spend tracking
        total_30_day_spend = 0.0

        for _conn_id, conn in org_connections.items():
            is_payer = "COST" in conn.get("connection_types", [])

            if is_payer:
                payer_accounts.append(conn.get("connection_id", "Unknown"))

                # Extract CUR features for payer accounts
                if cur_metadata := conn.get("cur_metadata"):
                    # Get the active CUR report name
                    active_cur = conn.get("master_payer_billing_report_name")
                    if active_cur and active_cur in cur_metadata:
                        # Get the AdditionalSchemaElements from the active CUR
                        if schema_elements := cur_metadata[active_cur].get("AdditionalSchemaElements", []):
                            for element in schema_elements:
                                if element in CUR_FEATURE_MAPPINGS:
                                    cur_features.add(CUR_FEATURE_MAPPINGS[element])

            # Record connection types
            if conn.get("cloud_provider"):
                connection_types.add(conn.get("cloud_provider"))

            # Calculate health status
            is_healthy = conn.get("is_healthy", False)
            is_connected = conn.get("is_connected", False)
            has_permission_errors = conn.get("permission_errors", False)

            if is_connected and is_healthy and not has_permission_errors:
                healthy_count += 1
            elif is_connected:
                # This counts partial connections (connected but with issues)
                unhealthy_count += 1

            # Track resource collection status - determined solely by the presence of resource_collector_configurations
            if rc_configs := conn.get("resource_collector_configurations"):
                resource_collection_enabled_count += 1

                # Check for CloudWatch Metrics
                if rc_configs.get("cwm", False):
                    cwm_enabled_count += 1

                # Check for probes
                if probes := rc_configs.get("probes", []):
                    probes_enabled_count += 1
                    # Track all probe types
                    for probe in probes:
                        probe_types.add(probe)

            # Check for last_30_days_billed_spend in types.COST.data
            cost_data = (conn.get("types") or {}).get("COST", {}).get("data", {})
            if spend_str := cost_data.get("last_30_days_billed_spend"):
                try:
                    total_30_day_spend += float(spend_str)
                except (ValueError, TypeError):
                    pass

        # Determine health icon based on status
        health_icon = HealthStatus.determine_overall_health_icon(
            healthy_count=healthy_count, unhealthy_count=unhealthy_count, total_count=connection_count
        )

        # Create combined connections info display
        connections_info = ConnectionUtils.format_connection_info_list(
            payer_accounts=payer_accounts, org_connections=org_connections
        )

        # If no payers found
        if not connections_info:
            connections_info = ["None"]

        # 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
        #

        connections_display = health_icon + " " + " | ".join(connections_info)

        # Create CUR features display - sort features alphabetically for consistent display
        if cur_features:
            # Map each feature to its color
            feature_colors = {
                "R": "green",  # Resources
                "T": "blue",  # Tags
                "S": "yellow",  # Split Cost Allocation
                "M": "magenta",  # Manual Discount
                "A": "cyan",  # Automation
                "E": "red",  # Enhanced Pricing
            }

            # Build display with individual colors
            colored_features = []
            for feature in sorted(cur_features):
                color = feature_colors.get(feature, "cyan")  # Default to cyan if unknown
                colored_features.append(f"[{color}]{feature}[/]")

            cur_features_display = "".join(colored_features)
        else:
            cur_features_display = "[dim]None[/]"

        # Create resource collection display
        if resource_collection_enabled_count == 0:
            rc_display = "[dim]None[/]"
        else:
            rc_display = f"[green]{resource_collection_enabled_count}/{connection_count}[/]"

            # Add CloudWatch metrics status if any are enabled
            if cwm_enabled_count > 0:
                rc_display += f" [green]CWM:{cwm_enabled_count}[/]"

            # Add probe status if any are enabled
            if probes_enabled_count > 0:
                probe_text = ", ".join(sorted(probe_types)) if probe_types else ""
                if len(probe_text) > 15:
                    # Truncate if too many probe types
                    probe_text = probe_text[:12] + "..."
                rc_display += f" [blue]P:{probes_enabled_count}[/]"

        # Format 30-day spend for display
        spend_display = f"[green]${total_30_day_spend:,.2f}[/]" if total_30_day_spend > 0 else "[dim]$0.00[/]"

        # Add row to the table
        row_data = [
            org_id,  # Now first column (never truncated)
            org_name,
            connections_display,
            cur_features_display,  # CUR features column
            spend_display,  # 30 day spend column
            rc_display,  # Resource collection status
            str(connection_count),  # Now last column
        ]

        summary_table.add_row(*row_data)

    # Print the summary
    console.print()
    console.print(
        PanelFactory.create_welcome_panel(
            description="[bold]CloudZero Connection Management[/bold]\nOrganization-level summary of connections",
            title="Connection Summary",
            style="green",
        )
    )
    console.print(summary_table)
    console.print()


# 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
#


def display_connections_for_organization(
    org_id: str, organization_data: dict[str, Any], connection_data: dict[str, Any], show_header: bool = True
):
    """
    Reusable function to display connections for a single organization.

    This function is used by both 'czops conn list' and 'czops org info' to ensure
    consistent connection display across commands.

    Args:
        org_id: Organization ID
        organization_data: Dictionary containing organization information
        connection_data: Dictionary of connections for the organization
        show_header: Whether to show the organization header (default: True)
    """
    manager = ConnectionManager(organization_data)

    org_info = organization_data.get(org_id, {})
    # Handle both Pydantic models and dicts
    if hasattr(org_info, "model_dump"):
        org_info = org_info.model_dump()
    org_name = org_info.get("cz_organization_name", "").capitalize() or org_id
    org_stage = org_info.get("organization_stage", "unknown")

    # Process and display connections
    manager._process_connections(connection_data, org_name, org_id, org_stage)


def list_functions(arguments: dict[str, Any], organization_data: dict[str, Any] | None = None):
    """Main function to list and process connections."""
    manager = ConnectionManager(organization_data)

    # Parse limit argument
    limit = arguments.get("--limit")
    if limit:
        limit = int(limit)

    if arguments.get("--age"):
        console.print("[yellow]Age filtering is currently not supported[/]")
        return

    if arguments.get("--list-disabled"):
        console.print("[cyan]Including disabled organizations in account list[/]")

    if arguments.get("--debug"):
        pprint(arguments)
        pprint(organization_data)

    # Fetch and process connections
    with ProgressFactory.create_fetch_progress("Fetching connections data") as progress:
        task = progress.add_task("Fetching connections data...", status="")
        result = _fetch_connections(arguments, organization_data, progress, task)

        # Handle both tuple (connections_data, org_data) and dict (just connections_data) returns
        if isinstance(result, tuple):
            connection_data, fetched_org_data = result
            # Update organization_data with fetched data if we got any
            if fetched_org_data:
                if organization_data is None:
                    organization_data = fetched_org_data
                else:
                    organization_data.update(fetched_org_data)
        else:
            connection_data = result

    # Apply limit to connections (flatten across orgs, apply limit, re-group)
    if limit and connection_data:
        # Flatten all connections into a list with their org_id
        flat_connections = []
        for org_id, org_connections in connection_data.items():
            for conn_key, conn_data in org_connections.items():
                flat_connections.append((org_id, conn_key, conn_data))

        # Apply limit
        flat_connections = flat_connections[:limit]

        # Re-group by org_id
        connection_data = {}
        for org_id, conn_key, conn_data in flat_connections:
            if org_id not in connection_data:
                connection_data[org_id] = {}
            connection_data[org_id][conn_key] = conn_data

    # Handle raw JSON output (actual JSON, machine-readable)
    if arguments.get("--raw"):
        import json

        print(json.dumps(connection_data, indent=2, default=str))
        return

    # Handle formatted dict output (human-readable via pprint)
    if arguments.get("--json"):
        pprint(connection_data)
        return

    if output_file := arguments.get("--output"):
        manager.export_to_csv(output_file, connection_data, organization_data)
    else:
        # Check if organization ID/name was specified - handle both string and list formats
        org_id_or_name = arguments.get("<ORG-ID-or-NAME>")
        has_org_specified = org_id_or_name and (
            isinstance(org_id_or_name, str) or (isinstance(org_id_or_name, list) and org_id_or_name)
        )

        # If no specific organization was provided, show the summary view
        if not has_org_specified:
            create_organization_summary(organization_data, connection_data)
        else:
            manager.print_connection_list(organization_data, connection_data)


# 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
#


def _fetch_connections(arguments, organization_data, progress, task):
    """Fetch connection data based on arguments using v4 API."""
    from src.api.v4_connections import get_connections, get_connections_for_org
    from src.utils.validation import looks_like_an_organization_id

    # Update progress information
    progress.update(task, description="Fetching connections data...")

    try:
        debug = arguments.get("--debug", False)
        connections_data = {}

        # Get organization ID if specified - handle both string and list formats
        org_id_or_name = arguments.get("<ORG-ID-or-NAME>")

        # Convert list to string if needed (CLI sometimes passes a list instead of a string)
        if isinstance(org_id_or_name, list) and org_id_or_name:
            org_id_or_name = org_id_or_name[0]

        # Check if input is a UUID (org ID) or a name/search term
        if org_id_or_name and not looks_like_an_organization_id(org_id_or_name):
            # Not a UUID - treat as organization name and look it up first
            progress.update(task, description=f"Looking up organization '{org_id_or_name}'...")

            try:
                from src.features.organizations import get_organization_data

                # Look up the organization by name (same as czops org info does)
                fetched_org_data = get_organization_data(org_id_or_name, arguments)

                if not fetched_org_data or len(fetched_org_data) == 0:
                    console.print(f"[yellow]No organization found matching '[cyan]{org_id_or_name}[/]'[/]")
                    return {}, {}

                # Get the org ID from the fetched organization data
                org_id = list(fetched_org_data.keys())[0]
                org_name = fetched_org_data[org_id].get("cz_organization_name", org_id_or_name)

                progress.update(task, description=f"Loading connections for organization '{org_name}'...")

                # Now fetch connections for this organization ID
                matching_connections = get_connections_for_org(org_id, debug=debug)

                if matching_connections:
                    # Store connections under the org_id
                    connections_data[org_id] = {}
                    for conn in matching_connections:
                        # Use composite key to handle duplicate connection_ids with different types
                        conn_dict = conn.model_dump()
                        composite_key = _get_connection_composite_key(conn_dict)
                        connections_data[org_id][composite_key] = conn_dict

                    progress.update(
                        task,
                        description=f"Found {len(matching_connections)} connections for organization '{org_name}'",
                        status="✓",
                    )

                    # Return both connections_data and organization_data
                    return connections_data, fetched_org_data
                else:
                    console.print(f"[yellow]No connections found for organization '[cyan]{org_name}[/]' ({org_id})[/]")

                return connections_data, fetched_org_data

            except Exception as e:
                console.print(f"[red]Error searching connections: {e}[/]")
                return {}, {}

        if org_id_or_name and looks_like_an_organization_id(org_id_or_name):
            # Specific organization requested by ID
            # If organization_data is missing this org, fetch it
            if not organization_data or org_id_or_name not in organization_data:
                progress.update(task, description=f"Loading organization data for {org_id_or_name}...")
                from src.features.organizations import get_organization_data

                try:
                    fetched_org_data = get_organization_data(org_id_or_name, arguments)
                    if fetched_org_data:
                        if organization_data is None:
                            organization_data = fetched_org_data
                        else:
                            organization_data.update(fetched_org_data)
                    else:
                        # Org not found, create minimal entry
                        if organization_data is None:
                            organization_data = {}
                        organization_data[org_id_or_name] = {
                            "cz_organization_id": org_id_or_name,
                            "organization_name": org_id_or_name[:8] + "...",
                            "organization_enabled": True,
                        }
                except Exception as org_err:
                    if debug:
                        console.print(f"[yellow]Warning: Could not fetch org data: {org_err}[/]")
                    # Create minimal entry so we can still try to fetch connections
                    if organization_data is None:
                        organization_data = {}
                    organization_data[org_id_or_name] = {
                        "cz_organization_id": org_id_or_name,
                        "organization_name": org_id_or_name[:8] + "...",
                        "organization_enabled": True,
                    }

            # Now fetch connections for the specific org
            org_ids = [org_id_or_name]

            for org_id in org_ids:
                progress.update(task, description=f"Loading connections for organization {org_id}...")

                # Get organization info for context
                org_info = organization_data.get(org_id, {}) if organization_data else {}
                expected_connections = org_info.get("connection_count", "unknown")
                org_name = org_info.get("organization_name", "Unknown")

                try:
                    connections_list = get_connections_for_org(org_id, debug=debug)

                    org_connections = {}
                    for conn in connections_list:
                        conn_dict = conn.model_dump()
                        composite_key = _get_connection_composite_key(conn_dict)
                        org_connections[composite_key] = conn_dict

                    if org_connections:
                        connections_data[org_id] = org_connections

                except Exception as e:
                    if "502" in str(e) or "Internal server error" in str(e):
                        console.print(
                            "[red]Error: V4 connections API is experiencing server issues (502). Please try again later or contact support.[/]"
                        )
                        console.print(
                            f"[dim]Organization '{org_name}' ({org_id}) has {expected_connections} connections according to metadata, but connection details cannot be fetched.[/]"
                        )
                        console.print("[dim]This is a temporary server-side issue with the v4 connections endpoint.[/]")
                    else:
                        console.print(f"[yellow]Warning: Could not fetch connections for organization {org_id}: {e}[/]")
                    continue
        else:
            # No specific organization requested, fetch ALL connections in one efficient request
            progress.update(task, description="Loading all connections in one request...")

            try:
                # Use get_connections() without filters to fetch ALL connections at once
                # This is much more efficient than calling get_connections_for_org() for each org
                all_connections = get_connections(enabled_only=False, debug=debug)

                # Group connections by organization and add V4->legacy field mappings
                for conn in all_connections:
                    org_id = conn.cz_organization_id
                    if org_id not in connections_data:
                        connections_data[org_id] = {}

                    conn_dict = conn.model_dump()
                    # Use composite key to handle duplicate connection_ids with different types
                    composite_key = _get_connection_composite_key(conn_dict)
                    connections_data[org_id][composite_key] = conn_dict

                if debug:
                    console.print(
                        f"[cyan]DEBUG: Fetched {len(all_connections)} connections across {len(connections_data)} organizations in single request[/]"
                    )

                # Fetch organization data for all unique org IDs found
                progress.update(
                    task, description=f"Loading organization details for {len(connections_data)} organizations..."
                )

                from src.features.organizations import get_organization_data

                fetched_org_data = {}
                for org_id in connections_data:
                    try:
                        org_data = get_organization_data(org_id, arguments)
                        if org_data:
                            fetched_org_data.update(org_data)
                    except Exception as org_err:
                        if debug:
                            console.print(f"[yellow]Warning: Could not fetch org data for {org_id}: {org_err}[/]")
                        # Create minimal org entry so display doesn't break
                        fetched_org_data[org_id] = {
                            "cz_organization_id": org_id,
                            "organization_name": org_id[:8] + "...",  # Truncated ID as fallback
                            "organization_enabled": True,
                        }

                # Return both connections_data and organization_data as tuple
                return connections_data, fetched_org_data

            except Exception as e:
                if "502" in str(e) or "Internal server error" in str(e):
                    console.print(
                        "[red]Error: V4 connections API is experiencing server issues (502). Please try again later or contact support.[/]"
                    )
                else:
                    console.print(f"[red]Error fetching all connections: {e}[/]")
                    if debug:
                        console.print(f"[dim]Error details: {str(e)}[/]")

                return {}, {}

        # Update progress
        total_orgs = len(connections_data)
        total_connections = sum(len(conns) for conns in connections_data.values())
        progress.update(
            task,
            description="Connections data retrieved",
            status=f"Found {total_connections} connections across {total_orgs} organizations",
        )
        progress.advance(task)

        console.print(
            f"[green]Retrieved information for {total_connections} connections across {total_orgs} organizations using v4 API[/]"
        )

        # Return tuple if organization_data was modified, otherwise just connections_data for backward compatibility
        if organization_data:
            return connections_data, organization_data
        return connections_data

    except Exception as error:
        console.print(f"[red]Error fetching connections: {error}")
        return {}


# Additional helper functions would go here...
