#!/usr/bin/env python3
"""
Command Line Interface handlers for Hippius SDK.

This module provides handler functions for CLI commands, including
file operations, marketplace interactions, configuration management, etc.
"""

import asyncio
import base64
import getpass
import json
import math
import os
import tempfile
import time
from typing import Any, List, Optional

from substrateinterface import Keypair, SubstrateInterface

from hippius_sdk import (
    HippiusClient,
    delete_account,
    format_size,
    get_account_address,
    get_active_account,
    get_all_config,
    get_config_value,
    get_seed_phrase,
    list_accounts,
    load_config,
    reset_config,
    save_config,
    set_active_account,
    set_config_value,
)
from hippius_sdk.cli_parser import get_default_address
from hippius_sdk.cli_rich import (
    console,
    create_progress,
    error,
    info,
    log,
    print_panel,
    print_table,
    success,
    warning,
)
from hippius_sdk.errors import (
    HippiusAlreadyDeletedError,
    HippiusFailedIPFSUnpin,
    HippiusFailedSubstrateDelete,
    HippiusMetadataError,
)

try:
    import nacl.secret
    import nacl.utils
except ImportError:
    ENCRYPTION_AVAILABLE = False
else:
    ENCRYPTION_AVAILABLE = True


# Client creation helper function
def create_client(args: Any) -> HippiusClient:
    """Create a HippiusClient instance from command line arguments."""
    # Process encryption flags
    encrypt = None
    if hasattr(args, "encrypt") and args.encrypt:
        encrypt = True
    elif hasattr(args, "no_encrypt") and args.no_encrypt:
        encrypt = False

    # Process encryption key if provided
    encryption_key = None
    if hasattr(args, "encryption_key") and args.encryption_key:
        try:
            encryption_key = base64.b64decode(args.encryption_key)
            if hasattr(args, "verbose") and args.verbose:
                print("Using provided encryption key")
        except Exception as e:
            print(f"Warning: Could not decode encryption key: {e}")
            print("Using default encryption key from configuration if available")

    # Get API URL based on local_ipfs flag if the flag exists
    api_url = None
    if hasattr(args, "local_ipfs") and args.local_ipfs:
        api_url = "http://localhost:5001"
    elif hasattr(args, "api_url"):
        api_url = args.api_url
    elif hasattr(args, "ipfs_api"):
        api_url = args.ipfs_api

    # Get HIPPIUS_KEY if provided
    hippius_key = None
    if hasattr(args, "hippius_key") and args.hippius_key:
        hippius_key = args.hippius_key

    # Determine if we need to use password based on the command
    # Only use password for: store, download, delete, erasure-code (unless --no-publish), reconstruct
    password = None
    hippius_key_password = None

    # First check if hippius_key_password is provided
    if hasattr(args, "hippius_key_password") and args.hippius_key_password:
        hippius_key_password = args.hippius_key_password
    # Otherwise check if old password argument is provided (for backward compatibility)
    elif hasattr(args, "password") and args.password:
        password = args.password
    # Otherwise, decide based on the command
    elif hasattr(args, "command"):
        command = args.command
        needs_password = False

        # Check if this is one of the commands that needs a password
        if command in [
            "store",
            "store-dir",
            "download",
            "delete",
            "delete-dir",
            "reconstruct",
            "credits",
            "files",
            "ec-files",
        ]:
            needs_password = True
        # Special case for erasure-code - only needs password if we're publishing
        elif command == "erasure-code" and not (
            hasattr(args, "no_publish") and args.no_publish
        ):
            needs_password = True
        # Special case for pin - only needs password if we're publishing
        elif command == "pin" and not (hasattr(args, "no_publish") and args.no_publish):
            needs_password = True

        # If this command doesn't need password access, set to empty string to skip prompting
        if not needs_password:
            # Use empty string to indicate "skip password prompt" to the config system
            password = ""
            hippius_key_password = ""

    # Initialize client with provided parameters
    client = HippiusClient(
        ipfs_api_url=api_url,
        hippius_key=hippius_key,
        hippius_key_password=hippius_key_password,
        account_name=args.account if hasattr(args, "account") else None,
        encrypt_by_default=encrypt,
        encryption_key=encryption_key,
    )

    return client


#
# IPFS File Operation Handlers
#


async def handle_download(
    client: HippiusClient, cid: str, output_path: str, decrypt: Optional[bool] = None
) -> int:
    """Handle the download command"""
    info(f"Downloading [bold cyan]{cid}[/bold cyan] to [bold]{output_path}[/bold]...")

    # Use the enhanced download method which returns formatted information
    result = await client.download_file(cid, output_path, decrypt=decrypt)

    # Create a success panel with download information
    details = [
        f"Download successful in [bold green]{result['elapsed_seconds']}[/bold green] seconds!",
        f"Saved to: [bold]{result['output_path']}[/bold]",
        f"Size: [bold cyan]{result['size_bytes']:,}[/bold cyan] bytes ([bold cyan]{result['size_formatted']}[/bold cyan])",
    ]

    # Add details about content type
    if result.get("is_directory", False):
        details.append("[bold green]Content type: Directory[/bold green]")
    else:
        details.append("[bold blue]Content type: File[/bold blue]")

    if result.get("decrypted"):
        details.append("[bold yellow]File was decrypted during download[/bold yellow]")

    print_panel("\n".join(details), title="Download Complete")

    return 0


async def handle_exists(client: HippiusClient, cid: str) -> int:
    """Handle the exists command"""
    info(f"Checking if CID [bold cyan]{cid}[/bold cyan] exists on IPFS...")
    result = await client.exists(cid)

    # Use the formatted CID from the result
    formatted_cid = result["formatted_cid"]
    exists = result["exists"]

    if exists:
        success(f"CID [bold cyan]{formatted_cid}[/bold cyan] exists on IPFS")

        # Display download command in a panel
        command = f"[bold green underline]hippius download {formatted_cid} <output_path>[/bold green underline]"
        print_panel(command, title="Download Command")
    else:
        error(f"CID [bold cyan]{formatted_cid}[/bold cyan] does not exist on IPFS")

    return 0


async def handle_cat(
    client: HippiusClient, cid: str, max_size: int, decrypt: Optional[bool] = None
) -> int:
    """Handle the cat command"""
    info(f"Displaying content of CID [bold cyan]{cid}[/bold cyan]...")
    with tempfile.NamedTemporaryFile() as temp:
        temp_path = temp.name
        download_result = await client.download_file(cid, temp_path, decrypt=decrypt)
        file_size = os.path.getsize(temp_path)

        # Read content based on max size
        with open(temp_path, "rb") as f:
            content = f.read(max_size)

        # Try to display as text, fall back to binary info
        try:
            decoded = content.decode("utf-8")
            log(
                f"\nContent (first [bold]{min(max_size, file_size):,}[/bold] bytes):",
                style="blue",
            )
            console.print("--------------------------------------------", style="dim")
            console.print(decoded)
            console.print("--------------------------------------------", style="dim")
        except UnicodeDecodeError:
            log("\nBinary content (showing size information only):", style="yellow")
            log(
                f"Total size: [bold cyan]{file_size:,}[/bold cyan] bytes ([bold cyan]{download_result['size_formatted']}[/bold cyan])"
            )
            log("Content type appears to be binary", style="yellow")

        notes = []
        if file_size > max_size:
            notes.append(
                f"Content truncated. Total file size: [bold]{file_size:,}[/bold] bytes"
            )
            notes.append(
                f"Use '[bold]hippius download {cid} <output_path>[/bold]' to download the entire file"
            )

        if download_result.get("decrypted"):
            notes.append(
                "[bold yellow]File was decrypted during download[/bold yellow]"
            )

        if notes:
            print_panel("\n".join(notes), title="Notes")


async def handle_store(
    client: HippiusClient,
    file_path: str,
    miner_ids: Optional[List[str]] = None,
    encrypt: Optional[bool] = None,
    publish: bool = True,
) -> int:
    """Handle the store command (upload file to IPFS and store on Substrate)"""
    if not os.path.exists(file_path):
        error(f"File [bold]{file_path}[/bold] does not exist")
        return 1

    if not os.path.isfile(file_path):
        error(f"[bold]{file_path}[/bold] is not a file")
        return 1

    # Get file size for display
    file_size = os.path.getsize(file_path)
    file_name = os.path.basename(file_path)

    # Format size for display
    if file_size >= 1024 * 1024:
        size_formatted = f"{file_size / (1024 * 1024):.2f} MB"
    else:
        size_formatted = f"{file_size / 1024:.2f} KB"

    # Upload information panel
    upload_info = [
        f"File: [bold]{file_name}[/bold]",
        f"Size: [bold cyan]{size_formatted}[/bold cyan] ({file_size:,} bytes)",
    ]

    # Add encryption status
    if encrypt is True:
        upload_info.append("[bold green]Encryption: Enabled[/bold green]")
    elif encrypt is False:
        upload_info.append("[bold red]Encryption: Disabled[/bold red]")
    else:
        upload_info.append(
            "[bold yellow]Encryption: Using default setting[/bold yellow]"
        )

    # Add publishing status
    if not publish:
        upload_info.append(
            "[bold yellow]Publishing: Disabled (local upload only)[/bold yellow]"
        )
        log(
            "\nUpload will be local only - not publishing to blockchain or pinning to IPFS"
        )
    else:
        upload_info.append(
            "[bold green]Publishing: Enabled (publishing to blockchain)[/bold green]"
        )

    # Parse miner IDs if provided
    miner_id_list = None
    if miner_ids:
        miner_id_list = [m.strip() for m in miner_ids if m.strip()]
        upload_info.append(
            f"Targeting [bold]{len(miner_id_list)}[/bold] miners for storage"
        )

    # Display upload information panel
    print_panel("\n".join(upload_info), title="Upload Operation")

    # Create progress for the upload process
    with create_progress() as progress:
        # Add a task for the upload
        task = progress.add_task("[cyan]Uploading...", total=100)

        # We can't track actual progress from the client.store_file method yet,
        # so we'll update the progress periodically
        start_time = time.time()

        # Create a task to update the progress while waiting for the upload
        async def update_progress():
            while not progress.finished:
                # Since we don't have actual progress data, we'll use time as a proxy
                # The progress will move faster at first, then slow down
                elapsed = time.time() - start_time
                # Use a logarithmic function to simulate progress
                # This is just an estimation and not actual progress
                pct = min(95, 100 * (1 - 1 / (1 + elapsed / 10)))
                progress.update(task, completed=pct)
                await asyncio.sleep(0.1)

        # Start the progress updater task
        updater = asyncio.create_task(update_progress())

        try:
            # Use the upload_file method to get the CID
            result = await client.upload_file(
                file_path=file_path,
                encrypt=encrypt,
            )

            # If publishing is enabled, pin to Hippius API
            if publish and result.get("cid"):
                try:
                    # Pin the file to Hippius API
                    await client.api_client.pin_file(result["cid"], filename=file_name)

                    log("\n[green]File has been pinned to Hippius via API[/green]")

                except Exception as e:
                    warning(f"Failed to pin file to Hippius API: {str(e)}")

            progress.update(task, completed=100)
            updater.cancel()

            elapsed_time = time.time() - start_time

            # Success panel with results
            success_info = [
                f"Upload completed in [bold green]{elapsed_time:.2f}[/bold green] seconds!",
                f"IPFS CID: [bold cyan]{result['cid']}[/bold cyan]",
            ]

            # Always add the gateway URL
            # Use IPFS API URL for display

            if result.get("encrypted"):
                success_info.append(
                    "[bold yellow]File was encrypted during upload[/bold yellow]"
                )

            if not publish:
                success_info.append(
                    "[bold yellow]File was uploaded locally only (not published to blockchain)[/bold yellow]"
                )

            print_panel("\n".join(success_info), title="Upload Successful")

            # If we stored in the marketplace
            if "transaction_hash" in result:
                log(
                    f"\nStored in marketplace. Transaction hash: [bold]{result['transaction_hash']}[/bold]"
                )

            # Display download command in a panel
            command = f"[bold green underline]hippius download {result['cid']} <output_path>[/bold green underline]"
            print_panel(command, title="Download Command")

            return 0

        except Exception as e:
            # Cancel the updater task in case of error
            updater.cancel()
            error(f"Upload failed: {str(e)}")


async def handle_store_dir(
    client: HippiusClient,
    dir_path: str,
    miner_ids: Optional[List[str]] = None,
    encrypt: Optional[bool] = None,
    publish: bool = True,
) -> int:
    """Handle the store directory command"""
    if not os.path.exists(dir_path):
        error(f"Directory [bold]{dir_path}[/bold] does not exist")
        return 1

    if not os.path.isdir(dir_path):
        error(f"[bold]{dir_path}[/bold] is not a directory")
        return 1

    # Upload information panel
    upload_info = [f"Directory: [bold]{dir_path}[/bold]"]

    # Add encryption status
    if encrypt is True:
        upload_info.append("[bold green]Encryption: Enabled[/bold green]")
    elif encrypt is False:
        upload_info.append("[bold red]Encryption: Disabled[/bold red]")
    else:
        upload_info.append(
            "[bold yellow]Encryption: Using default setting[/bold yellow]"
        )

    # Parse miner IDs if provided
    miner_id_list = None
    if miner_ids:
        miner_id_list = [m.strip() for m in miner_ids if m.strip()]
        upload_info.append(
            f"Targeting [bold]{len(miner_id_list)}[/bold] miners for storage"
        )

    # Display upload information panel
    print_panel("\n".join(upload_info), title="Directory Upload Operation")

    # Create progress for the directory upload process
    with create_progress() as progress:
        # Add a task for the directory upload
        task = progress.add_task("[cyan]Uploading directory...", total=100)

        # We can't track actual progress from the client.store_directory method yet,
        # so we'll update the progress periodically
        start_time = time.time()

        # Create a task to update the progress while waiting for the upload
        async def update_progress():
            while not progress.finished:
                # Since we don't have actual progress data, we'll use time as a proxy
                # The progress will move faster at first, then slow down
                elapsed = time.time() - start_time
                # Use a logarithmic function to simulate progress
                # This is just an estimation and not actual progress
                pct = min(95, 100 * (1 - 1 / (1 + elapsed / 10)))
                progress.update(task, completed=pct)
                await asyncio.sleep(0.1)

        # Start the progress updater task
        updater = asyncio.create_task(update_progress())

        try:
            # Upload info message based on publish flag
            if not publish:
                upload_info.append(
                    "[bold yellow]Publishing: Disabled (local upload only)[/bold yellow]"
                )
                log(
                    "\nUpload will be local only - not publishing to blockchain or pinning to IPFS"
                )
            else:
                upload_info.append(
                    "[bold green]Publishing: Enabled (publishing to blockchain)[/bold green]"
                )

            # Display updated upload information panel
            print_panel("\n".join(upload_info), title="Directory Upload Operation")

            # Use the store_directory method
            result = await client.ipfs_client.upload_directory(
                dir_path=dir_path,
                encrypt=encrypt,
            )

            # Skip publishing to blockchain if publish is False
            if not publish:
                # Remove any blockchain-related data from result to ensure we don't try to use it
                if "transaction_hash" in result:
                    del result["transaction_hash"]
            else:
                # If we want to publish, pin to Hippius API
                try:
                    # Use IPFS API URL for display

                    # Pin the directory root CID to Hippius API
                    await client.api_client.pin_file(result["cid"])

                    log("\n[green]Directory has been pinned to Hippius via API[/green]")

                except Exception as e:
                    warning(f"Failed to pin directory to Hippius API: {str(e)}")

            # Complete the progress
            progress.update(task, completed=100)
            # Cancel the updater task
            updater.cancel()

            elapsed_time = time.time() - start_time

            # Success panel with results
            success_info = [
                f"Upload completed in [bold green]{elapsed_time:.2f}[/bold green] seconds!",
                f"Directory CID: [bold cyan]{result['cid']}[/bold cyan]",
            ]

            # Always add the gateway URL
            # Use IPFS API URL for display

            # Add encryption and publish status to success info
            if result.get("encrypted"):
                success_info.append(
                    "[bold yellow]Directory was encrypted during upload[/bold yellow]"
                )

            if not publish:
                success_info.append(
                    "[bold yellow]Directory was uploaded locally only (not published to blockchain)[/bold yellow]"
                )

            print_panel("\n".join(success_info), title="Directory Upload Successful")

            # Display uploaded files in a table
            if "files" in result:
                table_data = []
                for i, file_info in enumerate(result["files"], 1):
                    table_data.append(
                        {
                            "Index": str(i),
                            "Filename": file_info["name"],
                            "CID": file_info["cid"],
                        }
                    )

                print_table(
                    f"Uploaded {len(result['files'])} Files",
                    table_data,
                    ["Index", "Filename", "CID"],
                )

            # If publishing is enabled and we stored in the marketplace
            if publish:
                # We only include transaction hash stuff if we actually created a blockchain transaction
                if "transaction_hash" in result:
                    log(
                        f"\nStored in marketplace. Transaction hash: [bold]{result['transaction_hash']}[/bold]"
                    )
                else:
                    # If publish is true but no transaction hash, just indicate files were published to IPFS
                    log("\n[green]Directory was published to IPFS network.[/green]")
            elif not publish:
                log(
                    "\n[yellow]Files were uploaded locally only. No blockchain publication or IPFS pinning.[/yellow]"
                )

            return 0

        except Exception as e:
            # Cancel the updater task in case of error
            updater.cancel()
            error(f"Directory upload failed: {str(e)}")
            return 1


async def handle_credits(
    client: HippiusClient, account_address: Optional[str] = None
) -> int:
    """Handle the credits command"""
    info("Checking credits for the authenticated account...")

    if account_address:
        warning(
            "Note: The credits command checks balance for the authenticated HIPPIUS_KEY account."
        )
        warning("The account_address parameter is ignored.")

    balance_data = await client.api_client.get_account_balance()

    credits = balance_data.get("balance", 0)
    # Convert to float if it's a string
    if isinstance(credits, str):
        credits = float(credits)

    # Create a panel with credit information
    credit_info = [
        f"Credit balance: [bold green]{credits:.2f} USD[/bold green]",
    ]

    # Add account info if available in response
    if "account" in balance_data:
        credit_info.append(f"Account: [bold cyan]{balance_data['account']}[/bold cyan]")

    print_panel("\n".join(credit_info), title="Account Credits")

    return 0


async def handle_files(
    client: HippiusClient,
    account_address: Optional[str] = None,
    show_all_miners: bool = False,
    file_cid: str = None,
    include_pending: bool = False,
    search: Optional[str] = None,
    ordering: Optional[str] = None,
    page: Optional[int] = None,
) -> int:
    """Handle the files command"""
    # Always use API client for listing files
    info("Fetching files from API...")
    files = await client.api_client.list_files(
        cid=file_cid,
        include_pending=include_pending,
        search=search,
        ordering=ordering,
        page=page,
    )

    if not files:
        info("No files found")
        return 0

    success(f"Found [bold]{len(files)}[/bold] files")

    for file in files:
        log(f"\n[bold cyan]CID:[/bold cyan] {file.get('cid', 'N/A')}")
        log(f"[bold]File ID:[/bold] {file.get('file_id', 'N/A')}")
        log(f"[bold]Name:[/bold] {file.get('original_name', 'N/A')}")
        log(f"[bold]Size:[/bold] {file.get('size_bytes', 0)} bytes")
        log(f"[bold]Status:[/bold] {file.get('status', 'N/A')}")
        log(f"[bold]Active Replicas:[/bold] {file.get('active_replica_count', 0)}")
        log(f"[bold]Updated:[/bold] {file.get('updated_at', 'N/A')}")

    return 0


async def handle_ec_files(
    client: HippiusClient,
    account_address: Optional[str] = None,
    show_all_miners: bool = False,
    show_chunks: bool = False,
    filter_metadata_cid: str = None,
) -> int:
    """Handle the ec-files command"""
    if account_address is None:
        # If no address provided, first try to get from keypair (if available)
        if False and None is not None:
            account_address = None.ss58_address
        else:
            # Use the active account address instead of default address
            from hippius_sdk.config import get_account_address, get_active_account

            active_account = get_active_account()
            if active_account:
                account_address = get_account_address(active_account)

            if not account_address:
                error("No account address provided and client has no keypair.")
                info(
                    "Please provide an account address with '--account_address' or set an active account with:"
                )
                log(
                    "  [bold green underline]hippius account switch <account_name>[/bold green underline]"
                )
                return 1

    info(f"Getting erasure-coded files for account: [bold]{account_address}[/bold]")

    # Get all files from the marketplace
    files = await None

    # Separate metadata files and chunks
    ec_metadata_files = []
    chunk_files = []

    for file in files:
        if file["file_name"].endswith(".ec_metadata"):
            ec_metadata_files.append(file)
        elif file["file_name"].endswith(".ec"):
            chunk_files.append(file)

    if not ec_metadata_files:
        info("No erasure-coded files found for this account")
        return 0

    # Display summary
    success(f"Found [bold]{len(ec_metadata_files)}[/bold] erasure-coded files")
    if chunk_files:
        log(f"Found [bold]{len(chunk_files)}[/bold] chunk files")

    # Store metadata CIDs for reconstruction command at the end
    metadata_cids = []

    # Process each metadata file
    for i, metadata_file in enumerate(ec_metadata_files, 1):
        metadata_cid = metadata_file["cid"]

        if filter_metadata_cid and metadata_cid != filter_metadata_cid:
            continue

        metadata_file_name = metadata_file["file_name"]
        metadata_size = metadata_file["file_size"]
        metadata_size_formatted = metadata_file["size_formatted"]

        # Store metadata CID for reconstruction command
        metadata_cids.append(metadata_cid)

        # Basic file info panel (always show this)
        file_info = [
            f"Metadata filename: [bold]{metadata_file_name}[/bold]",
            f"Metadata CID: [bold cyan]{metadata_cid}[/bold cyan]",
            f"Metadata size: [bold]{metadata_size}[/bold] bytes ([bold cyan]{metadata_size_formatted}[/bold cyan])",
            f"Selected validator: {metadata_file['selected_validator']}",
        ]

        # Add miners info if available and requested - with consistent formatting
        if show_all_miners and metadata_file["miner_ids"]:
            miners = metadata_file["miner_ids"]
            file_info.append(f"Metadata stored on [bold]{len(miners)}[/bold] miners:")
            miners_list = []
            for j, miner in enumerate(miners, 1):
                miners_list.append(f"  {j}. {miner}")
            file_info.append("\n".join(miners_list))

        # If show_chunks is enabled, download the metadata file and get chunk information
        if show_chunks:
            with tempfile.NamedTemporaryFile() as temp:
                temp_path = temp.name

                await client.download_file(metadata_cid, temp_path)

                # Open and parse the metadata file
                with open(temp_path, "r") as f:
                    metadata_content = json.load(f)

                # Extract the original file information
                original_file = metadata_content["original_file"]
                original_file_name = original_file["name"]
                original_file_size = original_file["size"]

                # Extract the erasure coding parameters
                erasure_coding = metadata_content["erasure_coding"]
                file_id = erasure_coding["file_id"]
                k = erasure_coding["k"]
                m = erasure_coding["m"]
                chunk_size = erasure_coding["chunk_size"]
                encrypted = erasure_coding["encrypted"]

                # Extract the chunks information
                chunks_info = metadata_content["chunks"]

                # Update file_info with detailed metadata information
                file_info = [
                    f"Original file: [bold]{original_file_name}[/bold]",
                    f"Size: [bold]{original_file_size}[/bold] bytes ([bold cyan]{format_size(original_file_size)}[/bold cyan])",
                    f"File hash: {original_file['hash']}",
                    f"Metadata CID: [bold cyan]{metadata_cid}[/bold cyan]",
                    f"File ID: [bold yellow]{file_id}[/bold yellow]",
                    f"Erasure coding: k=[bold]{k}[/bold], m=[bold]{m}[/bold] (need {k} of {m} chunks to reconstruct)",
                    f"Chunk size: [bold]{format_size(chunk_size)}[/bold]",
                    f"Encrypted: [bold]{'Yes' if encrypted else 'No'}[/bold]",
                    f"Total chunks from metadata: [bold]{len(chunks_info)}[/bold]",
                ]

                # Match chunks from the blockchain with the metadata by CID
                matching_chunks = []
                chunk_cids_in_metadata = []

                # Create a mapping of CIDs from metadata
                for chunk in chunks_info:
                    # Extract CID (handle both string and dict formats)
                    chunk_cid = chunk["cid"]
                    if isinstance(chunk_cid, dict) and "cid" in chunk_cid:
                        chunk_cid = chunk_cid["cid"]
                    chunk_cids_in_metadata.append(chunk_cid)

                # Find matching chunks
                for chunk_file in chunk_files:
                    if (
                        chunk_file["cid"] in chunk_cids_in_metadata
                        or file_id in chunk_file["file_name"]
                    ):
                        matching_chunks.append(chunk_file)

                # Add information about matched chunks
                if matching_chunks:
                    file_info.append(
                        f"Found [bold]{len(matching_chunks)}/{len(chunks_info)}[/bold] chunks in blockchain"
                    )

                    # Calculate if we have enough chunks for reconstruction
                    chunks_needed = k
                    if len(matching_chunks) >= chunks_needed:
                        file_info.append(
                            "[bold green]✓ Enough chunks available for reconstruction[/bold green]"
                        )
                    else:
                        file_info.append(
                            "[bold red]✗ Not enough chunks available for reconstruction[/bold red]"
                        )
                else:
                    file_info.append(
                        "[bold yellow]No associated chunks found in blockchain[/bold yellow]"
                    )

                # Display the panel with all file information
                print_panel(
                    "\n".join(file_info), title=f"File #{i}: {original_file_name}"
                )

                # Display the chunks in a table if we found any
                if matching_chunks:
                    # Limit the number of chunks displayed
                    MAX_DISPLAYED_CHUNKS = 25
                    chunk_table = []

                    # Sort chunks by original_chunk_idx and share_idx for better display
                    def chunk_sort_key(chunk):
                        chunk_name = chunk["file_name"]
                        chunk_parts = []
                        if "_chunk_" in chunk_name:
                            chunk_parts = (
                                chunk_name.split("_chunk_")[1].split(".")[0].split("_")
                            )

                        original_chunk_idx = (
                            int(chunk_parts[0])
                            if len(chunk_parts) > 0 and chunk_parts[0].isdigit()
                            else 0
                        )
                        share_idx = (
                            int(chunk_parts[1])
                            if len(chunk_parts) > 1 and chunk_parts[1].isdigit()
                            else 0
                        )
                        return (original_chunk_idx, share_idx)

                    sorted_chunks = sorted(matching_chunks, key=chunk_sort_key)
                    displayed_chunks = sorted_chunks[:MAX_DISPLAYED_CHUNKS]

                    for j, chunk in enumerate(displayed_chunks, 1):
                        # Extract information from the chunk filename
                        chunk_name = chunk["file_name"]
                        chunk_parts = []

                        # Try to extract original_chunk_idx and share_idx
                        if "_chunk_" in chunk_name:
                            chunk_parts = (
                                chunk_name.split("_chunk_")[1].split(".")[0].split("_")
                            )

                        original_chunk_idx = (
                            chunk_parts[0] if len(chunk_parts) > 0 else "?"
                        )
                        share_idx = chunk_parts[1] if len(chunk_parts) > 1 else "?"

                        chunk_table.append(
                            {
                                "Index": str(j),
                                "Name": chunk_name,
                                "Original": original_chunk_idx,
                                "Share": share_idx,
                                "Size": chunk["size_formatted"],
                                "CID": chunk["cid"][:10] + "..." + chunk["cid"][-6:]
                                if len(chunk["cid"]) > 20
                                else chunk["cid"],
                            }
                        )

                    # Print chunk table without title, using dim (grey) styling
                    print_table(
                        "",  # Empty title
                        chunk_table,
                        ["Index", "Name", "Original", "Share", "Size", "CID"],
                        style="dim",  # Use dim (grey) styling for chunk tables
                    )

                    # If there are more chunks than the display limit, show a compact note in dim text
                    if len(matching_chunks) > MAX_DISPLAYED_CHUNKS:
                        log(
                            f"[dim](Showing {MAX_DISPLAYED_CHUNKS} of {len(matching_chunks)} chunks. Use 'hippius ipfs download {metadata_cid}' to view all.)[/dim]"
                        )

        else:
            # If show_chunks is disabled, just display the basic file information
            print_panel("\n".join(file_info), title=f"File #{i}: {metadata_file_name}")

    # Show a generic reconstruction command at the end
    if metadata_cids:
        # Include a real example with the first metadata CID
        example_cid = metadata_cids[0] if metadata_cids else "<METADATA_CID>"
        print_panel(
            f"[bold green underline]hippius reconstruct <METADATA_CID> <OUTPUT_FILENAME>[/bold green underline]\n\nExample:\n[bold green underline]hippius reconstruct {example_cid} reconstructed_file.bin[/bold green underline]",
            title="Reconstruction Command",
        )

    return 0


async def handle_erasure_code(
    client: HippiusClient,
    file_path: str,
    k: int,
    m: int,
    chunk_size: int,
    miner_ids: Optional[List[str]] = None,
    encrypt: Optional[bool] = None,
    publish: bool = True,
    verbose: bool = False,
) -> int:
    """Handle the erasure-code command"""
    if not os.path.exists(file_path):
        error(f"File [bold]{file_path}[/bold] does not exist")
        return 1

    if not os.path.isfile(file_path):
        error(f"[bold]{file_path}[/bold] is not a file")
        return 1

    # Check if zfec is installed
    try:
        import zfec
    except ImportError:
        error("zfec is required for erasure coding")
        log(
            "Install it with: [bold green underline]pip install zfec[/bold green underline]"
        )
        log(
            "Then update your environment: [bold green underline]poetry add zfec[/bold green underline]"
        )
        return 1

    # If publishing is enabled, ensure we have a valid substrate client by accessing it
    # This will trigger password prompts if needed right at the beginning
    if publish and False and None:
        try:
            # Force keypair initialization - this will prompt for password if needed
            _ = None
        except Exception as e:
            warning(f"Failed to initialize blockchain client: {str(e)}")
            warning(
                "Will continue with erasure coding but blockchain publishing may fail"
            )

    # Get file size
    file_size = os.path.getsize(file_path)
    file_name = os.path.basename(file_path)

    # Convert chunk size from MB to bytes if needed
    if chunk_size < 1024:  # Assume it's in MB if small
        chunk_size = chunk_size * 1024 * 1024

    # Calculate potential chunks
    potential_chunks = file_size / chunk_size
    if potential_chunks < k:
        warning("File is too small for the requested parameters.")

        # Calculate new chunk size to get exactly k chunks
        new_chunk_size = file_size / k

        new_chunk_size = int(new_chunk_size)
        new_chunk_size = max(1, new_chunk_size)

        # Create a panel with parameter adjustment information
        adjustment_info = [
            f"Original parameters: k=[bold]{k}[/bold], m=[bold]{m}[/bold], chunk size=[bold]{chunk_size / 1024 / 1024:.2f} MB[/bold]",
            f"Would create only [bold red]{potential_chunks:.2f}[/bold red] chunks, which is less than k=[bold]{k}[/bold]",
            f"Automatically adjusting chunk size to [bold green]{new_chunk_size / 1024 / 1024:.6f} MB[/bold green] to create at least {k} chunks",
        ]
        print_panel("\n".join(adjustment_info), title="Parameter Adjustment")

        chunk_size = new_chunk_size

    # Calculate total number of chunks that will be created
    total_original_chunks = max(1, int(math.ceil(file_size / chunk_size)))
    total_encoded_chunks = total_original_chunks * m
    estimated_size_per_chunk = min(chunk_size, file_size / total_original_chunks)

    # Create parameter information panel
    param_info = [
        f"File: [bold]{file_name}[/bold] ([bold cyan]{file_size / 1024 / 1024:.2f} MB[/bold cyan])",
        f"Parameters: k=[bold]{k}[/bold], m=[bold]{m}[/bold] (need {k} of {m} chunks to reconstruct)",
        f"Chunk size: [bold cyan]{chunk_size / 1024 / 1024:.6f} MB[/bold cyan]",
        f"Total chunks to be created: [bold yellow]{total_encoded_chunks}[/bold yellow] ({total_original_chunks} original chunks × {m} encoded chunks each)",
        f"Estimated storage required: [bold magenta]{(total_encoded_chunks * estimated_size_per_chunk) / (1024 * 1024):.2f} MB[/bold magenta]",
    ]

    # Add encryption status
    if encrypt:
        param_info.append("[bold green]Encryption: Enabled[/bold green]")
    else:
        param_info.append("[bold yellow]Encryption: Disabled[/bold yellow]")

    # Add publish status
    if publish:
        param_info.append(
            "[bold blue]Publishing: Enabled[/bold blue] (will store on blockchain)"
        )
    else:
        param_info.append(
            "[bold cyan]Publishing: Disabled[/bold cyan] (local only, no password needed)"
        )

    # Parse miner IDs if provided
    miner_id_list = None
    if miner_ids:
        miner_id_list = [m.strip() for m in miner_ids if m.strip()]
        param_info.append(
            f"Targeting [bold]{len(miner_id_list)}[/bold] miners for storage"
        )

    # Display parameter information panel
    print_panel("\n".join(param_info), title="Erasure Coding Operation")

    start_time = time.time()

    # Create progress for the erasure coding operation
    with create_progress() as progress:
        # Add tasks for the different stages
        processing_task = progress.add_task(
            "[cyan]Processing file...", total=100, visible=False
        )
        encoding_task = progress.add_task(
            "[green]Encoding chunks...", total=100, visible=False
        )
        upload_task = progress.add_task(
            "[blue]Uploading chunks...", total=100, visible=False
        )

        # Progress callback function to update the appropriate task
        def update_progress_bar(stage, current, total):
            pct = min(100, int(current / total * 100))

            if stage == "processing":
                progress.update(processing_task, completed=pct)
                if pct >= 100 and not progress.tasks[encoding_task].visible:
                    progress.update(encoding_task, visible=True)

            elif stage == "encoding":
                progress.update(encoding_task, completed=pct)
                if pct >= 100 and not progress.tasks[upload_task].visible:
                    progress.update(upload_task, visible=True)

            elif stage == "upload":
                progress.update(upload_task, completed=pct)

        # As a fallback, create a task to update the general progress if no callbacks are received
        async def update_general_progress():
            while not progress.finished:
                elapsed = time.time() - start_time
                # If we haven't shown the encoding task yet, update the processing task
                if not progress.tasks[encoding_task].visible:
                    pct = min(95, 100 * (1 - 1 / (1 + elapsed / 5)))
                    progress.update(processing_task, completed=pct)
                    if pct > 90:
                        progress.update(encoding_task, visible=True)
                # If we haven't shown the upload task yet, update the encoding task
                elif not progress.tasks[upload_task].visible and elapsed > 3:
                    pct = min(95, 100 * (1 - 1 / (1 + (elapsed - 3) / 5)))
                    progress.update(encoding_task, completed=pct)
                    if pct > 90:
                        progress.update(upload_task, visible=True)

                await asyncio.sleep(0.1)

        # Start the fallback progress updater task
        updater = asyncio.create_task(update_general_progress())

        try:
            # Use the store_erasure_coded_file method directly from HippiusClient
            result = await client.store_erasure_coded_file(
                file_path=file_path,
                k=k,
                m=m,
                chunk_size=chunk_size,
                encrypt=encrypt,
                miner_ids=miner_id_list,
                max_retries=3,
                verbose=verbose,
                progress_callback=update_progress_bar,
                publish=publish,
            )

            # Complete all progress tasks
            progress.update(processing_task, completed=100)
            progress.update(encoding_task, completed=100, visible=True)
            progress.update(upload_task, completed=100, visible=True)
            # Cancel the updater task
            updater.cancel()

            # Store the original result before potentially overwriting it with publish result
            storage_result = result.copy()
            metadata_cid = storage_result.get("metadata_cid", "unknown")

            # If publish flag is set, publish to the global IPFS network
            if publish:
                if metadata_cid != "unknown":
                    info("Publishing to global IPFS network...")
                try:
                    # Publish the metadata to the global IPFS network
                    publish_result = await client.ipfs_client.publish_global(
                        metadata_cid
                    )
                    if publish_result.get("published", False):
                        success("Successfully published to global IPFS network")
                        log(
                            f"Access URL: [link]{client.ipfs_client.api_url}/ipfs/{metadata_cid}[/link]"
                        )
                    else:
                        warning(
                            f"{publish_result.get('message', 'Failed to publish to global network')}"
                        )
                except Exception as e:
                    warning(f"Failed to publish to global IPFS network: {str(e)}")

            elapsed_time = time.time() - start_time

            # Display metadata
            metadata = storage_result.get("metadata", {})
            total_files_stored = storage_result.get("total_files_stored", 0)

            original_file = metadata.get("original_file", {})
            erasure_coding = metadata.get("erasure_coding", {})

            # Create a summary panel with the erasure coding results
            summary_lines = [
                f"Completed in [bold green]{elapsed_time:.2f}[/bold green] seconds!"
            ]

            # If metadata_cid is known but metadata is empty, try to get file info from result directly
            if metadata_cid != "unknown" and not original_file:
                file_name = os.path.basename(file_path)
                file_size = (
                    os.path.getsize(file_path) if os.path.exists(file_path) else 0
                )

                # Use direct values from input parameters when metadata is not available
                summary_lines.extend(
                    [
                        f"Original file: [bold]{file_name}[/bold] ([bold cyan]{file_size / 1024 / 1024:.2f} MB[/bold cyan])",
                        f"Parameters: k=[bold]{k}[/bold], m=[bold]{m}[/bold]",
                        f"Total files stored in marketplace: [bold]{total_files_stored}[/bold]",
                        f"Metadata CID: [bold cyan]{metadata_cid}[/bold cyan]",
                    ]
                )

                # Add publish status if applicable
                if publish:
                    summary_lines.extend(
                        [
                            "Published to global IPFS: [bold green]Yes[/bold green]",
                            f"Global access URL: [link]{client.ipfs_client.api_url}/ipfs/{metadata_cid}[/link]",
                        ]
                    )
            else:
                summary_lines.extend(
                    [
                        f"Original file: [bold]{original_file.get('name')}[/bold] ([bold cyan]{original_file.get('size', 0) / 1024 / 1024:.2f} MB[/bold cyan])",
                        f"File ID: [bold]{erasure_coding.get('file_id')}[/bold]",
                        f"Parameters: k=[bold]{erasure_coding.get('k')}[/bold], m=[bold]{erasure_coding.get('m')}[/bold]",
                        f"Total chunks: [bold]{len(metadata.get('chunks', []))}[/bold]",
                        f"Total files stored in marketplace: [bold]{total_files_stored}[/bold]",
                        f"Metadata CID: [bold cyan]{metadata_cid}[/bold cyan]",
                    ]
                )

                # Add publish status if applicable
                if publish:
                    summary_lines.extend(
                        [
                            "Published to global IPFS: [bold green]Yes[/bold green]",
                            f"Global access URL: [link]{client.ipfs_client.api_url}/ipfs/{metadata_cid}[/link]",
                        ]
                    )

            # If we stored in the marketplace
            if "transaction_hash" in result:
                summary_lines.append(
                    f"Transaction hash: [bold]{result['transaction_hash']}[/bold]"
                )

            # Display the summary panel
            print_panel("\n".join(summary_lines), title="Erasure Coding Summary")

            # Get file name, either from metadata or directly from file path
            output_filename = original_file.get("name")
            if not output_filename:
                output_filename = os.path.basename(file_path)

            # Create reconstruction instructions panel
            reconstruction_lines = [
                "You will need:",
                f"  1. The metadata CID: [bold cyan]{metadata_cid}[/bold cyan]",
                f"  2. Access to at least [bold]{k}[/bold] chunks for each original chunk",
                "",
                "Reconstruction command:",
                f"[bold green underline]hippius reconstruct {metadata_cid} reconstructed_{output_filename}[/bold green underline]",
            ]

            print_panel(
                "\n".join(reconstruction_lines), title="Reconstruction Instructions"
            )

            return 0

        except Exception as e:
            # Cancel the updater task in case of error
            updater.cancel()
            error(f"Erasure coding failed: {str(e)}")

            # Provide helpful advice based on the error
            if "Wrong length" in str(e) and "input blocks" in str(e):
                # Create an advice panel for small file errors
                advice_lines = [
                    "This error typically occurs with very small files.",
                    "",
                    "Suggestions:",
                    "  1. Try using a smaller chunk size: [bold]--chunk-size 4096[/bold]",
                    "  2. Try using a smaller k value: [bold]--k 2[/bold]",
                    "  3. For very small files, consider using regular storage instead of erasure coding.",
                ]
                print_panel("\n".join(advice_lines), title="Troubleshooting")


async def handle_reconstruct(
    client: HippiusClient, metadata_cid: str, output_file: str, verbose: bool = False
) -> int:
    """Handle the reconstruct command"""
    # Create initial parameters panel
    param_info = [
        f"Metadata CID: [bold cyan]{metadata_cid}[/bold cyan]",
        f"Output file: [bold]{output_file}[/bold]",
    ]

    if verbose:
        param_info.append(
            "[bold yellow]Verbose mode enabled[/bold yellow] - will show detailed progress"
        )

    print_panel("\n".join(param_info), title="Reconstruction Operation")

    start_time = time.time()

    # Create progress for the reconstruction operation
    with create_progress() as progress:
        # Add a task for the reconstruction process
        download_task = progress.add_task("[cyan]Downloading metadata...", total=100)
        reconstruct_task = progress.add_task(
            "[green]Reconstructing file...", total=100, visible=False
        )

        # Create a task to update the progress while waiting for the operation
        async def update_progress():
            # Phase 1: Downloading metadata and chunks (first 40% of process)
            # Assume an approximate timing: phase1 = 5 seconds, phase2 = 10 seconds
            phase1_duration = 5
            total_duration = 15  # Estimation for both phases

            while not progress.finished:
                elapsed = time.time() - start_time

                # Phase 1: Downloading metadata and chunks (0-40%)
                if elapsed <= phase1_duration:
                    # Calculate progress for phase 1 (0-100%)
                    download_pct = min(100, elapsed / phase1_duration * 100)
                    progress.update(download_task, completed=download_pct)

                    # Make reconstruction task visible when metadata download starts progressing
                    if (
                        download_pct > 30
                        and not progress.tasks[reconstruct_task].visible
                    ):
                        progress.update(reconstruct_task, completed=0, visible=True)
                else:
                    # Ensure download task shows complete
                    progress.update(download_task, completed=100)

                    # Phase 2: Reconstructing (0-100%)
                    remaining_time = total_duration - phase1_duration
                    phase2_elapsed = elapsed - phase1_duration
                    if phase2_elapsed >= 0:
                        # Calculate progress for phase 2 (0-100%)
                        reconstruct_pct = min(95, phase2_elapsed / remaining_time * 100)
                        progress.update(
                            reconstruct_task, completed=reconstruct_pct, visible=True
                        )

                await asyncio.sleep(0.1)

        # Start the progress updater task
        updater = asyncio.create_task(update_progress())

        try:
            # Use the reconstruct_erasure_coded_file method
            result = await client.reconstruct_from_erasure_code(
                metadata_cid=metadata_cid, output_file=output_file, verbose=verbose
            )

            # Complete all progress tasks
            progress.update(download_task, completed=100)
            progress.update(reconstruct_task, completed=100)
            # Cancel the updater task
            updater.cancel()

            elapsed_time = time.time() - start_time

            # Display reconstruction results
            output_path = result.get("output_path", output_file)
            file_size = result.get("size_bytes", 0)
            size_formatted = format_size(file_size)

            # Create success panel
            success_info = [
                f"Reconstruction completed in [bold green]{elapsed_time:.2f}[/bold green] seconds!",
                f"Saved to: [bold]{output_path}[/bold]",
                f"Size: [bold cyan]{file_size:,}[/bold cyan] bytes ([bold cyan]{size_formatted}[/bold cyan])",
            ]

            if result.get("decrypted"):
                success_info.append(
                    "[bold yellow]File was decrypted during reconstruction[/bold yellow]"
                )

            print_panel("\n".join(success_info), title="Reconstruction Successful")

            return 0

        except Exception as e:
            # Cancel the updater task in case of error
            updater.cancel()
            error(f"Reconstruction failed: {str(e)}")

            if "No metadata found for CID" in str(e):
                advice_lines = [
                    "The metadata CID could not be found. Please check:",
                    "  1. The CID is correct",
                    "  2. The IPFS gateway is accessible",
                    "  3. If the file was published to the global IPFS network",
                ]
                print_panel("\n".join(advice_lines), title="Troubleshooting")

            elif "Failed to download chunk" in str(e):
                advice_lines = [
                    "Failed to download enough chunks for reconstruction. Please check:",
                    "  1. Your connection to the IPFS network",
                    "  2. If enough chunks are available (need at least k chunks)",
                    "  3. If the chunks are still pinned in the network",
                ]
                print_panel("\n".join(advice_lines), title="Troubleshooting")

            return 1


async def handle_delete(client: HippiusClient, cid: str, force: bool = False) -> int:
    """Handle the delete command for files or directories"""
    info(f"Preparing to delete content with CID: [bold cyan]{cid}[/bold cyan]")

    # First check if this is a directory
    try:
        exists_result = await client.exists(cid)
        if not exists_result["exists"]:
            error(f"CID [bold cyan]{cid}[/bold cyan] not found on IPFS")
            return 1
    except Exception as e:
        warning(f"Error checking if CID exists: {e}")

    if not force:
        warning("This will cancel storage and remove the content from the marketplace.")
        confirm = input("Continue? (y/n): ").strip().lower()
        if confirm != "y":
            log("Deletion cancelled", style="yellow")
            return 0

    # Show spinner during deletion
    with console.status("[cyan]Deleting content...[/cyan]", spinner="dots") as status:
        result = await client.delete_file(cid)

    # Display results
    is_directory = result.get("is_directory", False)
    child_files = result.get("child_files", [])

    if is_directory:
        # Directory deletion
        details = [
            f"Successfully deleted directory: [bold cyan]{cid}[/bold cyan]",
            f"Child files unpinned: [bold]{len(child_files)}[/bold]",
        ]

        # If there are child files, show them in a table
        if child_files:
            table_data = []
            for i, file in enumerate(
                child_files[:10], 1
            ):  # Limit to first 10 files if many
                table_data.append(
                    {
                        "Index": str(i),
                        "Filename": file.get("name", "unknown"),
                        "CID": file.get("cid", "unknown"),
                    }
                )

            if len(child_files) > 10:
                table_data.append(
                    {
                        "Index": "...",
                        "Filename": f"({len(child_files) - 10} more files)",
                        "CID": "...",
                    }
                )

            print_table(
                "Unpinned Child Files", table_data, ["Index", "Filename", "CID"]
            )
    else:
        # Regular file deletion
        details = [f"Successfully deleted file: [bold cyan]{cid}[/bold cyan]"]

    if "duration_seconds" in result.get("timing", {}):
        details.append(
            f"Deletion completed in [bold green]{result['timing']['duration_seconds']:.2f}[/bold green] seconds"
        )

    print_panel("\n".join(details), title="Deletion Complete")

    # Create an informative panel with notes
    notes = [
        "1. The content is now unpinned from the marketplace",
        "2. The CID may still resolve temporarily until garbage collection occurs",
        "3. If the content was published to the global IPFS network, it may still be",
        "   available through other nodes that pinned it",
    ]

    print_panel("\n".join(notes), title="Important Notes")

    return 0


async def handle_pin(
    client: HippiusClient, cid: str, publish: bool = True, miner_ids=None
) -> int:
    """Handle the pin command to pin a CID to IPFS and optionally publish to blockchain"""
    from rich.panel import Panel

    # First check if this CID exists
    try:
        exists_result = await client.exists(cid)
        if not exists_result["exists"]:
            error(f"CID [bold cyan]{cid}[/bold cyan] not found on IPFS")
            return 1
    except Exception as e:
        warning(f"Error checking if CID exists: {e}")
        return 1

    # Create operation title based on publish flag
    if publish:
        info(
            f"Preparing to pin and publish content with CID: [bold cyan]{cid}[/bold cyan]"
        )
        operation_title = "Pin & Publish Operation"
    else:
        info(f"Preparing to pin content with CID: [bold cyan]{cid}[/bold cyan]")
        operation_title = "Pin Operation"

    # Display operation details
    operation_details = [
        f"CID: [bold cyan]{cid}[/bold cyan]",
        f"Publishing to blockchain: {'Enabled' if publish else 'Disabled'}",
    ]
    print_panel("\n".join(operation_details), title=operation_title)

    # Need to authenticate if publishing to blockchain
    if publish:
        try:
            # Ensure we have a keypair for substrate operations
            _ = None
        except Exception as e:
            warning(f"Failed to initialize blockchain client: {str(e)}")
            warning("Will continue with pinning but blockchain publishing may fail")

    # Show spinner during pinning
    with console.status(
        "[cyan]Pinning content to IPFS...[/cyan]", spinner="dots"
    ) as status:
        try:
            # Pin the content to IPFS
            pin_result = await client.ipfs_client.pin(cid)

            if not pin_result.get("success", False):
                error(
                    f"Failed to pin content: {pin_result.get('message', 'Unknown error')}"
                )
                return 1

            # Blockchain publishing is deprecated
            if publish:
                warning(
                    "Blockchain publishing has been deprecated. Use the API client for storage management."
                )

            # Show success panel
            if True:
                # Just pinning, no blockchain publishing
                panel_details = [
                    f"Successfully pinned: [bold cyan]{cid}[/bold cyan]",
                    "\nThis content is now pinned to your IPFS node.",
                    "It will remain available as long as your node is running.",
                ]
                console.print(
                    Panel(
                        "\n".join(panel_details),
                        title="Pinning Complete",
                        border_style="green",
                    )
                )

            return 0
        except Exception as e:
            error(f"Error during operation: {e}")
            return 1


async def handle_ec_delete(
    client: HippiusClient, metadata_cid: str, force: bool = False
) -> int:
    """Handle the erasure-code delete command"""

    # Create a stylish header with the CID
    info("Preparing to delete erasure-coded file with metadata CID:")
    print_panel(f"[bold cyan]{metadata_cid}[/bold cyan]", title="Metadata CID")

    # Confirm the deletion if not forced
    if not force:
        warning_text = [
            "This will cancel the storage of this file on the Hippius blockchain.",
            "The file metadata will be removed from blockchain storage tracking.",
            "[dim]Note: Only the metadata CID will be canceled; contents may remain on IPFS.[/dim]",
        ]
        print_panel("\n".join(warning_text), title="Warning")

        confirm = input("Continue with deletion? (y/n): ").strip().lower()
        if confirm != "y":
            log("Deletion cancelled", style="yellow")
            return 0

    try:
        # First, pre-authenticate the client to get any password prompts out of the way
        # This accesses the substrate client to trigger authentication
        if not None:
            None

        # Now we can show the spinner after any password prompts
        info("Deleting erasure-coded file from marketplace...")

        # Create a more detailed spinner with phases
        with console.status(
            "[cyan]Processing file metadata and chunks...[/cyan]", spinner="dots"
        ) as status:
            try:
                # Use the specialized delete method that now throws specific exceptions
                await client.delete_ec_file(metadata_cid)

                # If we get here, deletion was successful
                deletion_success = True
                already_deleted = False

            except HippiusAlreadyDeletedError:
                # Special case - already deleted
                deletion_success = False
                already_deleted = True

            except HippiusFailedSubstrateDelete as e:
                # Blockchain deletion failed
                error(f"Blockchain storage cancellation failed: {e}")
                return 1

            except HippiusFailedIPFSUnpin:
                # IPFS unpinning failed, but blockchain deletion succeeded
                warning(
                    "Note: Some IPFS operations failed, but blockchain storage was successfully canceled"
                )
                # Consider this a success for the user since the more important blockchain part worked
                deletion_success = True
                already_deleted = False

            except HippiusMetadataError:
                # Metadata parsing failed, but we can still continue
                warning(
                    "Note: Metadata file was corrupted, but blockchain storage was successfully canceled"
                )
                # Consider this a success for the user since the blockchain part worked
                deletion_success = True
                already_deleted = False

            except Exception as e:
                # Handle any unexpected errors
                error(f"Unexpected error: {e}")
                return 1

        # Show the result
        if deletion_success:
            # Create a success panel
            success_panel = [
                "[bold green]✓[/bold green] Metadata CID canceled from blockchain storage",
                "[dim]This file is no longer tracked for storage payments[/dim]",
                "",
                "[dim]To purge file data completely:[/dim]",
                "• Individual chunks may still exist on IPFS and nodes",
                "• For complete deletion, all chunks should be unpinned manually",
            ]
            print_panel(
                "\n".join(success_panel), title="Storage Cancellation Successful"
            )
            return 0
        elif already_deleted:
            # Create a panel for the already deleted case
            already_panel = [
                "[bold yellow]![/bold yellow] This file has already been deleted from storage",
                "[dim]The CID was not found in the blockchain storage registry[/dim]",
                "",
                "This is expected if:",
                "• You previously deleted this file",
                "• The file was deleted by another process",
                "• The file was never stored in the first place",
            ]
            print_panel("\n".join(already_panel), title="Already Deleted")
            # Return 0 since this is not an error condition
            return 0
        else:
            # Create an error panel for all other failures
            error_panel = [
                "[bold red]×[/bold red] File not found in blockchain storage",
                "[dim]The metadata CID was not found in the blockchain storage registry[/dim]",
                "",
                "Possible reasons:",
                "• The CID may be incorrect",
                "• You may not be the owner of this file",
            ]
            print_panel("\n".join(error_panel), title="Storage Cancellation Failed")
            return 1
    except Exception as e:
        error(f"Error deleting erasure-coded file: {e}")
        return 1


#
# Configuration Handlers
#


def handle_config_get(section: str, key: str) -> int:
    """Handle the config get command"""
    try:
        value = get_config_value(section, key)
        log(
            f"[bold cyan]{section}[/bold cyan].[bold green]{key}[/bold green] = [bold]{value}[/bold]"
        )
        return 0
    except Exception as e:
        error(f"Error getting configuration value: {e}")
        return 1


def handle_config_set(section: str, key: str, value: str) -> int:
    """Handle the config set command"""
    try:
        # Convert string "true"/"false" to boolean if applicable
        if value.lower() == "true":
            value = True
        elif value.lower() == "false":
            value = False

        # Set the configuration value
        set_config_value(section, key, value)
        success(
            f"Set [bold cyan]{section}[/bold cyan].[bold green]{key}[/bold green] = [bold]{value}[/bold]"
        )
        return 0
    except Exception as e:
        error(f"Error setting configuration value: {e}")
        return 1


def handle_config_list() -> int:
    """Handle the config list command"""
    try:
        config = get_all_config()

        # Format the configuration as a multi-line string
        config_lines = ["Current configuration:"]

        for section, values in config.items():
            config_lines.append(f"\n[bold cyan]{section}[/bold cyan]")
            for key, value in values.items():
                config_lines.append(
                    f"  [bold green]{key}[/bold green] = [bold]{value}[/bold]"
                )

        # Print as a panel
        print_panel("\n".join(config_lines), title="Configuration")

        return 0
    except Exception as e:
        error(f"Error listing configuration: {e}")
        return 1


def handle_config_reset() -> int:
    """Handle the config reset command"""
    try:
        reset_config()
        success("Configuration reset to default values")
        return 0
    except Exception as e:
        error(f"Error resetting configuration: {e}")
        return 1


#
# Account Management Handlers
#


def handle_account_info(account_name: Optional[str] = None) -> int:
    """Handle the account info command - displays detailed information about an account"""
    try:
        # Load configuration
        config = load_config()

        # If account name not specified, use active account
        if not account_name:
            account_name = config.get("substrate", {}).get("active_account")
            if not account_name:
                error("No account specified and no active account")
                return 1

        # Check if account exists
        accounts = config.get("substrate", {}).get("accounts", {})
        if account_name not in accounts:
            error(f"Account '{account_name}' not found")
            return 1

        # Get account details
        account = accounts[account_name]
        is_encrypted = account.get("hippius_key_encoded", False)
        is_active = account_name == get_active_account()

        # Get API key and truncate for display
        hippius_key = account.get("hippius_key", "")
        if hippius_key:
            if len(hippius_key) > 6:
                api_key_display = f"{hippius_key[:3]}...{hippius_key[-3:]}"
            else:
                api_key_display = hippius_key
        else:
            api_key_display = "[dim]Not set[/dim]"

        # Account information panel with rich formatting
        account_info = [
            f"Account Name: [bold]{account_name}[/bold]",
            f"Active: [bold cyan]{'Yes' if is_active else 'No'}[/bold cyan]",
            f"API Key: [bold]{api_key_display}[/bold]",
            f"Encryption: [bold {'green' if is_encrypted else 'yellow'}]{'Encrypted' if is_encrypted else 'Unencrypted'}[/bold {'green' if is_encrypted else 'yellow'}]",
        ]

        # Add suggestions based on account status
        account_info.append("")
        if is_active:
            account_info.append("[bold green]This is your active account[/bold green]")
        else:
            account_info.append(
                f"[dim]To use this account: [bold green underline]hippius account switch {account_name}[/bold green underline][/dim]"
            )

        if not is_encrypted and hippius_key:
            account_info.append(
                "[bold yellow underline]WARNING:[/bold yellow underline] API key is not encrypted"
            )
            account_info.append(
                "[dim]Consider encrypting your key for better security[/dim]"
            )

        # Print the panel with rich formatting
        print_panel(
            "\n".join(account_info), title=f"Account Information: {account_name}"
        )

        return 0

    except Exception as e:
        error(f"Error getting account info: {e}")
        return 1


def handle_account_create(
    client: HippiusClient, name: str, encrypt: bool = False
) -> int:
    """DEPRECATED: This command has been removed. Use 'hippius account login' instead."""
    error(
        "[bold red]DEPRECATED:[/bold red] The 'account create' command has been removed."
    )
    console.print(
        "\n[bold]Account creation with seed phrases is no longer supported.[/bold]"
    )
    console.print("\nTo use Hippius, please:")
    console.print(
        "  1. Create an account at [bold cyan]https://hippius.com[/bold cyan]"
    )
    console.print(
        "  2. Get your HIPPIUS_KEY from [bold cyan]https://hippius.com/account/api-keys[/bold cyan]"
    )
    console.print(
        "  3. Run: [bold green underline]hippius account login[/bold green underline]\n"
    )
    return 1


def handle_account_export(
    client: HippiusClient, name: Optional[str] = None, file_path: Optional[str] = None
) -> int:
    """Handle the account export command"""
    try:
        # Determine account to export
        account_name = name or get_active_account()

        if not account_name:
            error("No account specified and no active account found")
            console.print("Use --name to specify an account to export")
            return 1

        info(f"Exporting account: [bold]{account_name}[/bold]")

        # Default file path if not provided
        if not file_path:
            file_path = f"{account_name}_hippius_account.json"

        # Export the account
        config = load_config()
        accounts = config.get("substrate", {}).get("accounts", {})

        if account_name not in accounts:
            error(f"Account '{account_name}' not found")
            return 1

        # Get the account data
        account_data = accounts[account_name]

        # Create export data (HIPPIUS_KEY only)
        export_data = {
            "name": account_name,
            "hippius_key": account_data.get("hippius_key", ""),
            "hippius_key_encoded": account_data.get("hippius_key_encoded", False),
        }

        # Only include encrypted data if needed
        if export_data["hippius_key_encoded"]:
            export_data["hippius_key_salt"] = account_data.get("hippius_key_salt")

        # Save to file
        with open(file_path, "w") as f:
            json.dump(export_data, f, indent=2)

        info(f"Account exported to: [bold cyan]{file_path}[/bold cyan]")

        # Security warning
        if not export_data.get("hippius_key_encoded"):
            console.print(
                "\n[bold yellow]WARNING:[/bold yellow] This export file contains an unencrypted HIPPIUS_KEY."
            )
            console.print("Keep this file secure and never share it with anyone.")

        return 0

    except Exception as e:
        error(f"Error exporting account: {e}")
        return 1


def handle_account_import(
    client: HippiusClient, file_path: str, encrypt: bool = False
) -> int:
    """Handle the account import command"""
    try:
        # Verify file exists
        if not os.path.exists(file_path):
            error(f"File {file_path} not found")
            return 1

        info(f"Importing account from: [bold]{file_path}[/bold]")

        # Read and parse the file
        try:
            with open(file_path, "r") as f:
                import_data = json.load(f)

            # Validate data
            if not isinstance(import_data, dict):
                error("Invalid account file format")
                return 1

            account_name = import_data.get("name")
            hippius_key = import_data.get("hippius_key")
            hippius_key_encoded = import_data.get("hippius_key_encoded", False)
            hippius_key_salt = import_data.get("hippius_key_salt")

            if not account_name:
                error("Missing account name in import file")
                return 1

            if not hippius_key:
                error("Missing HIPPIUS_KEY in import file")
                return 1

        except Exception as e:
            error(f"Error reading account file: {e}")
            return 1

        # Check if account already exists
        accounts = list_accounts()
        if account_name in accounts:
            console.print(
                f"[yellow]Warning: Account '{account_name}' already exists[/yellow]"
            )
            overwrite = input("Overwrite existing account? (y/n): ").strip().lower()
            if overwrite != "y":
                info("Import cancelled")
                return 0

        # Load config and add account
        config = load_config()
        if "substrate" not in config:
            config["substrate"] = {}
        if "accounts" not in config["substrate"]:
            config["substrate"]["accounts"] = {}

        # Import the account with HIPPIUS_KEY
        config["substrate"]["accounts"][account_name] = {
            "hippius_key": hippius_key,
            "hippius_key_encoded": hippius_key_encoded,
            "hippius_key_salt": hippius_key_salt,
        }

        # Set as active account
        config["substrate"]["active_account"] = account_name
        save_config(config)

        info(f"\nSuccessfully imported account: [bold]{account_name}[/bold]")
        info("This account is now active.")

        return 0

    except Exception as e:
        error(f"Error importing account: {e}")
        return 1


def handle_account_list() -> int:
    """Handle the account list command"""
    try:
        accounts = list_accounts()
        active_account = get_active_account()

        if not accounts:
            log("No accounts found", style="yellow")
            return 0

        info(f"Found [bold]{len(accounts)}[/bold] accounts:")

        # Load config to get more details
        config = load_config()
        account_config = config.get("substrate", {}).get("accounts", {})

        # Create data for a table
        account_data_list = []
        for i, account_name in enumerate(accounts, 1):
            account_data = account_config.get(account_name, {})

            is_active = account_name == active_account
            is_encrypted = account_data.get("hippius_key_encoded", False)

            # Get API key and truncate for display
            hippius_key = account_data.get("hippius_key", "")
            if hippius_key:
                if len(hippius_key) > 6:
                    api_key_display = f"{hippius_key[:3]}...{hippius_key[-3:]}"
                else:
                    api_key_display = hippius_key
            else:
                api_key_display = "[dim]Not set[/dim]"

            # Add to table data
            row = {
                "Index": str(i),
                "Name": account_name,
                "Status": "[bold green]Active[/bold green]" if is_active else "",
                "Encrypted": "[yellow]Yes[/yellow]" if is_encrypted else "No",
                "API Key": api_key_display,
            }
            account_data_list.append(row)

        # Display accounts in a table
        print_table(
            title="Accounts",
            data=account_data_list,
            columns=["Index", "Name", "Status", "Encrypted", "API Key"],
        )

        # Show active account status
        if active_account:
            success(f"Active account: [bold]{active_account}[/bold]")
        else:
            warning("No active account selected")

        # Instructions
        help_text = [
            "To switch accounts: [bold green underline]hippius account switch <account_name>[/bold green underline]",
            "To create a new account: [bold green underline]hippius account create --name <account_name>[/bold green underline]",
        ]
        print_panel("\n".join(help_text), title="Account Management")

        return 0

    except Exception as e:
        error(f"Error listing accounts: {e}")
        return 1


def handle_account_switch(account_name: str) -> int:
    """Handle the account switch command"""
    try:
        # Check if account exists
        accounts = list_accounts()
        if account_name not in accounts:
            print(f"Error: Account '{account_name}' not found")
            print("Available accounts:")
            for account in accounts:
                print(f"  {account}")
            return 1

        # Set as active account
        set_active_account(account_name)

        print(f"Switched to account: {account_name}")

        # Show account address if possible
        try:
            address = get_account_address(account_name)
            print(f"Address: {address}")
        except Exception as e:
            # Check if encrypted
            config = load_config()
            account_config = (
                config.get("substrate", {}).get("accounts", {}).get(account_name, {})
            )

            if account_config.get("encrypted", False):
                print("Address: Encrypted (password required to view)")
            else:
                print(f"Note: Unable to display address ({str(e)})")

        return 0

    except Exception as e:
        print(f"Error switching account: {e}")
        return 1


def handle_account_login() -> int:
    """Handle the account login command - prompts for account details and creates an account"""
    try:
        # Display the login banner
        from hippius_sdk.cli_assets import HERO_TITLE

        console.print(HERO_TITLE, style="bold cyan")
        console.print(
            "\n[bold blue]Welcome to Hippius![/bold blue] Let's set up your account.\n"
        )

        # Create a style for prompts
        prompt_style = "bold green"
        input_style = "bold cyan"

        # Prompt for account name with nice formatting
        console.print(
            "[bold]Step 1:[/bold] Choose a name for your account", style=prompt_style
        )
        console.print(
            "This name will be used to identify your account in the Hippius system.",
            style="dim",
        )
        console.print("Account name:", style=input_style, end=" ")
        name = input().strip()

        if not name:
            error("[bold red]Account name cannot be empty[/bold red]")
            return 1

        # Check if account already exists
        accounts = list_accounts()
        if name in accounts:
            warning(f"Account '[bold]{name}[/bold]' already exists")
            console.print(
                "Do you want to overwrite it? (y/n):", style=input_style, end=" "
            )
            confirm = input().strip().lower()
            if confirm != "y":
                info("Login cancelled")
                return 0

        # Prompt for HIPPIUS_KEY
        console.print(
            "\n[bold]Step 2:[/bold] Enter your HIPPIUS_KEY", style=prompt_style
        )
        console.print(
            "Your HIPPIUS_KEY is the master API key for your Hippius account.",
            style="dim",
        )
        console.print(
            "[dim]You can find this in your account settings at https://console.hippius.com/dashboard/settings[/dim]"
        )
        console.print("HIPPIUS_KEY:", style=input_style, end=" ")
        hippius_key = input().strip()

        if not hippius_key:
            error("[bold red]HIPPIUS_KEY cannot be empty[/bold red]")
            return 1

        # Prompt for encryption with security explanation
        console.print("\n[bold]Step 3:[/bold] Secure your account", style=prompt_style)
        console.print(
            "Encrypting your HIPPIUS_KEY adds an extra layer of security.", style="dim"
        )
        console.print(
            "[bold yellow]Strongly recommended[/bold yellow] to protect your account.",
            style="dim",
        )

        credential_name = "HIPPIUS_KEY"
        console.print(
            f"Encrypt {credential_name}? [bold green](Y/n)[/bold green]:",
            style=input_style,
            end=" ",
        )
        encrypt_input = input().strip().lower()
        encrypt = encrypt_input == "y" or encrypt_input == "" or encrypt_input == "yes"

        # Set up encryption if requested
        password = None
        if encrypt:
            console.print(
                "\n[bold]Step 4:[/bold] Set encryption password", style=prompt_style
            )
            console.print(
                "This password will be required whenever you use your account for operations.",
                style="dim",
            )

            password = getpass.getpass("Enter a password: ")
            confirm = getpass.getpass("Confirm password: ")

            if password != confirm:
                error("[bold red]Passwords do not match[/bold red]")
                return 1

            if not password:
                error("[bold red]Password cannot be empty for encryption[/bold red]")
                return 1

        # Create and store the account
        with console.status("[cyan]Setting up your account...[/cyan]", spinner="dots"):
            # First, directly modify the config to ensure account is created
            config = load_config()

            # Ensure accounts structure exists
            if "substrate" not in config:
                config["substrate"] = {}
            if "accounts" not in config["substrate"]:
                config["substrate"]["accounts"] = {}

            # Store HIPPIUS_KEY
            config["substrate"]["accounts"][name] = {
                "hippius_key": hippius_key,
                "hippius_key_encoded": False,
                "hippius_key_salt": None,
            }

            # Set as active account
            config["substrate"]["active_account"] = name

            # Save the config first
            save_config(config)

            # Now encrypt if requested
            if encrypt:
                from hippius_sdk import encrypt_hippius_key

                encrypt_hippius_key(hippius_key, password, name)

            time.sleep(0.5)  # Small delay for visual feedback

        # Success panel with account information
        account_info = [
            f"[bold]Account Name:[/bold] [bold magenta]{name}[/bold magenta]",
            "[bold]Authentication:[/bold] [bold green]HIPPIUS_KEY[/bold green]",
            "",
            "[bold green]✓ Login successful![/bold green]",
            "[bold green]✓ Account set as active[/bold green]",
        ]

        if encrypt:
            account_info.append("[bold green]✓ HIPPIUS_KEY encrypted[/bold green]")
            account_info.append("")
            account_info.append(
                "[dim]You'll need your password when using this account for operations.[/dim]"
            )
        else:
            account_info.append(
                "[bold yellow]⚠ HIPPIUS_KEY not encrypted[/bold yellow]"
            )
            account_info.append("")
            account_info.append(
                "[dim]For better security, consider encrypting your HIPPIUS_KEY:[/dim]"
            )
            account_info.append(
                f"[dim]  [bold green underline]hippius account encode --name {name}[/bold green underline][/dim]"
            )

        # Add next steps
        account_info.append("")
        account_info.append("[bold blue]Next steps:[/bold blue]")
        account_info.append(
            "• [bold green underline]hippius credits[/bold green underline] - Check your account balance"
        )
        account_info.append(
            "• [bold green underline]hippius files[/bold green underline] - View your stored files"
        )
        account_info.append(
            "• [bold green underline]hippius store <file>[/bold green underline] - Upload a file to IPFS"
        )

        print_panel(
            "\n".join(account_info), title="[bold green]Account Ready[/bold green]"
        )
        return 0

    except Exception as e:
        error(f"[bold red]Error logging in:[/bold red] {e}")
        return 1


def handle_account_delete(account_name: str) -> int:
    """Handle the account delete command"""
    try:
        # Check if account exists
        accounts = list_accounts()
        if account_name not in accounts:
            print(f"Error: Account '{account_name}' not found")
            return 1

        # Confirm deletion
        print(f"Warning: You are about to delete account '{account_name}'")
        print("This action cannot be undone unless you have exported the account.")
        confirm = input("Delete this account? (y/n): ").strip().lower()

        if confirm != "y":
            print("Deletion cancelled")
            return 0

        # Delete the account
        delete_account(account_name)

        print(f"Account '{account_name}' deleted successfully")

        # If this was the active account, notify user
        active_account = get_active_account()
        if active_account == account_name:
            print("This was the active account. No account is currently active.")

            # If there are other accounts, suggest one
            remaining_accounts = list_accounts()
            if remaining_accounts:
                print(
                    f"You can switch to another account with: hippius account switch {remaining_accounts[0]}"
                )

        return 0

    except Exception as e:
        print(f"Error deleting account: {e}")
        return 1


async def handle_account_balance(
    client: HippiusClient, account_address: Optional[str] = None
) -> int:
    """Handle the account balance command - now shows credit balance from API"""
    info("Checking account balance...")

    if account_address:
        warning(
            "Note: Blockchain balances are deprecated. Showing credit balance instead."
        )
        warning("The account_address parameter is ignored.")

    # Get credit balance from API
    balance_data = await client.api_client.get_account_balance()

    credits = balance_data.get("balance", 0)
    # Convert to float if it's a string
    if isinstance(credits, str):
        credits = float(credits)

    # Create a panel with balance information
    balance_info = [
        f"Credit balance: [bold green]{credits:.2f} USD[/bold green]",
    ]

    # Add account info if available in response
    if "account" in balance_data:
        balance_info.append(
            f"Account: [bold cyan]{balance_data['account']}[/bold cyan]"
        )

    print_panel("\n".join(balance_info), title="Account Balance")

    return 0


#
# Default Address Handlers
#


def handle_default_address_set(address: str) -> int:
    """Handle the address set-default command"""
    try:
        # Validate address format
        if not address.startswith("5"):
            warning("The address does not appear to be a valid Substrate address")
            log("Substrate addresses typically start with '5'", style="yellow")
            confirm = input("Continue anyway? (y/n): ").strip().lower()
            if confirm != "y":
                return 1

        # Update config
        config = load_config()

        if "substrate" not in config:
            config["substrate"] = {}

        config["substrate"]["default_address"] = address

        # Save config
        save_config(config)

        # Create success information
        details = [
            f"Default address set to: [bold cyan]{address}[/bold cyan]",
            "\nThis address will be used for read-only operations when no account is specified.",
        ]

        print_panel("\n".join(details), title="Default Address Updated")

        return 0

    except Exception as e:
        error(f"Error setting default address: {e}")
        return 1


def handle_default_address_get() -> int:
    """Handle the address get-default command"""
    try:
        address = get_default_address()

        if address:
            info(f"Default address: [bold cyan]{address}[/bold cyan]")
        else:
            warning("No default address set")
            log(
                "You can set one with: [bold]hippius address set-default <address>[/bold]"
            )

        return 0

    except Exception as e:
        error(f"Error getting default address: {e}")
        return 1


def handle_default_address_clear() -> int:
    """Handle the address clear-default command"""
    try:
        config = load_config()

        if "substrate" in config and "default_address" in config["substrate"]:
            del config["substrate"]["default_address"]
            save_config(config)
            success("Default address cleared")
        else:
            log("No default address was set", style="yellow")

        return 0

    except Exception as e:
        error(f"Error clearing default address: {e}")
        return 1


def handle_register_coldkey(
    client: HippiusClient,
    node_id: str,
    node_priv_hex: str,
    node_type: str,
    ipfs_config: str = None,
    ipfs_priv_b64: str = None,
    ipfs_peer_id: str = None,
    pay_in_credits: bool = False,
    expires_in: int = 10,
    block_width: str = "u32",
    domain: str = "HIPPIUS::REGISTER::v1",
    nonce_hex: str = None,
    dry_run: bool = False,
) -> int:
    """Handle miner register-coldkey command"""
    try:
        # Get current account info
        account = get_active_account()
        if not account:
            error(
                "No active account. Please set up an account first with 'hippius account create' or 'hippius seed set'"
            )
            return 1

        account_address = get_account_address(account)
        if not account_address:
            error(f"Could not get address for account '{account}'")
            return 1

        info(
            f"Registering node with coldkey using account: [bold cyan]{account}[/bold cyan] ({account_address})"
        )

        # Import and use incentives.py functionality
        from hippius_sdk.incentives import (
            load_ipfs_seed,
            load_main_seed,
            encode_account_id,
            verify_peer_id,
            blake2_256,
            manual_encode_challenge,
        )
        from nacl.signing import SigningKey
        import base58
        import secrets
        from binascii import hexlify

        # Initialize SubstrateInterface
        substrate = SubstrateInterface(url=get_config_value("substrate", "url"))

        # Get genesis hash and current block
        genesis_hash_hex = substrate.get_block_hash(0)
        genesis_hash = bytes.fromhex(genesis_hash_hex[2:])
        current_block_number = substrate.get_block_number(None)

        log(f"Current block number: {current_block_number}")

        # Process node_id
        if node_id.startswith("0x"):
            node_id_bytes = bytes.fromhex(node_id[2:])
        else:
            node_id_bytes = base58.b58decode(node_id)

        # Load IPFS and main seeds
        ipfs_seed, peerid_from_config = load_ipfs_seed(ipfs_config, ipfs_priv_b64)
        main_seed = load_main_seed(node_priv_hex)

        # Create signing keys
        main_sk = SigningKey(main_seed)
        main_pk = bytes(main_sk.verify_key)
        ipfs_sk = SigningKey(ipfs_seed)
        ipfs_pk = bytes(ipfs_sk.verify_key)

        # Handle IPFS peer ID
        if ipfs_peer_id:
            ipfs_peer_id_bytes = base58.b58decode(ipfs_peer_id)
        elif peerid_from_config:
            ipfs_peer_id_bytes = base58.b58decode(peerid_from_config)
        else:
            if node_type in ["StorageMiner", "Validator"]:
                error(
                    "IPFS PeerID is required for StorageMiner and Validator node types"
                )
                return 1
            ipfs_peer_id_bytes = b""

        # Verify keys match node IDs
        if not verify_peer_id(main_pk, node_id_bytes, "Ed25519"):
            error("Main public key does not match node ID")
            return 1

        if ipfs_peer_id_bytes and not verify_peer_id(
            ipfs_pk, ipfs_peer_id_bytes, "Ed25519"
        ):
            error("IPFS public key does not match peer ID")
            return 1

        # Create challenge data
        domain24 = b"HIPPIUS::REGISTER::v1" + b"\x00" * 3
        node_id_hash = blake2_256(node_id_bytes)
        ipfs_peer_id_hash = blake2_256(ipfs_peer_id_bytes)

        nonce = (
            bytes.fromhex(nonce_hex[2:])
            if nonce_hex and nonce_hex.startswith("0x")
            else bytes.fromhex(nonce_hex)
            if nonce_hex
            else secrets.token_bytes(32)
        )

        expires_at_block = current_block_number + expires_in
        account_bytes = encode_account_id(account_address)

        challenge_data = {
            "domain": domain24,
            "genesis_hash": genesis_hash,
            "account": account_bytes,
            "node_id_hash": node_id_hash,
            "ipfs_peer_id_hash": ipfs_peer_id_hash,
            "block_number": current_block_number,
            "nonce": nonce,
            "expires_at": expires_at_block,
        }

        challenge_bytes = manual_encode_challenge(challenge_data, block_width)

        # Sign challenge
        main_sig = main_sk.sign(challenge_bytes).signature
        ipfs_sig = ipfs_sk.sign(challenge_bytes).signature

        # Build call parameters
        call_params = {
            "node_type": node_type,
            "node_id": node_id,
            "node_id_hex": "0x" + hexlify(node_id_bytes).decode(),
            "pay_in_credits": pay_in_credits,
            "ipfs_node_id": ipfs_peer_id,
            "owner": account_address,
            "ipfs_peer_id": ipfs_peer_id,
            "ipfs_id_hex": "0x" + hexlify(ipfs_peer_id_bytes).decode(),
            "main_key_type": "Ed25519",
            "main_public_key": "0x" + main_pk.hex(),
            "main_sig": "0x" + main_sig.hex(),
            "ipfs_key_type": "Ed25519",
            "ipfs_public_key": "0x" + ipfs_pk.hex(),
            "ipfs_sig": "0x" + ipfs_sig.hex(),
            "challenge_bytes": "0x" + challenge_bytes.hex(),
        }

        if dry_run:
            log("Dry run mode - printing payload without submitting")
            payload = {
                "genesis_hash_hex": "0x" + genesis_hash.hex(),
                "current_block_number": current_block_number,
                "challenge_bytes_hex": "0x" + challenge_bytes.hex(),
                "call_module": "Registration",
                "call_function": "register_node_with_coldkey",
                "call_params": call_params,
            }
            console.print(json.dumps(payload, indent=2))
            return 0

        seed_phrase = get_seed_phrase()
        if not seed_phrase:
            error("No seed phrase available for signing transaction")
            return 1

        # Submit transaction
        log("Submitting registration transaction...")
        log("Using module: [bold cyan]Registration[/bold cyan]")
        call = substrate.compose_call(
            call_module="Registration",
            call_function="register_node_with_coldkey",
            call_params=call_params,
        )
        extrinsic = substrate.create_signed_extrinsic(call=call, keypair=kp)
        receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

        result = {
            "extrinsic_hash": receipt.extrinsic_hash,
            "is_success": receipt.is_success,
            "error_message": receipt.error_message,
            "triggered_events": [str(event) for event in receipt.triggered_events],
        }

        if receipt.is_success:
            success("Node registered successfully with coldkey!")
            success(f"Transaction hash: {receipt.extrinsic_hash}")
        else:
            error(f"Registration failed: {receipt.error_message}")

        log("Full result:")
        console.print(json.dumps(result, indent=2))

        return 0 if receipt.is_success else 1

    except Exception as e:
        error(f"Error registering node with coldkey: {e}")
        if hasattr(e, "__traceback__"):
            import traceback

            traceback.print_exc()
        return 1


def handle_register_hotkey(
    client: HippiusClient,
    coldkey: str,
    node_id: str,
    node_priv_hex: str,
    node_type: str,
    ipfs_config: str = None,
    ipfs_priv_b64: str = None,
    ipfs_peer_id: str = None,
    pay_in_credits: bool = False,
    expires_in: int = 10,
    block_width: str = "u32",
    domain: str = "HIPPIUS::REGISTER::v1",
    nonce_hex: str = None,
    dry_run: bool = False,
) -> int:
    """Handle miner register-hotkey command"""
    try:
        # Get current account info
        account = get_active_account()
        if not account:
            error(
                "No active account. Please set up an account first with 'hippius account create' or 'hippius seed set'"
            )
            return 1

        account_address = get_account_address(account)
        if not account_address:
            error(f"Could not get address for account '{account}'")
            return 1

        info(
            f"Registering node with hotkey using account: [bold cyan]{account}[/bold cyan] ({account_address})"
        )
        info(f"Coldkey: [bold yellow]{coldkey}[/bold yellow]")

        # Import and use incentives.py functionality
        from hippius_sdk.incentives import (
            load_ipfs_seed,
            load_main_seed,
            encode_account_id,
            verify_peer_id,
            blake2_256,
            manual_encode_challenge,
        )
        from nacl.signing import SigningKey
        import base58
        import secrets
        from binascii import hexlify

        # Initialize SubstrateInterface
        substrate = SubstrateInterface(url=get_config_value("substrate", "url"))

        # Get genesis hash and current block
        genesis_hash_hex = substrate.get_block_hash(0)
        genesis_hash = bytes.fromhex(genesis_hash_hex[2:])
        current_block_number = substrate.get_block_number(None)

        log(f"Current block number: {current_block_number}")

        # Process node_id
        if node_id.startswith("0x"):
            node_id_bytes = bytes.fromhex(node_id[2:])
        else:
            node_id_bytes = base58.b58decode(node_id)

        # Load IPFS and main seeds
        ipfs_seed, peerid_from_config = load_ipfs_seed(ipfs_config, ipfs_priv_b64)
        main_seed = load_main_seed(node_priv_hex)

        # Create signing keys
        main_sk = SigningKey(main_seed)
        main_pk = bytes(main_sk.verify_key)
        ipfs_sk = SigningKey(ipfs_seed)
        ipfs_pk = bytes(ipfs_sk.verify_key)

        # Handle IPFS peer ID
        if ipfs_peer_id:
            ipfs_peer_id_bytes = base58.b58decode(ipfs_peer_id)
        elif peerid_from_config:
            ipfs_peer_id_bytes = base58.b58decode(peerid_from_config)
        else:
            if node_type in ["StorageMiner", "Validator"]:
                error(
                    "IPFS PeerID is required for StorageMiner and Validator node types"
                )
                return 1
            ipfs_peer_id_bytes = b""

        # Verify keys match node IDs
        if not verify_peer_id(main_pk, node_id_bytes, "Ed25519"):
            error("Main public key does not match node ID")
            return 1

        if ipfs_peer_id_bytes and not verify_peer_id(
            ipfs_pk, ipfs_peer_id_bytes, "Ed25519"
        ):
            error("IPFS public key does not match peer ID")
            return 1

        # Create challenge data
        domain_bytes = domain.encode()
        domain24 = b"HIPPIUS::REGISTER::v1" + b"\x00" * 3
        node_id_hash = blake2_256(node_id_bytes)
        ipfs_peer_id_hash = blake2_256(ipfs_peer_id_bytes)

        nonce = (
            bytes.fromhex(nonce_hex[2:])
            if nonce_hex and nonce_hex.startswith("0x")
            else bytes.fromhex(nonce_hex)
            if nonce_hex
            else secrets.token_bytes(32)
        )

        expires_at_block = current_block_number + expires_in
        account_bytes = encode_account_id(account_address)

        challenge_data = {
            "domain": domain24,
            "genesis_hash": genesis_hash,
            "account": account_bytes,
            "node_id_hash": node_id_hash,
            "ipfs_peer_id_hash": ipfs_peer_id_hash,
            "block_number": current_block_number,
            "nonce": nonce,
            "expires_at": expires_at_block,
        }

        challenge_bytes = manual_encode_challenge(challenge_data, block_width)

        # Sign challenge
        main_sig = main_sk.sign(challenge_bytes).signature
        ipfs_sig = ipfs_sk.sign(challenge_bytes).signature

        # Build call parameters
        call_params = {
            "coldkey": coldkey,
            "node_type": node_type,
            "node_id": node_id,
            "node_id_hex": "0x" + hexlify(node_id_bytes).decode(),
            "pay_in_credits": pay_in_credits,
            "ipfs_node_id": ipfs_peer_id,
            "ipfs_peer_id": ipfs_peer_id,
            "ipfs_id_hex": "0x" + hexlify(ipfs_peer_id_bytes).decode(),
            "owner": account_address,
            "main_key_type": "Ed25519",
            "main_public_key": "0x" + main_pk.hex(),
            "main_sig": "0x" + main_sig.hex(),
            "ipfs_key_type": "Ed25519",
            "ipfs_public_key": "0x" + ipfs_pk.hex(),
            "ipfs_sig": "0x" + ipfs_sig.hex(),
            "challenge_bytes": "0x" + challenge_bytes.hex(),
        }

        if dry_run:
            log("Dry run mode - printing payload without submitting")
            payload = {
                "genesis_hash_hex": "0x" + genesis_hash.hex(),
                "current_block_number": current_block_number,
                "challenge_bytes_hex": "0x" + challenge_bytes.hex(),
                "call_module": "Registration",
                "call_function": "register_node_with_hotkey",
                "call_params": call_params,
            }
            console.print(json.dumps(payload, indent=2))
            return 0

        seed_phrase = get_seed_phrase()
        if not seed_phrase:
            error("No seed phrase available for signing transaction")
            return 1

        kp = Keypair.create_from_uri(seed_phrase)

        # Submit transaction
        log("Submitting registration transaction...")
        log("Using module: [bold cyan]Registration[/bold cyan]")
        call = substrate.compose_call(
            call_module="Registration",
            call_function="register_node_with_hotkey",
            call_params=call_params,
        )
        extrinsic = substrate.create_signed_extrinsic(call=call, keypair=kp)
        receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

        result = {
            "extrinsic_hash": receipt.extrinsic_hash,
            "is_success": receipt.is_success,
            "error_message": receipt.error_message,
            "triggered_events": [str(event) for event in receipt.triggered_events],
        }

        if receipt.is_success:
            success("Node registered successfully with hotkey!")
            success(f"Transaction hash: {receipt.extrinsic_hash}")
        else:
            error(f"Registration failed: {receipt.error_message}")

        log("Full result:")
        console.print(json.dumps(result, indent=2))

        return 0 if receipt.is_success else 1

    except Exception as e:
        error(f"Error registering node with hotkey: {e}")
        if hasattr(e, "__traceback__"):
            import traceback

            traceback.print_exc()
        return 1


def handle_verify_node(
    client: HippiusClient,
    node_id: str,
    node_priv_hex: str,
    ipfs_config: str = None,
    ipfs_priv_b64: str = None,
    ipfs_peer_id: str = None,
    expires_in: int = 10,
    block_width: str = "u32",
    domain: str = "HIPPIUS::REGISTER::v1",
    nonce_hex: str = None,
    dry_run: bool = False,
) -> int:
    """Handle miner verify-node command"""
    try:
        # Get current account info
        account = get_active_account()
        if not account:
            error(
                "No active account. Please set up an account first with 'hippius account create' or 'hippius seed set'"
            )
            return 1

        account_address = get_account_address(account)
        if not account_address:
            error(f"Could not get address for account '{account}'")
            return 1

        info(
            f"Verifying existing node using account: [bold cyan]{account}[/bold cyan] ({account_address})"
        )

        # Import and use incentives.py functionality
        from hippius_sdk.incentives import (
            load_ipfs_seed,
            load_main_seed,
            encode_account_id,
            verify_peer_id,
            blake2_256,
            manual_encode_challenge,
        )
        from nacl.signing import SigningKey
        import base58
        import secrets
        from binascii import hexlify

        # Initialize SubstrateInterface
        substrate = SubstrateInterface(url=get_config_value("substrate", "url"))

        # Get genesis hash and current block
        genesis_hash_hex = substrate.get_block_hash(0)
        genesis_hash = bytes.fromhex(genesis_hash_hex[2:])
        current_block_number = substrate.get_block_number(None)

        log(f"Current block number: {current_block_number}")

        # Process node_id
        if node_id.startswith("0x"):
            node_id_bytes = bytes.fromhex(node_id[2:])
        else:
            node_id_bytes = base58.b58decode(node_id)

        # Load IPFS and main seeds
        ipfs_seed, peerid_from_config = load_ipfs_seed(ipfs_config, ipfs_priv_b64)
        main_seed = load_main_seed(node_priv_hex)

        # Create signing keys
        main_sk = SigningKey(main_seed)
        main_pk = bytes(main_sk.verify_key)
        ipfs_sk = SigningKey(ipfs_seed)
        ipfs_pk = bytes(ipfs_sk.verify_key)

        # Handle IPFS peer ID
        if ipfs_peer_id:
            ipfs_peer_id_bytes = base58.b58decode(ipfs_peer_id)
        elif peerid_from_config:
            ipfs_peer_id_bytes = base58.b58decode(peerid_from_config)
        else:
            # For verification, IPFS peer ID might not be required for all node types
            ipfs_peer_id_bytes = b""

        # Verify keys match node IDs
        if not verify_peer_id(main_pk, node_id_bytes, "Ed25519"):
            error("Main public key does not match node ID")
            return 1

        if ipfs_peer_id_bytes and not verify_peer_id(
            ipfs_pk, ipfs_peer_id_bytes, "Ed25519"
        ):
            error("IPFS public key does not match peer ID")
            return 1

        # Create challenge data
        domain_bytes = domain.encode()
        domain24 = b"HIPPIUS::REGISTER::v1" + b"\x00" * 3
        node_id_hash = blake2_256(node_id_bytes)
        ipfs_peer_id_hash = blake2_256(ipfs_peer_id_bytes)

        nonce = (
            bytes.fromhex(nonce_hex[2:])
            if nonce_hex and nonce_hex.startswith("0x")
            else bytes.fromhex(nonce_hex)
            if nonce_hex
            else secrets.token_bytes(32)
        )

        expires_at_block = current_block_number + expires_in
        account_bytes = encode_account_id(account_address)

        challenge_data = {
            "domain": domain24,
            "genesis_hash": genesis_hash,
            "account": account_bytes,
            "node_id_hash": node_id_hash,
            "ipfs_peer_id_hash": ipfs_peer_id_hash,
            "block_number": current_block_number,
            "nonce": nonce,
            "expires_at": expires_at_block,
        }

        challenge_bytes = manual_encode_challenge(challenge_data, block_width)

        # Sign challenge
        main_sig = main_sk.sign(challenge_bytes).signature
        ipfs_sig = ipfs_sk.sign(challenge_bytes).signature

        # Build call parameters for verify_existing_node
        call_params = {
            "node_id": node_id,
            "node_id_hex": "0x" + hexlify(node_id_bytes).decode(),
            "ipfs_id_hex": "0x" + hexlify(ipfs_peer_id_bytes).decode(),
            "main_key_type": "Ed25519",
            "main_public_key": "0x" + main_pk.hex(),
            "main_sig": "0x" + main_sig.hex(),
            "ipfs_key_type": "Ed25519",
            "ipfs_public_key": "0x" + ipfs_pk.hex(),
            "ipfs_sig": "0x" + ipfs_sig.hex(),
            "challenge_bytes": "0x" + challenge_bytes.hex(),
        }

        if dry_run:
            log("Dry run mode - printing payload without submitting")
            payload = {
                "genesis_hash_hex": "0x" + genesis_hash.hex(),
                "current_block_number": current_block_number,
                "challenge_bytes_hex": "0x" + challenge_bytes.hex(),
                "call_module": "Registration",
                "call_function": "verify_existing_node",
                "call_params": call_params,
            }
            console.print(json.dumps(payload, indent=2))
            return 0

        seed_phrase = get_seed_phrase()
        if not seed_phrase:
            error("No seed phrase available for signing transaction")
            return 1

        # Submit transaction
        log("Submitting node verification transaction...")
        log("Using module: [bold cyan]Registration[/bold cyan]")
        call = SubstrateInterface.compose_call(
            call_module="Registration",
            call_function="verify_existing_node",
            call_params=call_params,
        )
        extrinsic = substrate.create_signed_extrinsic(call=call, keypair=kp)
        receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

        result = {
            "extrinsic_hash": receipt.extrinsic_hash,
            "is_success": receipt.is_success,
            "error_message": receipt.error_message,
            "triggered_events": [str(event) for event in receipt.triggered_events],
        }

        if receipt.is_success:
            success("Node verification successful!")
            success(f"Transaction hash: {receipt.extrinsic_hash}")
        else:
            error(f"Node verification failed: {receipt.error_message}")

        log("Full result:")
        console.print(json.dumps(result, indent=2))

        return 0 if receipt.is_success else 1

    except Exception as e:
        error(f"Error verifying node: {e}")
        if hasattr(e, "__traceback__"):
            import traceback

            traceback.print_exc()
        return 1


def handle_verify_coldkey_node(
    client: HippiusClient,
    node_id: str,
    node_priv_hex: str,
    ipfs_config: str = None,
    ipfs_priv_b64: str = None,
    ipfs_peer_id: str = None,
    expires_in: int = 10,
    block_width: str = "u32",
    domain: str = "HIPPIUS::REGISTER::v1",
    nonce_hex: str = None,
    dry_run: bool = False,
) -> int:
    """Handle miner verify-coldkey-node command"""
    try:
        # Get current account info
        account = get_active_account()
        if not account:
            error(
                "No active account. Please set up an account first with 'hippius account create' or 'hippius seed set'"
            )
            return 1

        account_address = get_account_address(account)
        if not account_address:
            error(f"Could not get address for account '{account}'")
            return 1

        info(
            f"Verifying existing coldkey node using account: [bold cyan]{account}[/bold cyan] ({account_address})"
        )

        # Import and use incentives.py functionality
        from hippius_sdk.incentives import (
            load_ipfs_seed,
            load_main_seed,
            encode_account_id,
            verify_peer_id,
            blake2_256,
            manual_encode_challenge,
        )
        from substrateinterface import SubstrateInterface
        from nacl.signing import SigningKey
        import base58
        import secrets
        from binascii import hexlify

        # Initialize SubstrateInterface
        substrate = SubstrateInterface(url=get_config_value("substrate", "url"))

        # Get genesis hash and current block
        genesis_hash_hex = substrate.get_block_hash(0)
        genesis_hash = bytes.fromhex(genesis_hash_hex[2:])
        current_block_number = substrate.get_block_number(None)

        log(f"Current block number: {current_block_number}")

        # Process node_id
        if node_id.startswith("0x"):
            node_id_bytes = bytes.fromhex(node_id[2:])
        else:
            node_id_bytes = base58.b58decode(node_id)

        # Load IPFS and main seeds
        ipfs_seed, peerid_from_config = load_ipfs_seed(ipfs_config, ipfs_priv_b64)
        main_seed = load_main_seed(node_priv_hex)

        # Create signing keys
        main_sk = SigningKey(main_seed)
        main_pk = bytes(main_sk.verify_key)
        ipfs_sk = SigningKey(ipfs_seed)
        ipfs_pk = bytes(ipfs_sk.verify_key)

        # Handle IPFS peer ID
        if ipfs_peer_id:
            ipfs_peer_id_bytes = base58.b58decode(ipfs_peer_id)
        elif peerid_from_config:
            ipfs_peer_id_bytes = base58.b58decode(peerid_from_config)
        else:
            # For verification, IPFS peer ID might not be required for all node types
            ipfs_peer_id_bytes = b""

        # Verify keys match node IDs
        if not verify_peer_id(main_pk, node_id_bytes, "Ed25519"):
            error("Main public key does not match node ID")
            return 1

        if ipfs_peer_id_bytes and not verify_peer_id(
            ipfs_pk, ipfs_peer_id_bytes, "Ed25519"
        ):
            error("IPFS public key does not match peer ID")
            return 1

        # Create challenge data
        domain_bytes = domain.encode()
        domain24 = b"HIPPIUS::REGISTER::v1" + b"\x00" * 3
        node_id_hash = blake2_256(node_id_bytes)
        ipfs_peer_id_hash = blake2_256(ipfs_peer_id_bytes)

        nonce = (
            bytes.fromhex(nonce_hex[2:])
            if nonce_hex and nonce_hex.startswith("0x")
            else bytes.fromhex(nonce_hex)
            if nonce_hex
            else secrets.token_bytes(32)
        )

        expires_at_block = current_block_number + expires_in
        account_bytes = encode_account_id(account_address)

        challenge_data = {
            "domain": domain24,
            "genesis_hash": genesis_hash,
            "account": account_bytes,
            "node_id_hash": node_id_hash,
            "ipfs_peer_id_hash": ipfs_peer_id_hash,
            "block_number": current_block_number,
            "nonce": nonce,
            "expires_at": expires_at_block,
        }

        challenge_bytes = manual_encode_challenge(challenge_data, block_width)

        # Sign challenge
        main_sig = main_sk.sign(challenge_bytes).signature
        ipfs_sig = ipfs_sk.sign(challenge_bytes).signature

        # Build call parameters for verify_existing_coldkey_node
        call_params = {
            "node_id": node_id,
            "node_id_hex": "0x" + hexlify(node_id_bytes).decode(),
            "ipfs_id_hex": "0x" + hexlify(ipfs_peer_id_bytes).decode(),
            "main_key_type": "Ed25519",
            "main_public_key": "0x" + main_pk.hex(),
            "main_sig": "0x" + main_sig.hex(),
            "ipfs_key_type": "Ed25519",
            "ipfs_public_key": "0x" + ipfs_pk.hex(),
            "ipfs_sig": "0x" + ipfs_sig.hex(),
            "challenge_bytes": "0x" + challenge_bytes.hex(),
        }

        if dry_run:
            log("Dry run mode - printing payload without submitting")
            payload = {
                "genesis_hash_hex": "0x" + genesis_hash.hex(),
                "current_block_number": current_block_number,
                "challenge_bytes_hex": "0x" + challenge_bytes.hex(),
                "call_module": "Registration",
                "call_function": "verify_existing_coldkey_node",
                "call_params": call_params,
            }
            console.print(json.dumps(payload, indent=2))
            return 0

        seed_phrase = get_seed_phrase()
        if not seed_phrase:
            error("No seed phrase available for signing transaction")
            return 1

        kp = Keypair.create_from_uri(seed_phrase)

        # Submit transaction
        log("Submitting coldkey node verification transaction...")
        log("Using module: [bold cyan]Registration[/bold cyan]")
        call = substrate.compose_call(
            call_module="Registration",
            call_function="verify_existing_coldkey_node",
            call_params=call_params,
        )
        extrinsic = substrate.create_signed_extrinsic(call=call, keypair=kp)
        receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

        result = {
            "extrinsic_hash": receipt.extrinsic_hash,
            "is_success": receipt.is_success,
            "error_message": receipt.error_message,
            "triggered_events": [str(event) for event in receipt.triggered_events],
        }

        if receipt.is_success:
            success("Coldkey node verification successful!")
            success(f"Transaction hash: {receipt.extrinsic_hash}")
        else:
            error(f"Coldkey node verification failed: {receipt.error_message}")

        log("Full result:")
        console.print(json.dumps(result, indent=2))

        return 0 if receipt.is_success else 1

    except Exception as e:
        error(f"Error verifying coldkey node: {e}")
        if hasattr(e, "__traceback__"):
            import traceback

            traceback.print_exc()
        return 1
