"""Main CLI application for Realms."""

from typing import List, Optional

import typer
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

from .commands.create import create_command
from .commands.db import db_command, db_find_command, db_get_command, db_schema_command
from .commands.deploy import deploy_command
from .commands.export_data import export_data_command
from .commands.extension import extension_command
from .commands.import_data import import_codex_command, import_data_command
from .commands.mundus import mundus_create_command, mundus_deploy_command, mundus_status_command
from .commands.ps import ps_kill_command, ps_logs_command, ps_ls_command, ps_start_command
from .commands.registry import (
    billing_add_credits_command,
    billing_balance_command,
    billing_deduct_credits_command,
    billing_status_command,
    registry_count_command,
    registry_create_command,
    registry_deploy_command,
    registry_get_command,
    registry_list_command,
    registry_remove_command,
    registry_search_command,
    registry_status_command,
)
from .commands.run import run_command
from .commands.shell import shell_command
from .constants import MAX_BATCH_SIZE, REALM_FOLDER
from .utils import (
    check_dependencies,
    display_info_panel,
    get_current_network,
    get_current_realm,
    get_current_realm_folder,
    get_effective_cwd,
    get_effective_network_and_canister,
    get_project_root,
    get_registry_canister_id,
    is_canister_id,
    list_realm_folders,
    resolve_realm_by_id,
    resolve_realm_details,
    resolve_realm_ref_to_canister_id,
    set_current_network,
    set_current_realm,
    set_current_realm_folder,
    unset_current_network,
    unset_current_realm,
    unset_current_realm_folder,
)

console = Console()

app = typer.Typer(
    name="realms",
    help="CLI tool for deploying and managing Realms",
    add_completion=False,
    rich_markup_mode="rich",
    invoke_without_command=True,
)




@app.command("extension")
def extension(
    action: str = typer.Argument(
        ...,
        help="Action to perform: list, install-from-source, package, install, uninstall",
    ),
    extension_id: Optional[str] = typer.Option(
        None, "--extension-id", help="Extension ID for package/uninstall operations"
    ),
    package_path: Optional[str] = typer.Option(
        None, "--package-path", help="Path to extension package for install operation"
    ),
    source_dir: str = typer.Option(
        "extensions", "--source-dir", help="Source directory for extensions"
    ),
    all_extensions: bool = typer.Option(
        False, "--all", help="Uninstall all extensions (only for uninstall action)"
    ),
) -> None:
    """Manage Realm extensions."""
    extension_command(action, extension_id, package_path, source_dir, all_extensions)


@app.command("import")
def import_data(
    file_path: str = typer.Argument(..., help="Path to JSON data file"),
    entity_type: Optional[str] = typer.Option(
        None, "--type", help="Entity type (codex for Python files, or json entity type)"
    ),
    format: str = typer.Option("json", "--format", help="Data format (json)"),
    batch_size: int = typer.Option(
        MAX_BATCH_SIZE, "--batch-size", help="Batch size for import"
    ),
    dry_run: bool = typer.Option(
        False, "--dry-run", help="Show what would be imported without executing"
    ),
    network: str = typer.Option("local", "--network", help="Network to use for import"),
    identity: Optional[str] = typer.Option(
        None, "--identity", help="Path to identity PEM file or identity name for dfx"
    ),
    canister: str = typer.Option(
        "realm_backend", "--canister", "-c", help="Canister name to import data to (e.g. realm1_backend for mundus)"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", "-f", help="Realm folder containing dfx.json (uses current realm context if not specified)"
    ),
) -> None:
    """Import data into the realm. Supports JSON data and Python codex files."""
    import_data_command(file_path, entity_type, format, batch_size, dry_run, network, identity, canister, folder)


@app.command("export")
def export_data(
    output_dir: str = typer.Option(
        "exported_realm", "--output-dir", help="Output directory for exported data"
    ),
    entity_types: Optional[str] = typer.Option(
        None, "--entity-types", help="Comma-separated list of entity types to export (default: all)"
    ),
    network: str = typer.Option("local", "--network", help="Network to use for export"),
    identity: Optional[str] = typer.Option(
        None, "--identity", help="Path to identity PEM file or identity name for dfx"
    ),
    include_codexes: bool = typer.Option(
        True, "--include-codexes/--no-codexes", help="Include codexes in export (default: True)"
    ),
) -> None:
    """Export data from the realm. Saves JSON data and Python codex files."""
    export_data_command(output_dir, entity_types, network, identity, include_codexes)


# Register deploy command directly from commands module
app.command("deploy")(deploy_command)


@app.command("status")
def status(
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name to check (overrides context)"
    ),
) -> None:
    """Show status of current Realms project."""
    console.print("[bold blue]📊 Realms Project Status[/bold blue]\n")

    # Get effective network and canister from context
    effective_network, effective_canister = get_effective_network_and_canister(
        network, canister
    )

    # Show current context
    current_realm = get_current_realm()
    if current_realm:
        console.print(f"[dim]Using realm context: {current_realm}[/dim]")
    console.print(
        f"[dim]Network: {effective_network}, Canister: {effective_canister}[/dim]\n"
    )

    # Check dependencies
    console.print("[bold]Dependencies:[/bold]")
    if check_dependencies():
        console.print("  ✅ All required tools are available")
    else:
        console.print("  ❌ Some dependencies are missing")
        return

    # Try to call backend canister status
    console.print("\n[bold]Canister Status:[/bold]")
    try:
        import subprocess

        cmd = ["dfx", "canister", "call", effective_canister, "status"]
        if effective_network != "local":
            cmd.extend(["--network", effective_network])

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
        if result.returncode == 0:
            console.print("  ✅ Backend canister is responding")
            # Parse the response if needed
            if "success = true" in result.stdout:
                console.print("  ✅ Backend status: healthy")
        else:
            console.print("  ❌ Backend canister not responding")
            if result.stderr:
                console.print(f"      Error: {result.stderr.strip()}")
    except Exception as e:
        console.print(f"  ❌ Could not check backend status: {e}")

    # Check dfx replica status
    console.print("\n[bold]dfx Replica:[/bold]")
    try:
        import subprocess

        cmd = ["dfx", "ping", effective_network]

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
        if result.returncode == 0:
            console.print("  ✅ dfx replica is running")
        else:
            console.print("  ❌ dfx replica is not running")
    except Exception:
        console.print("  ❌ dfx replica is not running")


# Create mundus subcommand group
mundus_app = typer.Typer(name="mundus", help="Multi-realm mundus operations")
app.add_typer(mundus_app, name="mundus")


@mundus_app.command("create")
def mundus_create(
    output_dir: str = typer.Option(
        ".realms/mundus", "--output-dir", help="Output directory for mundus"
    ),
    mundus_name: str = typer.Option(
        "Demo Mundus", "--mundus-name", help="Name of the mundus"
    ),
    manifest: Optional[str] = typer.Option(
        None, "--manifest", help="Path to mundus manifest.json (default: examples/demo/manifest.json)"
    ),
    network: str = typer.Option(
        "local", "--network", help="Target network for deployment"
    ),
    deploy: bool = typer.Option(
        False, "--deploy", help="Deploy the mundus after creation"
    ),
    identity: Optional[str] = typer.Option(
        None, "--identity", help="Path to identity PEM file or identity name for dfx"
    ),
    mode: str = typer.Option(
        "upgrade", "--mode", "-m", help="Deploy mode: 'upgrade' or 'reinstall' (wipes stable memory)"
    ),
) -> None:
    """Create a new multi-realm mundus from a manifest."""
    mundus_create_command(
        output_dir,
        mundus_name,
        manifest,
        network,
        deploy,
        identity,
        mode,
    )


@mundus_app.command("deploy")
def mundus_deploy(
    mundus_dir: str = typer.Option(
        ".realms/mundus", "--mundus-dir", help="Path to mundus directory"
    ),
    network: str = typer.Option(
        "local", "--network", help="Target network for deployment"
    ),
    identity: Optional[str] = typer.Option(
        None, "--identity", help="Path to identity PEM file or identity name for dfx"
    ),
    mode: str = typer.Option(
        "upgrade", "--mode", "-m", help="Deploy mode: 'upgrade' or 'reinstall'"
    ),
) -> None:
    """Deploy all realms and registry in an existing mundus."""
    mundus_deploy_command(mundus_dir, network, identity, mode)


@mundus_app.command("status")
def mundus_status(
    mundus_dir: Optional[str] = typer.Option(
        None, "--mundus-dir", help="Path to specific mundus directory (default: scan .realms/mundus)"
    ),
    network: str = typer.Option(
        "local", "--network", "-n", help="Network to check status for"
    ),
) -> None:
    """Show status of mundus deployments including realms and registries."""
    mundus_status_command(mundus_dir, network)


# Create realm subcommand group
realm_app = typer.Typer(name="realm", help="Realm-specific operations")
app.add_typer(realm_app, name="realm")


@realm_app.command("create")
def realm_create(
    output_dir: str = typer.Option(
        REALM_FOLDER, "--output-dir", help="Output directory"
    ),
    realm_name: str = typer.Option(
        "Generated Demo Realm", "--realm-name", help="Name of the realm"
    ),
    manifest: Optional[str] = typer.Option(
        "examples/demo/realm1/manifest.json", "--manifest", help="Path to realm manifest.json (defaults to realm1 template)"
    ),
    random: bool = typer.Option(
        False, "--random/--no-random", help="Generate random realm data"
    ),
    members: Optional[int] = typer.Option(
        None, "--members", help="Number of members to generate (overrides manifest)"
    ),
    organizations: Optional[int] = typer.Option(
        None, "--organizations", help="Number of organizations to generate (overrides manifest)"
    ),
    transactions: Optional[int] = typer.Option(
        None, "--transactions", help="Number of transactions to generate (overrides manifest)"
    ),
    disputes: Optional[int] = typer.Option(
        None, "--disputes", help="Number of disputes to generate (overrides manifest)"
    ),
    seed: Optional[int] = typer.Option(
        None, "--seed", help="Random seed for reproducible generation (overrides manifest)"
    ),
    network: str = typer.Option(
        "local", "--network", help="Target network for deployment"
    ),
    deploy: bool = typer.Option(
        False, "--deploy", help="Deploy the realm after creation"
    ),
    identity: Optional[str] = typer.Option(
        None, "--identity", help="Path to identity PEM file or identity name for dfx"
    ),
    mode: str = typer.Option(
        "upgrade", "--mode", "-m", help="Deploy mode: 'upgrade' or 'reinstall' (wipes stable memory)"
    ),
    bare: bool = typer.Option(
        False, "--bare", help="Create minimal realm (canisters only, no extensions or data)"
    ),
    plain_logs: bool = typer.Option(
        False, "--plain-logs", help="Show full verbose output instead of progress UI during deployment"
    ),
) -> None:
    """Create a new realm. Use --manifest for template or flags for custom configuration."""
    create_command(
        output_dir,
        realm_name,
        manifest,
        random,
        members,
        organizations,
        transactions,
        disputes,
        seed,
        network,
        deploy,
        identity,
        mode,
        bare,
        plain_logs,
    )


@realm_app.command("deploy")
def realm_deploy(
    folder: Optional[str] = typer.Option(
        None, "--folder", "-f", help="Path to realm folder to deploy"
    ),
    config_file: Optional[str] = typer.Option(
        None, "--config-file", help="Path to custom dfx.json"
    ),
    network: str = typer.Option(
        "local", "--network", "-n", help="Network to deploy to"
    ),
    clean: bool = typer.Option(
        False, "--clean", help="Clean deployment (wipes state)"
    ),
    identity: Optional[str] = typer.Option(
        None, "--identity", help="Identity file or name for IC deployment"
    ),
    mode: str = typer.Option(
        "upgrade", "--mode", "-m", help="Deployment mode (upgrade, reinstall)"
    ),
    plain_logs: bool = typer.Option(
        False, "--plain-logs", help="Show full verbose output instead of progress UI"
    ),
) -> None:
    """Deploy a realm to the specified network."""
    deploy_command(config_file, folder, network, clean, identity, mode, plain_logs)


@realm_app.command("call")
def realm_call(
    realm_ref: str = typer.Argument(help="Realm canister ID or name (e.g., 'Dominion' or '2lbfz-yiaaa-aaaac-qcyma-cai')"),
    method: str = typer.Argument(help="Method name or 'extension' for extension calls"),
    args: str = typer.Argument("()", help="Candid arguments (e.g., '(\"admin\")') or extension name for extension calls"),
    function_name: Optional[str] = typer.Argument(None, help="Function name (only for extension calls)"),
    function_args: Optional[str] = typer.Argument(None, help="JSON arguments (only for extension calls)"),
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    output: str = typer.Option(
        "json", "--output", "-o", help="Output format: json or candid"
    ),
    async_call: bool = typer.Option(
        False, "--async", "-a", help="Use async extension call (for extension calls only)"
    ),
    verbose: bool = typer.Option(
        False, "--verbose", "-v", help="Show verbose output"
    ),
) -> None:
    """
    Call a backend method or extension function on a realm.
    
    Examples:
        # Call backend method on a realm by name
        realms realm call Dominion status --network staging
        realms realm call Dominion join_realm '("admin")' --network staging
        
        # Call backend method on a realm by canister ID
        realms realm call 2lbfz-yiaaa-aaaac-qcyma-cai status --network staging
        
        # Call extension function
        realms realm call Dominion extension member_dashboard check_invoice_payment '{"invoice_id": "x"}' --network staging
        
        # Output format (default: json)
        realms realm call Dominion status --output candid --network staging
    """
    import json as json_module
    import subprocess
    import sys
    
    # Validate output format
    if output not in ("json", "candid"):
        console.print(f"[red]❌ Invalid output format: {output}. Use 'json' or 'candid'[/red]")
        raise typer.Exit(1)
    
    # Get effective network
    effective_network, _ = get_effective_network_and_canister(network, None, quiet=not verbose)
    
    # Resolve realm reference to canister ID
    try:
        backend_canister_id, realm_name = resolve_realm_ref_to_canister_id(
            realm_ref, effective_network
        )
    except ValueError as e:
        console.print(f"[red]❌ {e}[/red]")
        raise typer.Exit(1)
    
    effective_canister = backend_canister_id
    
    if verbose:
        console.print(f"[dim]Realm: {realm_name}[/dim]")
        console.print(f"[dim]Backend Canister: {backend_canister_id}[/dim]")
        console.print(f"[dim]Network: {effective_network}[/dim]\n")
    
    # Check if this is an extension call
    if method == "extension":
        if not function_name or not function_args:
            console.print("[red]❌ Extension calls require: extension <ext_name> <function> <json_args>[/red]")
            console.print("Example: realms realm call Dominion extension member_dashboard check_invoice_payment '{\"invoice_id\": \"x\"}' --network staging")
            raise typer.Exit(1)
        
        extension_name = args  # args is actually the extension name in this case
        
        if verbose:
            console.print("[bold blue]🔧 Calling Extension Function[/bold blue]\n")
            console.print(f"Extension: [cyan]{extension_name}[/cyan]")
            console.print(f"Function: [cyan]{function_name}[/cyan]")
            console.print(f"Args: [dim]{function_args}[/dim]")
            console.print(f"Async: [dim]{async_call}[/dim]\n")
        
        try:
            # Validate JSON args
            json_module.loads(function_args)
        except json_module.JSONDecodeError as e:
            console.print(f"[red]❌ Invalid JSON arguments: {e}[/red]")
            raise typer.Exit(1)
        
        # Build extension call
        escaped_args = function_args.replace('"', '\\"')
        call_record = f"""(record {{ extension_name = "{extension_name}"; function_name = "{function_name}"; args = "{escaped_args}"; }})"""
        
        call_method = "extension_async_call" if async_call else "extension_sync_call"
        cmd = ["dfx", "canister", "call", "--network", effective_network, effective_canister, call_method, call_record]
        if output == "json":
            cmd.extend(["--output", "json"])
    else:
        # Regular backend method call
        if verbose:
            console.print("[bold blue]📞 Calling Backend Method[/bold blue]\n")
            console.print(f"Method: [cyan]{method}[/cyan]")
            console.print(f"Args: [dim]{args}[/dim]\n")
        
        cmd = ["dfx", "canister", "call", "--network", effective_network, effective_canister, method, args]
        if output == "json":
            cmd.extend(["--output", "json"])
    
    try:
        if verbose:
            console.print("[dim]Executing...[/dim]")
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
        
        if result.returncode == 0:
            if verbose:
                console.print("[green]✅ Call successful[/green]\n")
                console.print("[bold]Response:[/bold]")
            
            print(result.stdout.strip())
        else:
            if result.stderr:
                sys.stderr.write(f"Error: {result.stderr}\n")
            if result.stdout:
                sys.stderr.write(f"Output: {result.stdout}\n")
            raise typer.Exit(1)
    except subprocess.TimeoutExpired:
        sys.stderr.write("Error: Call timed out\n")
        raise typer.Exit(1)
    except Exception as e:
        sys.stderr.write(f"Error: {e}\n")
        raise typer.Exit(1)


@realm_app.command("extension")
def realm_extension(
    realm_ref: str = typer.Argument(help="Realm canister ID or name"),
    extension_name: str = typer.Argument(help="Extension name"),
    function_name: str = typer.Argument(help="Function name to call"),
    args: str = typer.Argument(help="JSON arguments for the function"),
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
) -> None:
    """Call an extension function on the realm backend. (DEPRECATED: use 'realms realm call <realm> extension' instead)"""
    console.print("[yellow]⚠️  Deprecated: Use 'realms realm call <realm> extension ...' instead[/yellow]\n")
    console.print("[bold blue]🔧 Calling Extension Function[/bold blue]\n")

    # Get effective network
    effective_network, _ = get_effective_network_and_canister(network, None)
    
    # Resolve realm reference to canister ID
    try:
        backend_canister_id, realm_name = resolve_realm_ref_to_canister_id(
            realm_ref, effective_network
        )
    except ValueError as e:
        console.print(f"[red]❌ {e}[/red]")
        raise typer.Exit(1)

    console.print(f"Realm: [cyan]{realm_name}[/cyan]")
    console.print(f"Extension: [cyan]{extension_name}[/cyan]")
    console.print(f"Function: [cyan]{function_name}[/cyan]")
    console.print(f"Args: [dim]{args}[/dim]")
    console.print(f"Network: [dim]{effective_network}[/dim]")
    console.print(f"Canister: [dim]{backend_canister_id}[/dim]\n")

    try:
        import json
        import subprocess

        # Validate JSON args
        try:
            json.loads(args)
        except json.JSONDecodeError as e:
            console.print(f"[red]❌ Invalid JSON arguments: {e}[/red]")
            raise typer.Exit(1)

        # Build dfx command - escape quotes in JSON args
        escaped_args = args.replace('"', '\\"')
        call_record = f"""(
  record {{
    extension_name = "{extension_name}";
    function_name = "{function_name}";
    args = "{escaped_args}";
  }}
)"""

        cmd = [
            "dfx",
            "canister",
            "call",
            "--network", effective_network,
            backend_canister_id,
            "extension_sync_call",
            call_record,
        ]

        console.print("[dim]Executing...[/dim]")
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

        if result.returncode == 0:
            console.print("[green]✅ Extension call successful[/green]\n")
            console.print("[bold]Response:[/bold]")
            console.print(result.stdout)
        else:
            console.print("[red]❌ Extension call failed[/red]\n")
            if result.stderr:
                console.print(f"[red]Error: {result.stderr}[/red]")
            if result.stdout:
                console.print(f"Output: {result.stdout}")
            raise typer.Exit(1)

    except subprocess.TimeoutExpired:
        console.print("[red]❌ Extension call timed out[/red]")
        raise typer.Exit(1)
    except Exception as e:
        console.print(f"[red]❌ Error executing extension call: {e}[/red]")
        raise typer.Exit(1)


# Create registry subcommand group
registry_app = typer.Typer(name="registry", help="Realm registry operations")
app.add_typer(registry_app, name="registry")

# Create registry realm subgroup
registry_realm_app = typer.Typer(name="realm", help="Manage realms in registry")
registry_app.add_typer(registry_realm_app, name="realm")

# Create registry billing subgroup
registry_billing_app = typer.Typer(name="billing", help="Manage credits and billing")
registry_app.add_typer(registry_billing_app, name="billing")


@registry_realm_app.command("add")
def registry_add(
    realm_name: str = typer.Option(..., "--realm-name", help="Human-readable realm name"),
    frontend_url: str = typer.Option("", "--frontend-url", help="Frontend canister URL (optional, will auto-derive)"),
    backend_url: str = typer.Option("", "--backend-url", help="Backend canister URL for status fetching"),
    logo_url: str = typer.Option("", "--logo-url", help="Realm logo URL (served from frontend static folder)"),
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    registry_canister_id: str = typer.Option(
        "realm_registry_backend",
        "--registry-canister",
        help="Registry canister ID or name"
    ),
    realm_backend_canister: str = typer.Option(
        "realm_backend",
        "--realm-canister",
        help="This realm's backend canister ID or name"
    ),
) -> None:
    """
    Register this realm with the central registry.
    
    Calls the realm backend's register_realm_with_registry function which makes
    a secure inter-canister call to the registry. The registry uses the calling
    canister's principal as the realm ID.
    
    Example:
        realms registry realm add \\
            --realm-name "My Demo Governance Realm" \\
            --network local
    """
    import json
    import subprocess
    
    console.print(Panel.fit(
        "[bold cyan]🌐 Registering Realm with Central Registry[/bold cyan]",
        border_style="cyan"
    ))
    
    # Get canister IDs for registration
    frontend_canister_id = ""
    token_canister_id = ""
    nft_canister_id = ""
    
    # Auto-derive frontend canister ID and URL
    try:
        result = subprocess.run(
            ["dfx", "canister", "id", "realm_frontend", "--network", network],
            capture_output=True, text=True, check=True, timeout=5
        )
        frontend_canister_id = result.stdout.strip()
        
        if not frontend_url:
            # Format URL based on network
            if network == "ic":
                frontend_url = f"{frontend_canister_id}.ic0.app"
            elif network == "staging":
                frontend_url = f"{frontend_canister_id}.icp0.io"
            else:  # local
                frontend_url = f"{frontend_canister_id}.localhost:8000"
            console.print(f"[dim]Auto-derived frontend URL: {frontend_url}[/dim]")
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        console.print("[yellow]⚠️  Could not get frontend canister ID[/yellow]")
    
    # Try to get optional token_backend canister ID
    try:
        result = subprocess.run(
            ["dfx", "canister", "id", "token_backend", "--network", network],
            capture_output=True, text=True, check=True, timeout=5
        )
        token_canister_id = result.stdout.strip()
        console.print(f"[dim]Found token_backend: {token_canister_id}[/dim]")
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        pass  # Optional canister
    
    # Try to get optional nft_backend canister ID
    try:
        result = subprocess.run(
            ["dfx", "canister", "id", "nft_backend", "--network", network],
            capture_output=True, text=True, check=True, timeout=5
        )
        nft_canister_id = result.stdout.strip()
        console.print(f"[dim]Found nft_backend: {nft_canister_id}[/dim]")
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        pass  # Optional canister
    
    console.print(f"\n[cyan]Realm Name:[/cyan] {realm_name}")
    console.print(f"[cyan]Frontend URL:[/cyan] {frontend_url}")
    if frontend_canister_id:
        console.print(f"[cyan]Frontend Canister:[/cyan] {frontend_canister_id}")
    if backend_url:
        console.print(f"[cyan]Backend URL:[/cyan] {backend_url}")
    if logo_url:
        console.print(f"[cyan]Logo URL:[/cyan] {logo_url}")
    console.print(f"[dim]Network:[/dim] {network}\n")
    
    # Call realm backend's register_realm_with_registry (secure inter-canister call)
    # Pack canister IDs into JSON (Kybra limits params to 5)
    canister_ids_json = json.dumps({
        "backend_url": backend_url,
        "frontend_canister_id": frontend_canister_id,
        "token_canister_id": token_canister_id,
        "nft_canister_id": nft_canister_id,
    }).replace('"', '\\"')  # Escape quotes for dfx call
    
    args = f'("{registry_canister_id}", "{realm_name}", "{frontend_url}", "{logo_url}", "{canister_ids_json}")'
    cmd = ["dfx", "canister", "call", "--network", network, realm_backend_canister, "register_realm_with_registry", args]
    
    try:
        result = subprocess.run(
            cmd, capture_output=True, text=True, check=True, timeout=60
        )
        
        # Parse the JSON response
        output = result.stdout.strip()
        if output.startswith("(") and output.endswith(")"):
            output = output[1:-1].strip()
        if output.startswith('"') and output.endswith('"'):
            output = output[1:-1]
        
        # Unescape the JSON string
        output = output.replace('\\"', '"').replace('\\n', '\n')
        
        try:
            response = json.loads(output)
            if response.get("success"):
                console.print(Panel(
                    f"[green]✅ Successfully registered realm with registry![/green]\n\n"
                    f"[cyan]Realm ID:[/cyan] {response.get('realm_id', 'N/A')}\n"
                    f"[cyan]Realm Name:[/cyan] {realm_name}\n"
                    f"[cyan]Frontend URL:[/cyan] {frontend_url}",
                    border_style="green",
                    title="Registration Complete"
                ))
            else:
                console.print(Panel(
                    f"[red]❌ Registration failed[/red]\n\n"
                    f"[yellow]Error:[/yellow] {response.get('error', 'Unknown error')}",
                    border_style="red",
                    title="Registration Failed"
                ))
                raise typer.Exit(1)
        except json.JSONDecodeError:
            # Check for Ok/Err in raw output
            if "success" in output.lower() or "Ok" in output:
                console.print(Panel(
                    f"[green]✅ Successfully registered realm with registry![/green]",
                    border_style="green",
                    title="Registration Complete"
                ))
            else:
                console.print(f"[yellow]⚠️  Unexpected response:[/yellow] {output}")
            
    except subprocess.CalledProcessError as e:
        console.print(f"[red]❌ Error: {e.stderr.strip()}[/red]")
        raise typer.Exit(1)
    except subprocess.TimeoutExpired:
        console.print("[red]❌ Command timed out[/red]")
        raise typer.Exit(1)
    except Exception as e:
        console.print(f"[red]❌ Unexpected error: {str(e)}[/red]")
        raise typer.Exit(1)


@registry_realm_app.command("list")
def registry_list(
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """List all realms in the registry."""
    registry_list_command(network, canister_id)


@registry_realm_app.command("get")
def registry_get(
    realm_id: str = typer.Option(..., "--id", help="Realm ID to retrieve"),
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Get a specific realm by ID."""
    registry_get_command(realm_id, network, canister_id)


@registry_realm_app.command("remove")
def registry_remove(
    realm_id: str = typer.Option(..., "--id", help="Realm ID to remove"),
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Remove a realm from the registry."""
    registry_remove_command(realm_id, network, canister_id)


@registry_realm_app.command("search")
def registry_search(
    query: str = typer.Option(..., "--query", help="Search query"),
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Search realms by name or ID."""
    registry_search_command(query, network, canister_id)


@registry_realm_app.command("count")
def registry_count(
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Get the total number of realms."""
    registry_count_command(network, canister_id)


@registry_app.command("create")
def registry_create(
    registry_name: Optional[str] = typer.Option(None, "--name", help="Registry name"),
    output_dir: str = typer.Option(".realms", "--output-dir", "-o", help="Base output directory"),
    network: str = typer.Option("local", "--network", "-n", help="Network to deploy to"),
    deploy: bool = typer.Option(
        False, "--deploy", help="Deploy the registry after creation"
    ),
    identity: Optional[str] = typer.Option(
        None, "--identity", help="Path to identity PEM file or identity name for dfx"
    ),
    mode: str = typer.Option(
        "upgrade", "--mode", "-m", help="Deploy mode: 'upgrade' or 'reinstall' (wipes stable memory)"
    ),
) -> None:
    """Create a new registry instance."""
    registry_create_command(registry_name, output_dir, network, deploy, identity, mode)


@registry_app.command("deploy")
def registry_deploy(
    folder: str = typer.Option(..., "--folder", "-f", help="Path to registry directory"),
    network: str = typer.Option("local", "--network", "-n", help="Network to deploy to"),
    mode: str = typer.Option("upgrade", "--mode", "-m", help="Deployment mode (upgrade, reinstall)"),
    identity: Optional[str] = typer.Option(None, "--identity", help="Identity file for IC deployment"),
) -> None:
    """Deploy a registry instance."""
    registry_deploy_command(folder, network, mode, identity)


@registry_app.command("status")
def registry_status(
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Get the status of the registry backend canister."""
    registry_status_command(network, canister_id)


# ============== Billing Commands ==============

@registry_billing_app.command("balance")
def billing_balance(
    principal_id: str = typer.Option(..., "--principal", "-p", help="User principal ID"),
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Get a user's credit balance."""
    billing_balance_command(principal_id, network, canister_id)


@registry_billing_app.command("add_credits")
def billing_add_credits(
    principal_id: str = typer.Option(..., "--principal", "-p", help="User principal ID"),
    amount: int = typer.Option(..., "--amount", "-a", help="Amount of credits to add"),
    stripe_session_id: str = typer.Option("", "--stripe-session", help="Stripe session ID"),
    description: str = typer.Option("Manual top-up", "--description", "-d", help="Transaction description"),
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Add credits to a user's balance."""
    billing_add_credits_command(principal_id, amount, stripe_session_id, description, network, canister_id)


@registry_billing_app.command("deduct_credits")
def billing_deduct_credits(
    principal_id: str = typer.Option(..., "--principal", "-p", help="User principal ID"),
    amount: int = typer.Option(..., "--amount", "-a", help="Amount of credits to deduct"),
    description: str = typer.Option("Manual deduction", "--description", "-d", help="Transaction description"),
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Deduct credits from a user's balance."""
    billing_deduct_credits_command(principal_id, amount, description, network, canister_id)


@registry_billing_app.command("status")
def billing_status(
    network: str = typer.Option("local", "--network", "-n", help="Network to use"),
    canister_id: Optional[str] = typer.Option(
        None, "--canister-id", help="Registry canister ID"
    ),
) -> None:
    """Get overall billing status across all users."""
    billing_status_command(network, canister_id)


# Realm context management commands
@realm_app.command("ls")
def realm_ls(
    base_dir: Optional[str] = typer.Option(
        None, "--dir", "-d", help="Base directory to search for realms"
    ),
) -> None:
    """List all available realm folders."""
    from rich.table import Table
    
    console.print("[bold blue]📁 Available Realms[/bold blue]\n")
    
    realms = list_realm_folders(base_dir)
    
    if not realms:
        console.print(f"[yellow]No realm folders found in {base_dir or REALM_FOLDER}[/yellow]")
        console.print(f"\n[dim]💡 Create a realm with: realms create --realm-name <name>[/dim]")
        return
    
    # Get current realm folder to highlight it
    current_folder = get_current_realm_folder()
    
    # Create table
    table = Table(show_header=True, header_style="bold cyan")
    table.add_column("#", style="dim", width=3)
    table.add_column("Name", style="white")
    table.add_column("Network", style="cyan", width=10)
    table.add_column("Status", style="green", width=10)
    table.add_column("Canisters", justify="right", width=10)
    table.add_column("Modified", style="dim", width=20)
    
    for i, realm in enumerate(realms, 1):
        is_current = current_folder and realm["path"] == current_folder
        
        # Format status with color
        status = realm["status"]
        if status == "deployed":
            status_str = "[green]deployed[/green]"
        else:
            status_str = "[yellow]created[/yellow]"
        
        # Format name with indicator if current
        name = realm["name"]
        if is_current:
            name = f"[bold green]► {name}[/bold green]"
        
        # Format date
        date_str = ""
        if realm["created"]:
            date_str = realm["created"].strftime("%Y-%m-%d %H:%M")
        
        table.add_row(
            str(i),
            name,
            realm["network"],
            status_str,
            str(realm["canister_count"]) if realm["canister_count"] else "-",
            date_str,
        )
    
    console.print(table)
    
    if current_folder:
        console.print(f"\n[dim]Current realm: {current_folder}[/dim]")
    
    console.print(f"\n[dim]💡 Set active realm: realms realm set <# or name>[/dim]")


@realm_app.command("set")
def realm_set(
    realm_id: str = typer.Argument(help="Realm index number or folder name"),
    base_dir: Optional[str] = typer.Option(
        None, "--dir", "-d", help="Base directory to search for realms"
    ),
) -> None:
    """Set the current realm context by folder."""
    console.print("[bold blue]🏛️  Setting Realm Context[/bold blue]\n")

    realm = resolve_realm_by_id(realm_id, base_dir)
    
    if not realm:
        console.print(f"[red]❌ Realm not found: {realm_id}[/red]")
        console.print(f"\n[dim]💡 List available realms with: realms realm ls[/dim]")
        raise typer.Exit(1)
    
    # Set both the realm folder and network
    set_current_realm_folder(realm["path"])
    set_current_realm(realm["name"])
    if realm["network"] != "unknown":
        set_current_network(realm["network"])

    console.print(
        f"[green]✅ Realm context set to: [bold]{realm['name']}[/bold][/green]"
    )
    console.print(f"[dim]   Folder: {realm['path']}[/dim]")
    console.print(f"[dim]   Network: {realm['network']}[/dim]")
    console.print(f"[dim]   Status: {realm['status']}[/dim]")
    if realm["canister_count"]:
        console.print(f"[dim]   Canisters: {realm['canister_count']}[/dim]")


@realm_app.command("current")
def realm_current() -> None:
    """Show the current realm and network context."""
    console.print("[bold blue]📍 Current Context[/bold blue]\n")

    current_realm = get_current_realm()
    current_folder = get_current_realm_folder()
    current_network = get_current_network()

    if current_folder:
        console.print(f"[green]📁 Folder: [bold]{current_folder}[/bold][/green]")
        
        # Check if folder still exists and get current status
        from pathlib import Path
        folder_path = Path(current_folder)
        if folder_path.exists():
            realms = list_realm_folders()
            for r in realms:
                if r["path"] == current_folder:
                    console.print(f"[dim]   Name: {r['name']}[/dim]")
                    console.print(f"[dim]   Status: {r['status']}[/dim]")
                    if r["canister_count"]:
                        console.print(f"[dim]   Canisters: {r['canister_count']}[/dim]")
                    break
        else:
            console.print(f"[yellow]   ⚠️  Folder no longer exists[/yellow]")
    elif current_realm:
        # Legacy: realm set via registry
        try:
            realm_network, realm_canister = resolve_realm_details(current_realm)
            console.print(f"[green]🏛️  Realm: [bold]{current_realm}[/bold][/green]")
            console.print(f"[dim]   Network: {realm_network}[/dim]")
            console.print(f"[dim]   Canister: {realm_canister}[/dim]")
        except ValueError as e:
            console.print(f"[red]🏛️  Realm: [bold]{current_realm}[/bold] (⚠️  {e})[/red]")
    else:
        console.print("[dim]📁 Realm: Not set[/dim]")

    console.print(f"[cyan]🌐 Network: [bold]{current_network}[/bold][/cyan]")

    if not current_folder and not current_realm and current_network == "local":
        console.print(
            "\n[dim]💡 Set a realm with: realms realm ls && realms realm set <#>[/dim]"
        )


@realm_app.command("unset")
def realm_unset() -> None:
    """Clear the current realm context."""
    console.print("[bold blue]🔄 Clearing Realm Context[/bold blue]\n")

    current_folder = get_current_realm_folder()
    current_realm = get_current_realm()
    
    if current_folder or current_realm:
        if current_folder:
            unset_current_realm_folder()
            console.print(
                f"[green]✅ Cleared realm folder: [bold]{current_folder}[/bold][/green]"
            )
        if current_realm:
            unset_current_realm()
            console.print(
                f"[green]✅ Cleared realm context: [bold]{current_realm}[/bold][/green]"
            )
        console.print("[dim]Will use network context or defaults[/dim]")
    else:
        console.print("[yellow]No realm context set[/yellow]")


@realm_app.command("status")
def realm_status(
    realm_ref: Optional[str] = typer.Argument(
        None, help="Realm canister ID or name (e.g., 'Dominion' or '2lbfz-yiaaa-aaaac-qcyma-cai')"
    ),
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
) -> None:
    """Show canister IDs and URLs for a realm.
    
    Examples:
        realms realm status Dominion --network staging
        realms realm status 2lbfz-yiaaa-aaaac-qcyma-cai --network staging
        realms realm status  # Uses local folder context
    """
    console.print("[bold blue]🏛️  Realm Status[/bold blue]\n")

    # Get effective network
    effective_network, _ = get_effective_network_and_canister(network, None)
    
    # If a realm reference is provided, resolve it to canister ID
    if realm_ref:
        try:
            backend_canister_id, realm_name = resolve_realm_ref_to_canister_id(
                realm_ref, effective_network
            )
            console.print(f"[dim]Realm: {realm_name}[/dim]")
            console.print(f"[dim]Backend Canister: {backend_canister_id}[/dim]")
            console.print(f"[dim]Network: {effective_network}[/dim]\n")
            
            # For remote realms, show info from the registry
            _show_remote_realm_status(backend_canister_id, realm_name, effective_network)
            return
            
        except ValueError as e:
            console.print(f"[red]❌ {e}[/red]")
            raise typer.Exit(1)
    
    # No realm_ref provided - use local folder context (existing behavior)
    console.print(f"[dim]Network: {effective_network}[/dim]\n")

    # Load canister IDs
    try:
        import json
        import subprocess
        from pathlib import Path

        # Use effective cwd which respects realm folder context
        effective_cwd = get_effective_cwd()
        if effective_cwd:
            project_root = Path(effective_cwd)
            console.print(f"[dim]Realm folder: {project_root}[/dim]\n")
        else:
            project_root = get_project_root()
        
        # Initialize variables for local network
        local_port = "8000"  # Default port
        candid_ui_id = None
        
        # For local network, get canister IDs dynamically from dfx
        if effective_network == "local":
            # Get list of canisters from dfx.json
            dfx_json_path = project_root / "dfx.json"
            if not dfx_json_path.exists():
                console.print(
                    "[yellow]⚠️  dfx.json not found in project root[/yellow]"
                )
                console.print(
                    "[dim]Make sure you're in a Realms project directory[/dim]"
                )
                raise typer.Exit(1)
            
            with open(dfx_json_path, "r") as f:
                dfx_config = json.load(f)
            
            canister_names = list(dfx_config.get("canisters", {}).keys())
            
            # Get network configuration for port
            networks_config = dfx_config.get("networks", {})
            local_network_config = networks_config.get("local", {})
            bind_address = local_network_config.get("bind", "127.0.0.1:8000")
            # Extract port from bind address (format: "127.0.0.1:8000")
            local_port = bind_address.split(":")[-1] if ":" in bind_address else "8000"
            
            # Fetch canister IDs dynamically
            canister_ids = {}
            for canister_name in canister_names:
                try:
                    result = subprocess.run(
                        ["dfx", "canister", "id", canister_name],
                        capture_output=True,
                        text=True,
                        check=True,
                        timeout=5,
                        cwd=str(project_root)
                    )
                    canister_id = result.stdout.strip()
                    canister_ids[canister_name] = {"local": canister_id}
                except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
                    # Canister not deployed yet, skip it
                    continue
            
            # Get Candid UI canister ID for backend URLs
            candid_ui_id = None
            try:
                result = subprocess.run(
                    ["dfx", "canister", "id", "__Candid_UI"],
                    capture_output=True,
                    text=True,
                    check=True,
                    timeout=5,
                    cwd=str(project_root)
                )
                candid_ui_id = result.stdout.strip()
            except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
                # Candid UI not available, will fall back to default
                pass
            
            if not canister_ids:
                console.print(
                    "[yellow]⚠️  No canisters deployed on local network[/yellow]"
                )
                console.print(
                    "[dim]Deploy canisters first with: dfx deploy[/dim]"
                )
                raise typer.Exit(1)
        else:
            # For non-local networks, read from canister_ids.json
            canister_ids_path = project_root / "canister_ids.json"

            if not canister_ids_path.exists():
                console.print(
                    "[yellow]⚠️  canister_ids.json not found in project root[/yellow]"
                )
                console.print(
                    "[dim]Make sure you're in a Realms project directory or deploy first[/dim]"
                )
                raise typer.Exit(1)

            with open(canister_ids_path, "r") as f:
                canister_ids = json.load(f)

            # Check if network has any canisters
            has_canisters = False
            for canister_name in canister_ids:
                if effective_network in canister_ids[canister_name]:
                    has_canisters = True
                    break

            if not has_canisters:
                console.print(
                    f"[yellow]⚠️  No canisters found for network: {effective_network}[/yellow]"
                )
                console.print("[dim]Available networks:[/dim]")
                networks = set()
                for canister_data in canister_ids.values():
                    networks.update(canister_data.keys())
                for net in sorted(networks):
                    console.print(f"  - {net}")
                raise typer.Exit(1)

        # Create table
        table = Table(title=f"Realm Canisters on {effective_network.upper()}")
        table.add_column("Canister", style="cyan", no_wrap=True)
        table.add_column("Canister ID", style="green")
        table.add_column("URL", style="blue")

        # Add rows for each canister
        for canister_name, network_ids in sorted(canister_ids.items()):
            if effective_network in network_ids:
                canister_id = network_ids[effective_network]

                # Determine if this is a backend or frontend canister
                is_backend = "backend" in canister_name.lower()

                # Construct URL based on network and canister type
                if is_backend:
                    # Backend canisters use Candid UI
                    if effective_network == "ic":
                        candid_ui = "a4gq6-oaaaa-aaaab-qaa4q-cai"
                        url = f"https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.ic0.app/?id={canister_id}"
                    elif effective_network == "staging":
                        candid_ui = "a4gq6-oaaaa-aaaab-qaa4q-cai"
                        url = f"https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id={canister_id}"
                    elif effective_network == "local":
                        # For local, use dynamically fetched Candid UI and port
                        if candid_ui_id:
                            url = f"http://127.0.0.1:{local_port}/?canisterId={candid_ui_id}&id={canister_id}"
                        else:
                            # Fallback if Candid UI not found
                            url = f"http://127.0.0.1:{local_port}/?canisterId=<candid-ui>&id={canister_id}"
                    else:
                        # Other networks, use staging format
                        url = f"https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id={canister_id}"
                else:
                    # Frontend canisters use direct URLs
                    if effective_network == "ic":
                        url = f"https://{canister_id}.ic0.app"
                    elif effective_network == "staging":
                        url = f"https://{canister_id}.icp0.io"
                    elif effective_network == "local":
                        # Use recommended format for local
                        url = f"http://{canister_id}.localhost:{local_port}/"
                    else:
                        url = f"https://{canister_id}.icp0.io"

                table.add_row(canister_name, canister_id, url)

        console.print(table)

        # Add helpful notes
        console.print(
            f"\n[dim]💡 Frontend canisters can be accessed directly via their URLs[/dim]"
        )
        console.print(
            f"[dim]💡 Use 'realms realm extension' to call backend extension functions[/dim]"
        )

    except Exception as e:
        console.print(f"[red]❌ Error reading canister IDs: {e}[/red]")
        raise typer.Exit(1)


def _show_remote_realm_status(backend_canister_id: str, realm_name: str, network: str) -> None:
    """Show status for a remote realm (queried from registry)."""
    import subprocess
    
    # Construct URLs based on network
    if network == "ic":
        frontend_url = f"https://{backend_canister_id}.ic0.app"  # Actually need frontend ID
        backend_url = f"https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.ic0.app/?id={backend_canister_id}"
    elif network == "staging":
        frontend_url = f"https://{backend_canister_id}.icp0.io"  # Actually need frontend ID
        backend_url = f"https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id={backend_canister_id}"
    else:
        frontend_url = f"http://{backend_canister_id}.localhost:8000/"
        backend_url = f"http://127.0.0.1:8000/?canisterId=<candid-ui>&id={backend_canister_id}"
    
    # Create table
    table = Table(title=f"Realm '{realm_name}' on {network.upper()}")
    table.add_column("Component", style="cyan", no_wrap=True)
    table.add_column("Canister ID", style="green")
    table.add_column("URL", style="blue")
    
    table.add_row("Backend", backend_canister_id, backend_url)
    
    # Try to get realm info from backend to find frontend canister
    try:
        cmd = [
            "dfx", "canister", "call",
            "--network", network,
            backend_canister_id,
            "status"
        ]
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
        if result.returncode == 0:
            console.print(f"[dim]Backend is responding[/dim]\n")
    except Exception:
        pass
    
    console.print(table)
    
    console.print(f"\n[dim]💡 Use 'realms realm call {realm_name} <method> --network {network}' to call backend methods[/dim]")


# Create db subcommand group
db_app = typer.Typer(name="db", help="Database exploration and querying", invoke_without_command=True)
app.add_typer(db_app, name="db")


@db_app.callback()
def db_callback(
    ctx: typer.Context,
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name to connect to (overrides context)"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", "-f", help="Realm folder containing dfx.json (uses current realm context if not specified)"
    ),
) -> None:
    """Explore the Realm database. Use subcommands for specific operations or run without subcommand for interactive mode."""
    # Store network, canister, and folder in context for subcommands
    ctx.obj = {"network": network, "canister": canister, "folder": folder}
    
    # If no subcommand is provided, run interactive explorer
    if ctx.invoked_subcommand is None:
        db_command(network, canister, folder)


@db_app.command("get")
def db_get(
    ctx: typer.Context,
    entity_type: str = typer.Argument(help="Entity type (e.g., User, Transfer, Mandate)"),
    entity_id: Optional[str] = typer.Argument(None, help="Optional entity ID to retrieve specific entity"),
) -> None:
    """Get entities from the database and output as JSON.
    
    Examples:
        realms db get User              # Get all users
        realms db get User user1        # Get specific user by ID
        realms db get Transfer          # Get all transfers
    """
    # Get network, canister, and folder from context
    network = ctx.obj.get("network") if ctx.obj else None
    canister = ctx.obj.get("canister") if ctx.obj else None
    folder = ctx.obj.get("folder") if ctx.obj else None
    
    db_get_command(entity_type, entity_id, network, canister, folder)


@db_app.command("find")
def db_find(
    ctx: typer.Context,
    entity_type: str = typer.Argument(help="Entity type (e.g., User, Transfer, Mandate)"),
    filters: List[str] = typer.Argument(help="Field=value filters (e.g., id=system status=active)"),
) -> None:
    """Find entities matching given field criteria.
    
    Searches for entities where all specified field values match.
    
    Examples:
        realms db find User id=system
        realms db find Transfer status=completed
        realms db find Invoice recipient=user123 status=Pending
    """
    network = ctx.obj.get("network") if ctx.obj else None
    canister = ctx.obj.get("canister") if ctx.obj else None
    folder = ctx.obj.get("folder") if ctx.obj else None
    
    db_find_command(entity_type, filters, network, canister, folder)


@db_app.command("schema")
def db_schema(
    ctx: typer.Context,
) -> None:
    """Get the database schema showing all entity types, fields, and relationships.
    
    Outputs JSON with entity types, field definitions, and relationship mappings.
    This is useful for discovering what data types are available in the realm.
    
    Examples:
        realms db schema                    # Get full schema
        realms db schema -n staging         # Get schema from staging network
    """
    # Get network, canister, and folder from context
    network = ctx.obj.get("network") if ctx.obj else None
    canister = ctx.obj.get("canister") if ctx.obj else None
    folder = ctx.obj.get("folder") if ctx.obj else None
    
    db_schema_command(network, canister, folder)


@app.command("shell")
def shell(
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name to connect to (overrides context)"
    ),
    file: Optional[str] = typer.Option(
        None, "--file", "-f", help="Execute Python file instead of interactive shell"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", help="Realm folder to use (overrides context)"
    ),
) -> None:
    """Start an interactive Python shell connected to the Realms backend canister or execute a Python file."""
    # Get effective network and canister from context
    effective_network, effective_canister = get_effective_network_and_canister(
        network, canister
    )
    # Get effective working directory from context
    effective_cwd = get_effective_cwd(folder)
    shell_command(effective_network, effective_canister, file, effective_cwd)


@app.command("run")
def run(
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name to connect to (overrides context)"
    ),
    file: Optional[str] = typer.Option(
        None, "--file", "-f", help="Execute Python file instead of interactive shell"
    ),
    wait: bool = typer.Option(
        False, "--wait", "-w", help="Wait for async tasks to complete (default 600s timeout)"
    ),
    wait_timeout: Optional[int] = typer.Option(
        None, "--wait-timeout", help="Custom timeout in seconds for async task waiting"
    ),
    every: Optional[int] = typer.Option(
        None, "--every", help="Schedule task to run every N seconds (creates recurring task)"
    ),
    after: Optional[int] = typer.Option(
        None, "--after", help="Delay first run by N seconds (default: 5s)"
    ),
    config: Optional[str] = typer.Option(
        None, "--config", help="Multi-step task configuration file (JSON)"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", help="Realm folder to use (overrides context)"
    ),
) -> None:
    """Start an interactive Python shell connected to the Realms backend canister or execute a Python file (with async task waiting support)."""
    # Get effective network and canister from context
    effective_network, effective_canister = get_effective_network_and_canister(
        network, canister
    )
    # Determine the actual wait timeout (0 signals to use default in run_command)
    actual_wait = None
    if wait or wait_timeout is not None:
        actual_wait = wait_timeout if wait_timeout is not None else 0
    
    run_command(effective_network, effective_canister, file, actual_wait, every, after, config, folder)


# Create network subcommand group
network_app = typer.Typer(name="network", help="Network context management")
app.add_typer(network_app, name="network")


@network_app.command("set")
def network_set(
    network_name: str = typer.Argument(
        help="Network name to set as current context (local, ic, testnet, etc.)"
    ),
) -> None:
    """Set the current network context."""
    console.print("[bold blue]🌐 Setting Network Context[/bold blue]\n")

    set_current_network(network_name)
    console.print(
        f"[green]✅ Network context set to: [bold]{network_name}[/bold][/green]"
    )

    # Show warning if realm context overrides network
    current_realm = get_current_realm()
    if current_realm:
        console.print(
            f"[yellow]⚠️  Note: Realm context '[bold]{current_realm}[/bold]' may override network setting[/yellow]"
        )


@network_app.command("current")
def network_current() -> None:
    """Show the current network context."""
    console.print("[bold blue]📍 Current Network Context[/bold blue]\n")

    current_network = get_current_network()
    console.print(f"[cyan]🌐 Network: [bold]{current_network}[/bold][/cyan]")

    if current_network == "local":
        console.print("[dim]💡 This is the default network[/dim]")


@network_app.command("unset")
def network_unset() -> None:
    """Clear the current network context (reverts to 'local')."""
    console.print("[bold blue]🔄 Clearing Network Context[/bold blue]\n")

    current_network = get_current_network()
    if current_network != "local":
        unset_current_network()
        console.print(
            f"[green]✅ Cleared network context: [bold]{current_network}[/bold][/green]"
        )
        console.print("[dim]Reverted to default: local[/dim]")
    else:
        console.print("[yellow]Already using default network: local[/yellow]")


# Create ps subcommand group
ps_app = typer.Typer(name="ps", help="Manage scheduled tasks")
app.add_typer(ps_app, name="ps")


@ps_app.command("ls")
def ps_ls(
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name (overrides context)"
    ),
    verbose: bool = typer.Option(
        False, "--verbose", "-v", help="Show detailed information"
    ),
    output: str = typer.Option(
        "json", "--output", "-o", help="Output format: 'json' (default) or 'table'"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", "-f", help="Realm folder containing dfx.json (uses current realm context if not specified)"
    ),
) -> None:
    """List all scheduled tasks."""
    effective_network, effective_canister = get_effective_network_and_canister(
        network, canister
    )
    ps_ls_command(effective_network, effective_canister, verbose, output, folder)


@ps_app.command("start")
def ps_start(
    task_id: str = typer.Argument(help="Task ID (full or partial) to start"),
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name (overrides context)"
    ),
    output: str = typer.Option(
        "json", "--output", "-o", help="Output format: 'json' (default) or 'table'"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", "-f", help="Realm folder containing dfx.json (uses current realm context if not specified)"
    ),
) -> None:
    """Start a scheduled task."""
    effective_network, effective_canister = get_effective_network_and_canister(
        network, canister
    )
    ps_start_command(task_id, effective_network, effective_canister, output, folder)


@ps_app.command("kill")
def ps_kill(
    task_id: str = typer.Argument(help="Task ID (full or partial) to stop"),
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name (overrides context)"
    ),
    output: str = typer.Option(
        "json", "--output", "-o", help="Output format: 'json' (default) or 'table'"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", "-f", help="Realm folder containing dfx.json (uses current realm context if not specified)"
    ),
) -> None:
    """Stop a scheduled task."""
    effective_network, effective_canister = get_effective_network_and_canister(
        network, canister
    )
    ps_kill_command(task_id, effective_network, effective_canister, output, folder)


@ps_app.command("logs")
def ps_logs(
    task_id: str = typer.Argument(help="Task ID (full or partial) to view logs"),
    network: Optional[str] = typer.Option(
        None, "--network", "-n", help="Network to use (overrides context)"
    ),
    canister: Optional[str] = typer.Option(
        None, "--canister", "-c", help="Canister name (overrides context)"
    ),
    tail: int = typer.Option(
        20, "--tail", "-t", help="Number of recent executions to show (default: 20)"
    ),
    output: str = typer.Option(
        "json", "--output", "-o", help="Output format: 'json' (default) or 'table'"
    ),
    follow: bool = typer.Option(
        False, "--follow", help="Follow logs in real-time (use with Ctrl+C to stop)"
    ),
    output_file: Optional[str] = typer.Option(
        None, "--output-file", help="Write logs to file"
    ),
    limit: int = typer.Option(
        100, "--limit", "-l", help="Maximum number of log entries to retrieve (default: 100, max: 1000)"
    ),
    from_entry: int = typer.Option(
        0, "--from", help="Start index for pagination (default: 0)"
    ),
    folder: Optional[str] = typer.Option(
        None, "--folder", help="Realm folder containing dfx.json (uses current realm context if not specified)"
    ),
) -> None:
    """View execution logs for a task.
    
    By default, outputs JSON. Use --output table for table format.
    Use --follow for continuous task-specific logs.
    Use --output-file to save logs to a file.
    Use --limit and --from for pagination of large log sets.
    """
    effective_network, effective_canister = get_effective_network_and_canister(
        network, canister
    )
    ps_logs_command(
        task_id, 
        effective_network, 
        effective_canister, 
        tail, 
        output,
        follow,
        output_file,
        limit,
        from_entry,
        folder
    )


@app.command("clean")
def clean(
    yes: bool = typer.Option(
        False, "--yes", "-y", help="Skip confirmation prompt"
    ),
) -> None:
    """Clean up dfx state and .realms directory.
    
    Runs scripts/clean_dfx.sh and removes the .realms directory.
    """
    import shutil
    import subprocess
    from pathlib import Path
    
    if not yes:
        confirm = typer.confirm(
            "⚠️  This will stop dfx, clean dfx state, and remove .realms directory. Continue?"
        )
        if not confirm:
            console.print("[yellow]Aborted.[/yellow]")
            raise typer.Exit(0)
    
    console.print("[bold blue]🧹 Cleaning up...[/bold blue]\n")
    
    # Run scripts/clean_dfx.sh
    scripts_dir = Path("scripts")
    clean_script = scripts_dir / "clean_dfx.sh"
    
    if clean_script.exists():
        console.print("Running scripts/clean_dfx.sh...")
        result = subprocess.run(
            ["bash", str(clean_script)],
            capture_output=False
        )
        if result.returncode == 0:
            console.print("[green]✅ clean_dfx.sh completed[/green]")
        else:
            console.print(f"[yellow]⚠️  clean_dfx.sh exited with code {result.returncode}[/yellow]")
    else:
        console.print(f"[yellow]⚠️  {clean_script} not found, skipping[/yellow]")
    
    # Remove .realms directory
    realms_dir = Path(".realms")
    if realms_dir.exists():
        console.print("Removing .realms directory...")
        shutil.rmtree(realms_dir)
        console.print("[green]✅ .realms directory removed[/green]")
    else:
        console.print("[dim].realms directory does not exist[/dim]")
    
    console.print("\n[green]🎉 Cleanup complete![/green]")


@app.command("version")
def version() -> None:
    """Show version information."""
    import subprocess
    from pathlib import Path
    from . import __version__
    
    try:
        from . import __commit__, __commit_date__
        print(f"{__version__}+{__commit__}")
        print(__commit_date__)
        return
    except ImportError:
        pass
    
    try:
        cli_dir = Path(__file__).parent.parent.parent
        commit_hash = subprocess.check_output(
            ["git", "rev-parse", "--short", "HEAD"],
            cwd=cli_dir,
            stderr=subprocess.DEVNULL
        ).decode().strip()
        commit_date = subprocess.check_output(
            ["git", "log", "-1", "--format=%cI"],
            cwd=cli_dir,
            stderr=subprocess.DEVNULL
        ).decode().strip()
        print(f"{__version__}+{commit_hash}")
        print(commit_date)
    except Exception:
        print(__version__)


@app.callback()
def main(
    ctx: typer.Context,
    verbose: bool = typer.Option(
        False, "--verbose", "-v", help="Enable verbose output"
    ),
    version_flag: bool = typer.Option(False, "--version", help="Show version and exit"),
) -> None:
    """
    Realms CLI - Deploy and manage Realms projects.

    🏛️ Build and deploy digital government platforms on the Internet Computer.

    Quick start:
    1. Copy and modify example_realm_config.json
    2. realms realm deploy --file your_config.json
    3. realms status
    """
    if version_flag:
        version()
        raise typer.Exit()

    if verbose:
        console.print("[dim]Verbose mode enabled[/dim]")
    
    # If no command was provided, show error and help suggestion
    if ctx.invoked_subcommand is None:
        console.print("[red]Error: No command provided.[/red]")
        console.print("\n💡 Try running [cyan]realms --help[/cyan] to see available commands.")
        raise typer.Exit(code=1)


if __name__ == "__main__":
    app()
