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

from rich import box
from rich.columns import Columns
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

console = Console()

from src.api.v4_connections import get_connection, update_connection
from src.api.v4_healthcheck import get_connection_healthcheck
from src.api.v4_organizations import get_organizations
from src.features.connections.account_link import parse_s3_url
from src.features.connections.healthcheck_display import display_healthcheck
from src.utils.console import confirm, eprint, get_choice


def update_and_refetch(connection_id: str, payload: dict) -> dict:
    """Update connection and refetch to get complete details."""
    update_connection(connection_id, payload=payload)
    updated_connection = get_connection(connection_id)
    return updated_connection.model_dump()


def _toggle_billing(connection_id: str, action: str) -> dict | None:
    """Toggle billing connection on/off and return updated record."""
    payload = {"is_cost_connection": action == "enable"}
    console.print(f"\n[bold]{action.capitalize()[:-1]}ing {connection_id} as a payer account...[/bold]")
    try:
        return update_and_refetch(connection_id, payload)
    except Exception as e:
        console.print(f"\n[bold red]FAILED[/bold red] - Failed to {action} master payer account\n")
        console.print(Panel(str(e), title="[bold red]Error Details[/bold red]", border_style="red", expand=False))
        return None


def _display_billing_status(result: dict, org_name: str, account_name: str, connection_id: str, action: str) -> None:
    """Display billing status table after enable/disable/update."""
    action_past = "enabled" if action == "enable" else "disabled"
    console.print(f"\n[bold green]SUCCESS[/bold green] - Account successfully {action_past} as master payer\n")

    status_table = Table(title="Account Billing Status", box=box.ROUNDED)
    status_table.add_column("Property", style="dim")
    status_table.add_column("Value")

    is_cost = "COST" in result.get("connection_types", [])
    status_color = "green" if is_cost else "red"
    status_text = "Enabled" if is_cost else "Disabled"

    status_table.add_row("Master Payer Status", f"[{status_color}]{status_text}[/{status_color}]")
    status_table.add_row("Organization ID", result.get("cz_organization_id", "N/A"))
    status_table.add_row("Organization", org_name)
    status_table.add_row("Account Name", account_name)
    status_table.add_row("Connection ID", connection_id)

    cur_data = _get_cur_data(result)
    if cur_data.get("bucket_name"):
        status_table.add_row("Billing Bucket", cur_data["bucket_name"])
    if cur_data.get("report_name"):
        status_table.add_row("Current CUR", cur_data["report_name"])

    console.print(status_table)


def _handle_enable_disable(
    billing_action: str,
    account_record: dict,
    connection_id: str,
    org_name: str,
    account_name: str,
    auto_confirm: bool,
    non_interactive: bool,
) -> None:
    """Handle enable/disable billing action with state check, confirmation, and display."""
    is_currently_enabled = "COST" in account_record.get("connection_types", [])

    if billing_action == "enable" and is_currently_enabled:
        print(f"Connection {connection_id} is already enabled as a master payer account")
        if not auto_confirm:
            sys.exit(0)
    elif billing_action == "disable" and not is_currently_enabled:
        print(f"Connection {connection_id} is already disabled as a master payer account")
        if not auto_confirm:
            sys.exit(0)

    if not auto_confirm and not non_interactive:
        if not confirm(f"{billing_action.capitalize()} {connection_id} as a payer account?"):
            print("Aborting, nothing changed")
            sys.exit(0)

    result = _toggle_billing(connection_id, billing_action)
    if result:
        _display_billing_status(result, org_name, account_name, connection_id, billing_action)


def _resolve_org_name(healthcheck_result: dict, account_record: dict) -> str:
    """Resolve organization name from healthcheck metadata or organizations API."""
    org_name = "Unknown"
    if healthcheck_result:
        metadata = healthcheck_result.get("metadata", {})
        if isinstance(metadata, dict):
            org_name = metadata.get("cz_organization_name") or "Unknown"
    if org_name == "Unknown":
        org_id = account_record.get("cz_organization_id", "")
        if org_id:
            try:
                orgs = get_organizations(org_id)
                org_data = orgs.get(org_id)
                if org_data:
                    org_name = (
                        org_data.get("cz_organization_name")
                        if isinstance(org_data, dict)
                        else getattr(org_data, "cz_organization_name", None)
                    ) or "Unknown"
            except Exception:
                pass
    return org_name


def _get_cur_data(account_record: dict) -> dict:
    """Extract current CUR data from the connection record.

    Checks types.COST.data.cur_data first, then falls back to connection_details.cur_data.
    """
    types = account_record.get("types") or {}
    cost_type = types.get("COST") or {}
    cost_data = cost_type.get("data") or {}
    cur_data = cost_data.get("cur_data")
    if cur_data:
        return cur_data

    conn_details = account_record.get("connection_details") or {}
    return conn_details.get("cur_data") or {}


def _direct_update(arguments: dict, connection_id: str, account_record: dict) -> None:
    """Handle direct updates when CLI options are specified (skip healthcheck)."""
    auto_confirm = arguments.get("-y") or arguments.get("--yes")
    billing_enable = arguments.get("--billing-enable")
    billing_disable = arguments.get("--billing-disable")
    billing_cur = arguments.get("--billing-cur")
    cur_path = arguments.get("--cur-path")
    name = arguments.get("--name")

    # Mutual exclusion check
    if billing_enable and billing_disable:
        eprint("Error: Cannot specify both --billing-enable and --billing-disable")
        sys.exit(1)

    # Validate --cur-path requires COST type (unless --billing-enable is also set)
    is_cost = "COST" in account_record.get("connection_types", [])
    if cur_path and not billing_enable and not is_cost:
        conn_types = ", ".join(account_record.get("connection_types", [])) or "NONE"
        eprint(f"Can not set CUR path on {conn_types} type connections. Connection must be of type COST.")
        sys.exit(1)

    # Build payload from options
    payload: dict = {}

    if name:
        payload["connection_name"] = name

    if billing_enable:
        payload["is_cost_connection"] = True
    elif billing_disable:
        payload["is_cost_connection"] = False

    if cur_path:
        bucket_name, bucket_path = parse_s3_url(cur_path)
        cur_data: dict = {"bucket_name": bucket_name, "bucket_path": bucket_path}
        if billing_cur:
            cur_data["report_name"] = billing_cur
        payload["cur_data"] = cur_data

    elif billing_cur:
        # --billing-cur without --cur-path: update report_name only, keep existing bucket
        existing_cur = _get_cur_data(account_record)
        existing_bucket = existing_cur.get("bucket_name", "")
        existing_path = existing_cur.get("bucket_path", "")
        if not existing_bucket:
            eprint("Error: No existing CUR bucket configured. Use --cur-path to specify the S3 URL.")
            sys.exit(1)
        payload["cur_data"] = {
            "bucket_name": existing_bucket,
            "bucket_path": existing_path,
            "report_name": billing_cur,
        }

    if not payload:
        eprint("Error: No update fields specified")
        sys.exit(1)

    # Display before/after summary
    account_name = account_record.get("connection_name", "Unknown")
    existing_cur = _get_cur_data(account_record)

    before_table = Table(title="Current Configuration", box=box.ROUNDED, title_style="bold dim")
    before_table.add_column("Setting", style="dim")
    before_table.add_column("Value")

    after_table = Table(title="Proposed Changes", box=box.ROUNDED, title_style="bold cyan")
    after_table.add_column("Setting", style="dim")
    after_table.add_column("Value", style="cyan")

    if name:
        before_table.add_row("Name", account_name)
        after_table.add_row("Name", name)

    if billing_enable or billing_disable:
        current_cost = "Enabled" if is_cost else "Disabled"
        new_cost = "Enabled" if billing_enable else "Disabled"
        before_table.add_row("Billing Status", current_cost)
        after_table.add_row("Billing Status", new_cost)

    if cur_path:
        before_table.add_row("CUR Bucket", existing_cur.get("bucket_name", "None"))
        before_table.add_row("CUR Path", existing_cur.get("bucket_path", "None"))
        before_table.add_row("CUR Report", existing_cur.get("report_name", "None"))
        after_table.add_row("CUR Bucket", payload["cur_data"]["bucket_name"])
        after_table.add_row("CUR Path", payload["cur_data"]["bucket_path"])
        after_table.add_row("CUR Report", billing_cur or "[dim]None (cleared)[/dim]")

    elif billing_cur:
        before_table.add_row("CUR Report", existing_cur.get("report_name", "None"))
        after_table.add_row("CUR Report", billing_cur)

    console.print()
    console.print(Columns([before_table, after_table], padding=2))

    # Confirm unless -y
    if not auto_confirm:
        if not confirm(f"\nApply changes to connection {connection_id}?"):
            print("Aborting, nothing changed")
            sys.exit(0)

    # Execute update
    console.print(f"\n[bold]Updating connection {connection_id}...[/bold]")
    try:
        update_connection(connection_id, payload=payload)
    except Exception as e:
        console.print("\n[bold red]FAILED[/bold red] - Failed to update connection\n")
        console.print(Panel(str(e), title="[bold red]Error Details[/bold red]", border_style="red", expand=False))
        return

    console.print("\n[bold green]SUCCESS[/bold green] - Connection updated successfully\n")

    # Show what changed: before → after based on what was sent
    summary_table = Table(title=f"Changes Applied to {connection_id}", box=box.ROUNDED)
    summary_table.add_column("Field", style="dim")
    summary_table.add_column("Before", style="red")
    summary_table.add_column("After", style="green")

    if name:
        summary_table.add_row("Name", account_name, name)

    if billing_enable or billing_disable:
        current_cost = "Enabled" if is_cost else "Disabled"
        new_cost = "Enabled" if billing_enable else "Disabled"
        summary_table.add_row("Billing Status", current_cost, new_cost)

    if cur_path:
        summary_table.add_row(
            "CUR Bucket", existing_cur.get("bucket_name") or "None", payload["cur_data"]["bucket_name"]
        )
        summary_table.add_row("CUR Path", existing_cur.get("bucket_path") or "None", payload["cur_data"]["bucket_path"])
        summary_table.add_row(
            "CUR Report",
            existing_cur.get("report_name") or "None",
            billing_cur or "None",
        )
    elif billing_cur:
        summary_table.add_row("CUR Report", existing_cur.get("report_name") or "None", billing_cur)

    console.print(summary_table)


def functions(arguments, _organization_data=None):
    result = "Dry Run"
    connection_id = arguments.get("<ID>")
    print(f"Fetching connection information for {connection_id}...\n")

    try:
        connection_record = get_connection(connection_id)
        account_record = connection_record.model_dump()
    except Exception as e:
        eprint(f"ERROR: Failed to get connection {connection_id}: {e}")
        sys.exit(-1)

    account_name = account_record.get("connection_name", "Unknown")

    billing_enable = arguments.get("--billing-enable")
    billing_disable = arguments.get("--billing-disable")
    billing_cur = arguments.get("--billing-cur")
    cur_path = arguments.get("--cur-path")
    name_opt = arguments.get("--name")
    auto_confirm = arguments.get("-y") or arguments.get("--yes")

    # If any direct options are specified, skip healthcheck and go to direct update
    has_options = any([billing_enable, billing_disable, billing_cur, cur_path, name_opt])
    if has_options:
        _direct_update(arguments, connection_id, account_record)
        return

    feature = "BILLING"
    print(f"Configuring {feature}")

    console.print("\n[cyan]-> Verifying connection health before proceeding...[/cyan]")
    try:
        healthcheck_response = get_connection_healthcheck(connection_id)
        healthcheck_result = healthcheck_response.model_dump()

        # Extract visible_curs directly from the V4 API response
        visible_curs = []
        checks = healthcheck_result.get("checks", [])
        for check in checks:
            if check.get("name") == "cur_and_data_collection":
                check_details = check.get("details", {})
                visible_curs = check_details.get("visible_curs", [])
                break
    except Exception as e:
        eprint(f"Error fetching healthcheck: {e}")
        healthcheck_result = {}
        visible_curs = []

    # Resolve org name
    org_name = _resolve_org_name(healthcheck_result, account_record)

    print(f"Ready to configure {org_name}: {account_name} ({connection_id})")

    # Build lookup for CUR health by name (preserves all V4 API data)
    cur_health_by_name: dict[str, dict] = {}
    for cur in visible_curs:
        name = cur.get("name")
        if name:
            cur_health_by_name[name] = cur

    # Display health summary using the shared healthcheck display
    if healthcheck_result:
        display_healthcheck(healthcheck_result, account_record)

    if visible_curs:
        provider = account_record.get("provider", "")
        if provider.lower() != "aws":
            eprint(" - Only AWS account billing connections are currently supported by CZOPS")
            sys.exit()

        # CUR details are already displayed by display_healthcheck() above.
        cur_data = _get_cur_data(account_record)
        current_cur = cur_data.get("report_name")
        healthy_CURs = {}
        for cur in visible_curs:
            cur_name = cur.get("name")
            if not cur_name:
                continue
            if cur.get("status", "").lower() == "healthy":
                raw_config = cur.get("raw_config", {})
                if isinstance(raw_config, dict) and raw_config:
                    healthy_CURs[cur_name] = raw_config

        if healthy_CURs:
            console.print(f"\n[bold green]{len(healthy_CURs)} Healthy CUR(s) available for selection[/bold green]")

        # Exit if no healthy CURs are available
        if not healthy_CURs:
            console.print()
            failure_lines = []
            for cur in visible_curs:
                cur_name = cur.get("name", "Unknown")
                if cur.get("status", "").lower() != "healthy":
                    health_details = cur.get("health_details", {})
                    reason = health_details.get("reason", "Unknown") if isinstance(health_details, dict) else "Unknown"
                    access_error = cur.get("access_error", "")
                    detail = f"  * {cur_name}: {reason}"
                    if access_error:
                        detail += f" ({access_error})"
                    failure_lines.append(detail)

            error_panel = Panel(
                "[bold red]No healthy CUR configurations found![/bold red]\n\n"
                + (
                    "\n".join(failure_lines)
                    if failure_lines
                    else "Health check data unavailable - try running healthcheck first."
                )
                + "\n\n[dim]Run 'czops connection healthcheck <id>' for detailed diagnostics.[/dim]",
                title="[bold red]Cannot Reconfigure[/bold red]",
                border_style="red",
                box=box.ROUNDED,
            )
            console.print(error_panel)
            console.print()
            sys.exit(1)

        console.print()  # Add spacing before menu

        # Interactive mode
        action_text = "disable" if "COST" in account_record.get("connection_types", []) else "enable"
        billing_choices = [
            "Change CUR and Enable",
            f"{action_text.capitalize()} Connection",
            "Do nothing and exit",
        ]
        choice = get_choice(billing_choices, "Billing Account Option")

        if choice == billing_choices[0]:
            billing_action = "change_cur"
        elif choice == billing_choices[1]:
            billing_action = action_text
        else:
            billing_action = "exit"

        if billing_action == "change_cur":
            print(f"OK, Reconfiguring billing connection for connection {connection_id}\n")

            # Interactive mode - only show healthy CURs as options
            desired_cur = get_choice(healthy_CURs, "healthy CUR Configuration")

            if desired_cur and desired_cur.get("ReportName"):
                previous_cur_name = current_cur or "None"
                previous_bucket = cur_data.get("bucket_name", "None")
                previous_path = cur_data.get("bucket_path", "None")

                # Build bucket path: use S3Prefix if present, otherwise just the report name
                s3_prefix = (desired_cur.get("S3Prefix") or "").strip()
                if s3_prefix:
                    bucket_path = f"{s3_prefix}/{desired_cur['ReportName']}"
                else:
                    bucket_path = f"/{desired_cur['ReportName']}"

                payload = {
                    "is_cost_connection": True,
                    "cur_data": {
                        "bucket_name": desired_cur["S3Bucket"],
                        "bucket_path": bucket_path,
                        "report_name": desired_cur["ReportName"],
                    },
                }

                console.print(
                    f"\n[bold]Updating connection settings to use CUR '[cyan]{desired_cur['ReportName']}[/cyan]'...[/bold]"
                )

                try:
                    result = update_and_refetch(connection_id, payload)
                except Exception as e:
                    console.print("\n[bold red]FAILED[/bold red] - Failed to update billing configuration\n")
                    error_panel = Panel(
                        str(e), title="[bold red]Error Details[/bold red]", border_style="red", expand=False
                    )
                    console.print(error_panel)
                    return

                if result:
                    console.print("\n[bold green]SUCCESS[/bold green] - Billing configuration updated successfully\n")

                    before_table = Table(title="Previous Configuration", box=box.ROUNDED, title_style="bold red")
                    before_table.add_column("Setting", style="dim")
                    before_table.add_column("Value", style="red")
                    before_table.add_row("CUR Name", previous_cur_name)
                    before_table.add_row("S3 Bucket", previous_bucket)
                    before_table.add_row("S3 Path", previous_path)

                    after_table = Table(title="New Configuration", box=box.ROUNDED, title_style="bold green")
                    after_table.add_column("Setting", style="dim")
                    after_table.add_column("Value", style="green")
                    after_table.add_row("CUR Name", desired_cur["ReportName"])
                    after_table.add_row("S3 Bucket", desired_cur["S3Bucket"])
                    after_table.add_row("S3 Path", bucket_path)

                    console.print(Columns([before_table, after_table], padding=2))

                    cur_updated = _get_cur_data(result)
                    if cur_updated:
                        console.print("\n[bold]Account Billing Status:[/bold]")
                        console.print("  * Master Payer Account: [green]Enabled[/green]")
                        console.print(f"  * Organization ID: {result.get('cz_organization_id', 'N/A')}")
                        console.print(f"  * Organization: {org_name}")
                        console.print(f"  * Account Name: {account_name}")
                        console.print(f"  * Connection ID: {connection_id}")
                        console.print("  * Health Status: [green]Updated[/green]")
            else:
                print("Invalid choice, aborting")
        elif billing_action in ["enable", "disable"]:
            _handle_enable_disable(
                billing_action, account_record, connection_id, org_name, account_name, auto_confirm, False
            )
        elif billing_action == "exit":
            print("Aborting, nothing changed")
    else:
        # Even without CUR metadata, we can still enable/disable billing
        print(" - No CUR configurations found for this account")

        # Interactive mode - offer to enable/disable
        action_text = "disable" if "COST" in account_record.get("connection_types", []) else "enable"
        billing_choices = [
            f"{action_text.capitalize()} Connection",
            "Do nothing and exit",
        ]
        choice = get_choice(billing_choices, "Billing Account Option")

        billing_action = action_text if choice == billing_choices[0] else "exit"

        if billing_action in ["enable", "disable"]:
            _handle_enable_disable(
                billing_action, account_record, connection_id, org_name, account_name, auto_confirm, False
            )
        else:
            print("Aborting, nothing changed")
