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

"""
System command utilities for CloudZero Operations CLI.

This module provides hidden system commands for:
- info: Display system information (version, runtime, health)
- refresh-cache: Refresh backend cache and show statistics
- reset-database: Reset backend cache - clears all cached data (auto-regenerates)
"""

import json
import sys
import time
import traceback

from rich.console import Console
from rich.json import JSON
from rich.pretty import pprint

from src import __version__ as cli_version
from src.api.v4_system import (
    drop_system_database,
    get_materialization_stats,
    get_refresh_status,
    get_system_metadata,
    get_system_refresh,
)
from src.exceptions import APIError
from src.utils.console import eprint, print_info

# Initialize console for full-width output
console = Console(width=None)


def _status_style(status: str) -> tuple[str, str]:
    """Return (icon, rich color) for a health status string."""
    if status.lower() in ("healthy", "ok", "pass", "true"):
        return "\u2705", "green"
    if status.lower() == "degraded":
        return "\u26a0\ufe0f", "yellow"
    return "\u274c", "red"


def system_info(arguments: dict):
    """Display unified system information: version, runtime, and health status."""
    debug_mode = arguments.get("--debug", False)
    raw_output = arguments.get("--raw", False)
    json_output = arguments.get("--json", False)

    try:
        print_info("Fetching system info...")
        metadata = get_system_metadata(debug=debug_mode)
        metadata_dict = metadata.model_dump(exclude_none=True)

        if debug_mode:
            console.print("\n[cyan]Raw API Response:[/]")
            console.print(JSON(json.dumps(metadata_dict)))

        if raw_output:
            print(json.dumps(metadata_dict, indent=2, default=str))
            return

        if json_output:
            pprint(metadata_dict)
            return

        console.print("\n[bold cyan]═══ CloudZero System Info ═══[/]\n")

        # Version
        console.print("[bold yellow]Version:[/]")
        console.print(f"  Client:       [green]{cli_version}[/]")
        if metadata.cz_ops_services:
            console.print(f"  Server:       [green]{metadata.cz_ops_services}[/]")
        else:
            console.print("  Server:       [dim yellow]unavailable[/]")
        if metadata.build_commit:
            console.print(f"  Build Commit: [green]{metadata.build_commit}[/]")

        # Runtime
        has_runtime = any(
            [
                metadata.environment,
                metadata.deployment,
                metadata.region,
                metadata.python_version,
                metadata.lambda_function,
                metadata.lambda_memory_mb,
            ]
        )
        if has_runtime:
            console.print("\n[bold yellow]Runtime:[/]")
            if metadata.environment:
                console.print(f"  Environment:  [magenta]{metadata.environment}[/]")
            if metadata.deployment:
                console.print(f"  Deployment:   [magenta]{metadata.deployment}[/]")
            if metadata.region:
                console.print(f"  Region:       [magenta]{metadata.region}[/]")
            if metadata.python_version:
                console.print(f"  Python:       [cyan]{metadata.python_version}[/]")
            if metadata.lambda_function:
                console.print(f"  Function:     [cyan]{metadata.lambda_function}[/]")
            if metadata.lambda_memory_mb:
                console.print(f"  Memory:       [cyan]{metadata.lambda_memory_mb} MB[/]")

        # Health
        if metadata.status:
            overall_icon, overall_color = _status_style(metadata.status)
            console.print(f"\n{overall_icon} [bold {overall_color}]Health: {metadata.status.upper()}[/]")

            if metadata.services:
                for svc_key, svc in metadata.services.items():
                    svc_icon, svc_color = _status_style(svc.status)
                    if len(svc.checks) == 1:
                        check = svc.checks[0]
                        duration = f"  [dim]{check.duration_ms:.0f}ms[/]" if check.duration_ms else ""
                        msg = f"  [dim]{check.message}[/]" if check.message else ""
                        console.print(f"  {svc_icon} [{svc_color}]{svc.service_name}[/]{duration}{msg}")
                    else:
                        console.print(f"  {svc_icon} [{svc_color}]{svc.service_name}[/]")
                        for check in svc.checks:
                            duration = f"  [dim]{check.duration_ms:.0f}ms[/]" if check.duration_ms else ""
                            msg = f"  [dim]{check.message}[/]" if check.message else ""
                            chk_icon, chk_color = _status_style(check.status)
                            console.print(
                                f"     {chk_icon} [{chk_color}]{check.name.replace('_', ' ')}[/]{duration}{msg}"
                            )

        if metadata.timestamp:
            console.print(f"\n  [dim]{metadata.timestamp}[/]")

    except Exception as e:
        eprint(f"Error fetching system info: {str(e)}")
        if debug_mode:
            traceback.print_exc()
        sys.exit(1)


def refresh_cache(arguments: dict):
    """Trigger an async backend cache refresh and poll until complete."""
    debug_mode = arguments.get("--debug", False)
    poll_interval = 5  # seconds between status checks
    max_wait = 600  # 10-minute timeout

    try:
        print_info("Starting backend cache refresh (async)...")
        start_response = get_system_refresh(debug=debug_mode)

        job_id = start_response.get("job_id")
        if not job_id:
            # Backend may have completed synchronously (older backend or small dataset)
            console.print("\n[bold cyan]═══ Cache Refresh Results ═══[/]\n")
            console.print(JSON(json.dumps(start_response)))
            return

        console.print(f"  Job started: [cyan]{job_id}[/]")
        if debug_mode:
            console.print(f"  [dim]Full start response: {json.dumps(start_response)}[/]")

        # Poll for completion
        elapsed = 0
        with console.status("[bold cyan]Refreshing cache...", spinner="dots") as status:
            while elapsed < max_wait:
                time.sleep(poll_interval)
                elapsed += poll_interval

                job_status = get_refresh_status(job_id, debug=debug_mode)
                current_status = job_status.get("status", "unknown")

                if debug_mode:
                    console.print(f"  [dim]Poll {elapsed}s — status: {current_status}[/]")

                if current_status == "completed":
                    status.stop()
                    _display_refresh_results(job_status)
                    return

                if current_status == "failed":
                    status.stop()
                    error_msg = job_status.get("error") or job_status.get("message") or "Unknown error"
                    eprint(f"Cache refresh failed: {error_msg}")
                    if debug_mode:
                        console.print(JSON(json.dumps(job_status)))
                    sys.exit(1)

                status.update(f"[bold cyan]Refreshing cache... ({elapsed}s)")

        # Timed out
        eprint(f"Cache refresh did not complete within {max_wait}s (job: {job_id})")
        eprint("The refresh is still running in the background. Check status with --debug.")
        sys.exit(1)

    except Exception as e:
        eprint(f"Error refreshing cache: {str(e)}")
        if debug_mode:
            traceback.print_exc()
        sys.exit(1)


def _display_refresh_results(job_status: dict):
    """Display formatted results from a completed refresh job."""
    console.print("\n[bold cyan]═══ Cache Refresh Complete ═══[/]\n")

    duration_ms = job_status.get("duration_ms")
    orgs = int(job_status.get("organizations_written", 0) or 0)
    conns = int(job_status.get("connections_written", 0) or 0)

    # Results from nested result dict (background jobs include result key)
    result = job_status.get("result", {})
    materialization = result.get("materialization", {})
    if materialization:
        orgs = orgs or int(materialization.get("organizations_written", 0) or 0)
        conns = conns or int(materialization.get("connections_written", 0) or 0)
    duration_ms = duration_ms or result.get("duration_ms")

    console.print("[bold green]Status: COMPLETED[/]")

    if orgs or conns:
        console.print(f"  Organizations written: [cyan]{orgs:,}[/]")
        console.print(f"  Connections written:   [cyan]{conns:,}[/]")

    if duration_ms:
        console.print(f"  Duration:              [blue]{float(duration_ms) / 1000:.1f}s[/]")

    started = job_status.get("started_at", "")
    completed = job_status.get("completed_at", "")
    if started and completed:
        console.print(f"  Started:  [dim]{started}[/]")
        console.print(f"  Finished: [dim]{completed}[/]")

    message = job_status.get("message") or result.get("message")
    if message:
        console.print(f"\n  {message}")


def reset_database(arguments: dict):
    """Reset the backend database cache by dropping all cached data.

    This function clears all cached data from the system database, which acts as a cache
    for CloudZero operations. The data will automatically regenerate within 1 hour or
    can be manually refreshed if needed. This operation affects all users temporarily.

    Use when schema changes require cache clearing or when a complete cache reset is needed.
    """
    debug_mode = arguments.get("--debug", False)
    confirm = arguments.get("--confirm", False)
    force = arguments.get("--force", False)

    try:
        # Show informational warning
        console.print("\n[bold yellow]BACKEND CACHE RESET OPERATION[/]\n")
        console.print("[cyan]This will clear all cached data from the system database:[/]")
        console.print("[yellow]  Organization cache data[/]")
        console.print("[yellow]  Connection cache information[/]")
        console.print("[yellow]  Historical cache records[/]")
        console.print("[yellow]  Configuration cache settings[/]")
        console.print("[yellow]  All cached database entries[/]")
        console.print("\n[bold cyan]IMPORTANT NOTES:[/]")
        console.print("[green]  Data will automatically regenerate within 1 hour[/]")
        console.print("[green]  Manual refresh can be triggered if needed[/]")
        console.print("[yellow]  This affects ALL users temporarily[/]")
        console.print("[yellow]  Some czops commands may timeout or run slower until cache rebuilds[/]")
        console.print("[blue]  This operation is typically needed for schema changes or complete cache refresh[/]\n")

        # If no confirmation flag, require interactive confirmation
        if not confirm:
            console.print("[yellow]To proceed, you must:[/]")
            console.print("[yellow]1. Add --confirm flag to your command[/]")
            console.print("[yellow]2. Optionally add --force to bypass additional confirmation[/]")
            console.print("\n[dim]Example: czops system reset-database --confirm --force[/]")
            console.print("\n[red]Cache reset cancelled - confirmation required[/]")
            sys.exit(1)

        # Additional interactive confirmation for extra safety
        if not force:
            try:
                user_input = input("\nType 'RESET CACHE' to confirm this operation affects all users: ")
                if user_input != "RESET CACHE":
                    console.print("[red]Cache reset cancelled - confirmation text did not match[/]")
                    sys.exit(1)
            except KeyboardInterrupt:
                console.print("\n[red]Cache reset cancelled by user[/]")
                sys.exit(1)

        console.print("\n[yellow]Initiating cache reset operation...[/]")

        # Call the V4 API to drop the database
        result = drop_system_database(confirm=True, force=force, debug=debug_mode)

        # Convert DropDatabaseResponse object to dict for display
        result_dict = result.model_dump()

        if debug_mode:
            console.print("\n[cyan]Raw API Response:[/]")
            console.print(JSON(json.dumps(result_dict)))

        # Display comprehensive results
        console.print("\n[bold cyan]═══ Cache Reset Results ═══[/]\n")

        # Operation status
        dropped = result_dict.get("dropped", False)
        if dropped:
            console.print("[bold green]Status: SUCCESSFUL[/]")
        else:
            console.print("[bold red]Status: FAILED[/]")

        # Statistics
        items_deleted = result_dict.get("items_deleted", 0)
        items_failed = result_dict.get("items_failed", 0)
        total_items = result_dict.get("total_items", 0)
        success_rate = result_dict.get("success_rate_percent", 0.0)

        console.print("[bold yellow]Items Statistics:[/]")
        console.print(f"  Total Items: [cyan]{total_items:,}[/]")
        console.print(f"  Successfully Deleted: [green]{items_deleted:,}[/]")
        console.print(f"  Failed to Delete: [red]{items_failed:,}[/]")
        if success_rate is not None:
            color = "green" if success_rate >= 99.0 else "yellow" if success_rate >= 90.0 else "red"
            console.print(f"  Success Rate: [{color}]{success_rate:.2f}%[/]")

        # Performance information
        duration_ms = result_dict.get("duration_ms")
        batches_processed = result_dict.get("batches_processed", 0)
        if duration_ms is not None:
            console.print("\n[bold yellow]Performance:[/]")
            console.print(f"  Duration: [blue]{duration_ms:.1f}ms ({duration_ms / 1000:.2f}s)[/]")
            console.print(f"  Batches Processed: [cyan]{batches_processed:,}[/]")
            if items_deleted > 0 and duration_ms > 0:
                rate = items_deleted / (duration_ms / 1000)
                console.print(f"  Processing Rate: [magenta]{rate:.0f} items/second[/]")

        # Operation details
        operation_id = result_dict.get("operation_id")
        table_name = result_dict.get("table_name")
        namespace = result_dict.get("namespace")

        if operation_id or table_name or namespace:
            console.print("\n[bold yellow]Operation Details:[/]")
            if operation_id:
                console.print(f"  Operation ID: [cyan]{operation_id}[/]")
            if table_name:
                console.print(f"  Table Name: [cyan]{table_name}[/]")
            if namespace:
                console.print(f"  Namespace: [cyan]{namespace}[/]")

        # Messages
        message = result_dict.get("message")
        warning = result_dict.get("warning")

        if message:
            console.print(f"\n[bold cyan]Message:[/] {message}")

        if warning:
            console.print(f"\n[bold yellow]Warning:[/] {warning}")

        # Failed items details (if any)
        failed_items = result_dict.get("failed_items", [])
        if failed_items:
            console.print(f"\n[bold red]Failed Items ({len(failed_items)}):[/]")
            for i, item in enumerate(failed_items[:10]):
                pk = item.get("PK", "Unknown")
                sk = item.get("SK", "Unknown")
                console.print(f"  [red]{i + 1}. PK: {pk}, SK: {sk}[/]")

            if len(failed_items) > 10:
                remaining = len(failed_items) - 10
                console.print(f"  [dim]... and {remaining} more failed items[/]")

        # Final status
        if dropped and items_failed == 0:
            console.print("\n[bold green]Cache reset completed successfully![/]")
            console.print(f"[green]All {items_deleted:,} cache items were cleared from the database.[/]")
            console.print("[cyan]Cache will automatically regenerate within 1 hour or can be manually refreshed.[/]")
        elif dropped and items_failed > 0:
            console.print("\n[bold yellow]Cache reset completed with warnings![/]")
            console.print(f"[yellow]{items_deleted:,} items cleared successfully, {items_failed:,} items failed.[/]")
            console.print("[cyan]Cache will automatically regenerate within 1 hour or can be manually refreshed.[/]")
        else:
            console.print("\n[bold red]Cache reset failed![/]")
            console.print("[red]Operation was not completed successfully.[/]")
            sys.exit(1)

    except KeyboardInterrupt:
        console.print("\n[red]Cache reset cancelled by user[/]")
        sys.exit(1)
    except APIError as e:
        # Check for the specific AWS API Gateway signature error that indicates the endpoint isn't implemented
        server_resp_str = str(e.server_response) if e.server_response else ""
        if e.status_code == 403 and (
            "IncompleteSignatureException" in server_resp_str
            or "Invalid key=value pair (missing equal-sign)" in server_resp_str
        ):
            console.print("\n[bold red]Backend endpoint not available[/]")
            console.print("[yellow]The /system/data DELETE endpoint appears to be unimplemented or misconfigured.[/]")
            if debug_mode:
                console.print(
                    "\n[dim]DEBUG: Server returned IncompleteSignatureException, suggesting the endpoint doesn't exist or isn't configured for JWT authentication.[/]"
                )
                console.print(f"[dim]DEBUG: Status: {e.status_code}[/]")
                console.print(f"[dim]DEBUG: Server response: {e.server_response}[/]")
            console.print("\n[blue]This command requires backend infrastructure changes to be fully functional.[/]")
            console.print("[blue]Please contact the platform team to implement the /system/data DELETE endpoint.[/]")
        else:
            console.print(f"\n[red]Error during cache reset: {str(e)}[/]")
            if debug_mode:
                console.print(f"[dim]Status: {e.status_code}[/]")
                console.print(f"[dim]Server response: {e.server_response}[/]")
        sys.exit(1)
    except Exception as e:
        console.print(f"\n[red]Unexpected error during cache reset: {str(e)}[/]")
        if debug_mode:
            traceback.print_exc()
        sys.exit(1)


def materialization_stats(arguments: dict):
    """Display materialization statistics showing data freshness and processing status."""
    debug_mode = arguments.get("--debug", False)
    raw_output = arguments.get("--raw", False)
    json_output = arguments.get("--json", False)

    try:
        print_info("Fetching materialization stats...")
        stats = get_materialization_stats(debug=debug_mode)

        if debug_mode:
            console.print("\n[cyan]Raw API Response:[/]")
            console.print(JSON(json.dumps(stats, default=str)))

        if raw_output:
            print(json.dumps(stats, indent=2, default=str))
            return

        if json_output:
            pprint(stats)
            return

        console.print("\n[bold cyan]═══ Materialization Stats ═══[/]\n")

        # Status
        status = stats.get("status", "unknown")
        icon, color = _status_style(status)
        console.print(f"{icon} [bold {color}]Status: {status.upper()}[/]")

        # Data counts
        data_counts = stats.get("data_counts", {})
        if data_counts:
            console.print("\n[bold yellow]Data Counts:[/]")
            total_orgs = int(data_counts.get("total_organizations", 0) or 0)
            enabled_orgs = int(data_counts.get("enabled_organizations", 0) or 0)
            orgs_with_summaries = int(data_counts.get("organizations_with_summaries", 0) or 0)
            console.print(f"  Total organizations:       [cyan]{total_orgs:,}[/]")
            console.print(f"  Enabled organizations:     [cyan]{enabled_orgs:,}[/]")
            console.print(f"  Orgs with summaries:       [cyan]{orgs_with_summaries:,}[/]")

        # Write counts
        orgs_written = int(stats.get("organizations_written", 0) or 0)
        conns_written = int(stats.get("connections_written", 0) or 0)
        orgs_with_sums = int(stats.get("organizations_with_summaries", 0) or 0)
        if orgs_written or conns_written:
            console.print("\n[bold yellow]Last Materialization:[/]")
            console.print(f"  Organizations written:     [cyan]{orgs_written:,}[/]")
            console.print(f"  Connections written:        [cyan]{conns_written:,}[/]")
            if orgs_with_sums:
                console.print(f"  Orgs with summaries:       [cyan]{orgs_with_sums:,}[/]")

        # Performance
        performance = stats.get("performance", {})
        if performance:
            console.print("\n[bold yellow]Performance:[/]")
            for key, value in performance.items():
                label = key.replace("_", " ").title()
                if isinstance(value, float):
                    console.print(f"  {label}: [blue]{value:.2f}s[/]")
                else:
                    console.print(f"  {label}: [blue]{value}[/]")

        # Performance analysis
        analysis = stats.get("performance_analysis", {})
        if analysis:
            console.print("\n[bold yellow]Performance Analysis:[/]")
            for key, value in analysis.items():
                label = key.replace("_", " ").title()
                console.print(f"  {label}: [magenta]{value}[/]")

        # Timestamps
        started = stats.get("started_at", "")
        completed = stats.get("completed_at", "")
        if started:
            console.print(f"\n  Started:   [dim]{started}[/]")
        if completed:
            console.print(f"  Completed: [dim]{completed}[/]")

        # Schema version
        schema = stats.get("schema_version")
        extraction = stats.get("extraction_version")
        if schema or extraction:
            console.print(f"\n  [dim]Schema: {schema or 'n/a'} | Extraction: {extraction or 'n/a'}[/]")

    except Exception as e:
        eprint(f"Error fetching materialization stats: {str(e)}")
        if debug_mode:
            traceback.print_exc()
        sys.exit(1)


def handle_command(arguments: dict):
    """Main entry point for system commands."""
    if arguments.get("info"):
        system_info(arguments)
    elif arguments.get("refresh-cache"):
        refresh_cache(arguments)
    elif arguments.get("reset-database"):
        reset_database(arguments)
    elif arguments.get("stats"):
        materialization_stats(arguments)
    else:
        eprint("Unknown system command. Available commands: info, refresh-cache, reset-database, stats")
        sys.exit(1)
