"""Local wallet management for BotVegas agents.

This module provides cross-platform Solana wallet functionality:
- Solana CLI detection and installation
- Keypair generation and secure storage
- Balance checking (SOL and SPL tokens)
- Transaction building and signing

Security:
- Keys stored in OS keychain (macOS Keychain, Windows Credential Locker, Linux Secret Service)
- File fallback with restrictive permissions (chmod 600)
- Keys never leave the local machine
"""

import json
import logging
import os
import platform
import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import Any

# Try to import keyring for secure credential storage
try:
    import keyring

    KEYRING_AVAILABLE = True
except ImportError:
    KEYRING_AVAILABLE = False

# Try to import solders for keypair operations (preferred)
try:
    from solders.keypair import Keypair
    from solders.pubkey import Pubkey

    SOLDERS_AVAILABLE = True
except ImportError:
    SOLDERS_AVAILABLE = False

# Try to import solana for RPC operations
try:
    from solana.rpc.api import Client as SolanaClient
    from solana.rpc.commitment import Confirmed

    SOLANA_RPC_AVAILABLE = True
except ImportError:
    SOLANA_RPC_AVAILABLE = False

logger = logging.getLogger(__name__)

# =============================================================================
# Configuration
# =============================================================================

# Network name to RPC URL mapping
NETWORK_RPC_MAP: dict[str, str] = {
    "mainnet": "https://api.mainnet-beta.solana.com",
    "mainnet-beta": "https://api.mainnet-beta.solana.com",
    "devnet": "https://api.devnet.solana.com",
    "testnet": "https://api.testnet.solana.com",
}


@dataclass
class WalletConfig:
    """Configuration for wallet operations."""

    # Service name for keyring storage
    SERVICE_NAME: str = "botvegas-agent"

    # Directory for file-based key storage (fallback)
    KEYS_DIR: Path = Path.home() / ".config" / "botvegas" / "keys"

    # $BOTVEGAS token mint address
    # TODO: Replace with actual mint from pump.fun
    BOTVEGAS_MINT: str = os.getenv(
        "BOTVEGAS_TOKEN_MINT",
        "BoTvEgAsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",  # Placeholder
    )

    # Network selection: mainnet, devnet, or testnet
    # Can be overridden by SOLANA_RPC_URL for custom RPC endpoints
    SOLANA_NETWORK: str = os.getenv("SOLANA_NETWORK", "mainnet")

    @property
    def RPC_URL(self) -> str:
        """Get RPC URL based on network or custom override."""
        # Custom RPC takes precedence
        custom_rpc = os.getenv("SOLANA_RPC_URL")
        if custom_rpc:
            return custom_rpc
        # Otherwise use network map
        return NETWORK_RPC_MAP.get(
            self.SOLANA_NETWORK.lower(),
            NETWORK_RPC_MAP["mainnet"],  # fallback
        )

    # Keep these for backward compatibility
    @property
    def RPC_MAINNET(self) -> str:
        return NETWORK_RPC_MAP["mainnet"]

    @property
    def RPC_DEVNET(self) -> str:
        return NETWORK_RPC_MAP["devnet"]

    # pump.fun URL format
    @property
    def PUMP_FUN_URL(self) -> str:
        return f"https://pump.fun/coin/{self.BOTVEGAS_MINT}"

    # Solscan URL formats (network-aware)
    @property
    def _solscan_cluster_suffix(self) -> str:
        """Get cluster query param for Solscan URLs."""
        network = self.SOLANA_NETWORK.lower()
        if network in ("devnet", "testnet"):
            return f"?cluster={network}"
        return ""

    @property
    def SOLSCAN_WALLET_URL(self) -> str:
        return "https://solscan.io/account/{address}" + self._solscan_cluster_suffix

    @property
    def SOLSCAN_TOKEN_URL(self) -> str:
        return "https://solscan.io/token/{mint}" + self._solscan_cluster_suffix

    @property
    def SOLSCAN_TX_URL(self) -> str:
        return "https://solscan.io/tx/{signature}" + self._solscan_cluster_suffix

    # Minimum balances for warnings
    MIN_SOL_FOR_FEES: float = 0.01  # ~$2 worth
    MIN_BOTVEGAS_TO_PLAY: float = 10.0  # Minimum stake
    RECOMMENDED_SOL: float = 0.05  # ~$10 worth
    RECOMMENDED_BOTVEGAS: float = 100.0  # Good starting amount


CONFIG = WalletConfig()

# =============================================================================
# OS Detection
# =============================================================================


def get_os_type() -> str:
    """Detect operating system.

    Returns:
        'macos', 'linux', 'windows', or 'unknown'
    """
    system = platform.system().lower()
    if system == "darwin":
        return "macos"
    elif system == "windows":
        return "windows"
    elif system == "linux":
        return "linux"
    else:
        return "unknown"


# =============================================================================
# Solana CLI Detection & Installation
# =============================================================================


def check_solana_installed() -> tuple[bool, str | None]:
    """Check if Solana CLI is installed and return version.

    Returns:
        Tuple of (is_installed, version_string)
    """
    # First check if command exists in PATH
    if shutil.which("solana") is None:
        return False, None

    try:
        result = subprocess.run(
            ["solana", "--version"], capture_output=True, text=True, timeout=10
        )
        if result.returncode == 0:
            # Output: "solana-cli 1.18.18 (src:...; feat:...)"
            version = result.stdout.strip().split()[1]
            return True, version
    except (subprocess.TimeoutExpired, FileNotFoundError, IndexError):
        pass

    return False, None


def install_solana_cli() -> tuple[bool, str]:
    """Install Solana CLI for the current OS.

    Returns:
        Tuple of (success, message)
    """
    os_type = get_os_type()

    if os_type == "macos":
        return _install_solana_macos()
    elif os_type == "linux":
        return _install_solana_linux()
    elif os_type == "windows":
        return _install_solana_windows()
    else:
        return False, f"Unsupported operating system: {platform.system()}"


def _install_solana_macos() -> tuple[bool, str]:
    """Install Solana CLI on macOS."""
    # Use official installer (works on both Intel and Apple Silicon)
    install_cmd = 'sh -c "$(curl -sSfL https://release.solana.com/stable/install)"'

    try:
        result = subprocess.run(
            install_cmd,
            shell=True,
            capture_output=True,
            text=True,
            timeout=300,  # 5 min timeout for download
        )

        if result.returncode == 0:
            return (
                True,
                "Solana CLI installed. You may need to restart your terminal or run: "
                "export PATH=\"$HOME/.local/share/solana/install/active_release/bin:$PATH\"",
            )
        else:
            return False, f"Installation failed: {result.stderr}"
    except subprocess.TimeoutExpired:
        return False, "Installation timed out. Check your internet connection."
    except Exception as e:
        return False, f"Installation error: {str(e)}"


def _install_solana_linux() -> tuple[bool, str]:
    """Install Solana CLI on Linux."""
    # Same installer works for Linux
    install_cmd = 'sh -c "$(curl -sSfL https://release.solana.com/stable/install)"'

    try:
        result = subprocess.run(
            install_cmd,
            shell=True,
            capture_output=True,
            text=True,
            timeout=300,
        )

        if result.returncode == 0:
            return (
                True,
                "Solana CLI installed. Run 'source ~/.profile' or restart terminal, "
                "or run: export PATH=\"$HOME/.local/share/solana/install/active_release/bin:$PATH\"",
            )
        else:
            return False, f"Installation failed: {result.stderr}"
    except subprocess.TimeoutExpired:
        return False, "Installation timed out."
    except Exception as e:
        return False, f"Installation error: {str(e)}"


def _install_solana_windows() -> tuple[bool, str]:
    """Install Solana CLI on Windows."""
    # Create temp directory
    temp_dir = "C:\\solana-install-tmp"
    os.makedirs(temp_dir, exist_ok=True)

    installer_path = f"{temp_dir}\\solana-install-init.exe"

    # Download installer
    download_cmd = (
        f"curl https://release.solana.com/stable/solana-install-init-x86_64-pc-windows-msvc.exe "
        f'--output "{installer_path}" --create-dirs'
    )

    try:
        # Download
        result = subprocess.run(
            download_cmd,
            shell=True,
            capture_output=True,
            text=True,
            timeout=120,
        )

        if result.returncode != 0:
            return False, f"Download failed: {result.stderr}"

        # Run installer
        install_result = subprocess.run(
            [installer_path, "stable"],
            capture_output=True,
            text=True,
            timeout=300,
        )

        if install_result.returncode == 0:
            return (
                True,
                "Solana CLI installed. Please restart your terminal/command prompt.",
            )
        else:
            return False, f"Installation failed: {install_result.stderr}"

    except subprocess.TimeoutExpired:
        return False, "Installation timed out."
    except Exception as e:
        return False, f"Installation error: {str(e)}"


# =============================================================================
# Keypair Generation
# =============================================================================


def generate_keypair() -> list[int] | None:
    """Generate a new Solana keypair.

    Returns:
        List of 64 bytes (secret key + public key) or None on failure
    """
    if SOLDERS_AVAILABLE:
        try:
            kp = Keypair()
            return list(bytes(kp))
        except Exception as e:
            logger.error(f"Failed to generate keypair with solders: {e}")

    # Fallback to CLI
    return _generate_keypair_cli()


def _generate_keypair_cli() -> list[int] | None:
    """Generate keypair using solana-keygen CLI."""
    import tempfile

    with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
        temp_path = f.name

    try:
        result = subprocess.run(
            ["solana-keygen", "new", "--no-bip39-passphrase", "-o", temp_path, "--force"],
            capture_output=True,
            check=True,
            timeout=30,
        )
        with open(temp_path) as f:
            return json.load(f)
    except Exception as e:
        logger.error(f"Failed to generate keypair with CLI: {e}")
        return None
    finally:
        try:
            os.unlink(temp_path)
        except Exception:
            pass


def get_address_from_keypair(keypair_bytes: list[int]) -> str:
    """Get public address from keypair bytes.

    Args:
        keypair_bytes: 64-byte keypair (secret + public)

    Returns:
        Base58-encoded public key string
    """
    if SOLDERS_AVAILABLE:
        try:
            kp = Keypair.from_bytes(bytes(keypair_bytes))
            return str(kp.pubkey())
        except Exception as e:
            logger.error(f"Failed to get address with solders: {e}")

    # Fallback to CLI
    return _get_address_cli(keypair_bytes)


def _get_address_cli(keypair_bytes: list[int]) -> str:
    """Get address using solana CLI."""
    import tempfile

    with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
        json.dump(keypair_bytes, f)
        temp_path = f.name

    try:
        result = subprocess.run(
            ["solana", "address", "-k", temp_path],
            capture_output=True,
            text=True,
            timeout=10,
        )
        return result.stdout.strip()
    finally:
        try:
            os.unlink(temp_path)
        except Exception:
            pass


# =============================================================================
# Secure Key Storage
# =============================================================================


def store_keypair(identity_name: str, keypair_bytes: list[int]) -> bool:
    """Store keypair securely using OS-native credential storage.

    Args:
        identity_name: Unique identifier for this keypair
        keypair_bytes: 64-byte keypair to store

    Returns:
        True if stored successfully
    """
    keypair_json = json.dumps(keypair_bytes)

    if KEYRING_AVAILABLE:
        try:
            keyring.set_password(CONFIG.SERVICE_NAME, identity_name, keypair_json)
            logger.info(f"Stored keypair for '{identity_name}' in system keyring")
            return True
        except Exception as e:
            logger.warning(f"Keyring storage failed, falling back to file: {e}")

    # Fallback: encrypted file storage
    return _store_keypair_file(identity_name, keypair_json)


def retrieve_keypair(identity_name: str) -> list[int] | None:
    """Retrieve keypair from secure storage.

    Args:
        identity_name: Unique identifier for the keypair

    Returns:
        64-byte keypair or None if not found
    """
    keypair_json = None

    if KEYRING_AVAILABLE:
        try:
            keypair_json = keyring.get_password(CONFIG.SERVICE_NAME, identity_name)
        except Exception as e:
            logger.warning(f"Keyring retrieval failed: {e}")

    if keypair_json is None:
        # Try file-based storage
        keypair_json = _retrieve_keypair_file(identity_name)

    if keypair_json:
        try:
            return json.loads(keypair_json)
        except json.JSONDecodeError:
            logger.error(f"Invalid keypair JSON for '{identity_name}'")

    return None


def delete_keypair(identity_name: str) -> bool:
    """Delete keypair from secure storage.

    Args:
        identity_name: Unique identifier for the keypair

    Returns:
        True if deleted successfully
    """
    success = False

    if KEYRING_AVAILABLE:
        try:
            keyring.delete_password(CONFIG.SERVICE_NAME, identity_name)
            success = True
            logger.info(f"Deleted keypair for '{identity_name}' from keyring")
        except Exception as e:
            logger.warning(f"Keyring deletion failed: {e}")

    # Also try to delete file if it exists
    file_path = CONFIG.KEYS_DIR / f"{identity_name}.json"
    if file_path.exists():
        try:
            file_path.unlink()
            success = True
            logger.info(f"Deleted keypair file for '{identity_name}'")
        except Exception as e:
            logger.error(f"Failed to delete keypair file: {e}")

    return success


def _store_keypair_file(identity_name: str, keypair_json: str) -> bool:
    """Fallback: Store keypair in file with restrictive permissions."""
    try:
        CONFIG.KEYS_DIR.mkdir(parents=True, exist_ok=True)
        file_path = CONFIG.KEYS_DIR / f"{identity_name}.json"

        # Write file
        file_path.write_text(keypair_json)

        # Set restrictive permissions (owner read/write only)
        if os.name != "nt":  # Unix-like
            os.chmod(file_path, 0o600)

        logger.info(f"Stored keypair for '{identity_name}' in file: {file_path}")
        return True
    except Exception as e:
        logger.error(f"Failed to store keypair file: {e}")
        return False


def _retrieve_keypair_file(identity_name: str) -> str | None:
    """Fallback: Retrieve keypair from file."""
    file_path = CONFIG.KEYS_DIR / f"{identity_name}.json"
    if file_path.exists():
        try:
            return file_path.read_text()
        except Exception as e:
            logger.error(f"Failed to read keypair file: {e}")
    return None


def list_stored_identities() -> list[str]:
    """List all identities with stored keypairs.

    Returns:
        List of identity names
    """
    identities = set()

    # Check file-based storage
    if CONFIG.KEYS_DIR.exists():
        for f in CONFIG.KEYS_DIR.glob("*.json"):
            identities.add(f.stem)

    # Note: keyring doesn't have a "list all" function,
    # so we rely on file-based tracking
    # For a complete solution, we'd need to maintain a separate index

    return sorted(identities)


# =============================================================================
# Balance Checking
# =============================================================================


def get_wallet_balances(
    address: str, rpc_url: str | None = None
) -> tuple[float | None, float | None]:
    """Get SOL and $BOTVEGAS balances for an address.

    Args:
        address: Solana wallet address
        rpc_url: RPC endpoint (defaults to CONFIG.RPC_URL based on SOLANA_NETWORK)

    Returns:
        Tuple of (sol_balance, botvegas_balance), None if fetch failed
    """
    rpc_url = rpc_url or CONFIG.RPC_URL

    sol_balance = _get_sol_balance(address, rpc_url)
    botvegas_balance = _get_spl_token_balance(address, CONFIG.BOTVEGAS_MINT, rpc_url)

    return sol_balance, botvegas_balance


def _get_sol_balance(address: str, rpc_url: str) -> float | None:
    """Get SOL balance using RPC or CLI."""
    if SOLANA_RPC_AVAILABLE:
        try:
            client = SolanaClient(rpc_url)
            pubkey = Pubkey.from_string(address) if SOLDERS_AVAILABLE else address
            response = client.get_balance(pubkey, commitment=Confirmed)
            if response.value is not None:
                # Convert lamports to SOL
                return response.value / 1_000_000_000
        except Exception as e:
            logger.warning(f"RPC balance check failed: {e}")

    # Fallback to CLI
    try:
        result = subprocess.run(
            ["solana", "balance", address, "--url", rpc_url],
            capture_output=True,
            text=True,
            timeout=10,
        )
        if result.returncode == 0:
            # Output: "1.5 SOL"
            return float(result.stdout.strip().split()[0])
    except Exception as e:
        logger.warning(f"CLI balance check failed: {e}")

    return None


def _get_spl_token_balance(
    owner_address: str, mint_address: str, rpc_url: str
) -> float | None:
    """Get SPL token balance using CLI.

    Note: Using CLI because solana-py's token support is more complex.
    """
    try:
        result = subprocess.run(
            [
                "spl-token",
                "balance",
                mint_address,
                "--owner",
                owner_address,
                "--url",
                rpc_url,
            ],
            capture_output=True,
            text=True,
            timeout=10,
        )
        if result.returncode == 0:
            balance_str = result.stdout.strip()
            if balance_str:
                return float(balance_str)
    except Exception as e:
        logger.warning(f"SPL token balance check failed: {e}")

    return None


# =============================================================================
# Funding Instructions Template
# =============================================================================

FUNDING_INSTRUCTIONS_TEMPLATE = """
🎰 **Wallet Setup Complete!**

**Your Agent's Wallet Address:**
```
{wallet_address}
```

---

⚠️ **IMPORTANT: Fund your agent to start playing staked games!**

Your agent needs two things:

1. **SOL** (for transaction fees)
   - Send ~0.05 SOL (about $10 worth)
   - This covers hundreds of game transactions

2. **$BOTVEGAS** (for staking in games)
   - Buy $BOTVEGAS on pump.fun: {pump_fun_url}
   - Send desired amount to your agent's wallet
   - Minimum recommended: 100 $BOTVEGAS to start

---

**How to fund:**

**Option A: From Phantom/Solflare**
1. Open your wallet app
2. Send SOL to: `{wallet_address}`
3. Send $BOTVEGAS to the same address

**Option B: From an Exchange**
1. Withdraw SOL to: `{wallet_address}`
2. Buy $BOTVEGAS on pump.fun, send to same address

---

**Verify funding:**
View wallet on Solscan: {solscan_url}

Once funded, use `botvegas_get_wallet_balance` to check balances,
then `botvegas_create_staked_game` to start playing!
"""


def build_funding_instructions(wallet_address: str) -> str:
    """Build funding instructions for a wallet.

    Args:
        wallet_address: The agent's wallet address

    Returns:
        Formatted funding instructions string
    """
    return FUNDING_INSTRUCTIONS_TEMPLATE.format(
        wallet_address=wallet_address,
        pump_fun_url=CONFIG.PUMP_FUN_URL,
        solscan_url=CONFIG.SOLSCAN_WALLET_URL.format(address=wallet_address),
    )


# =============================================================================
# Wallet Setup Response
# =============================================================================


@dataclass
class WalletSetupResult:
    """Result of wallet setup operation."""

    success: bool
    wallet_address: str | None
    identity_name: str
    is_new_wallet: bool
    solana_cli_installed: bool
    solana_cli_version: str | None
    sol_balance: float | None
    botvegas_balance: float | None
    funding_instructions: str
    botvegas_pump_fun_url: str
    solscan_wallet_url: str | None
    error: str | None = None

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for MCP response."""
        return {
            "success": self.success,
            "wallet_address": self.wallet_address,
            "identity_name": self.identity_name,
            "is_new_wallet": self.is_new_wallet,
            "solana_cli_installed": self.solana_cli_installed,
            "solana_cli_version": self.solana_cli_version,
            "sol_balance": self.sol_balance,
            "botvegas_balance": self.botvegas_balance,
            "funding_instructions": self.funding_instructions,
            "botvegas_pump_fun_url": self.botvegas_pump_fun_url,
            "solscan_wallet_url": self.solscan_wallet_url,
            "error": self.error,
        }


def setup_wallet(identity_name: str, force_new: bool = False) -> WalletSetupResult:
    """Set up a wallet for an identity.

    Args:
        identity_name: Unique identifier for this wallet
        force_new: If True, generate new keypair even if one exists

    Returns:
        WalletSetupResult with wallet details and funding instructions
    """
    # Step 1: Check/install Solana CLI
    cli_installed, cli_version = check_solana_installed()

    if not cli_installed and not SOLDERS_AVAILABLE:
        # Need either CLI or solders for keypair operations
        install_success, install_msg = install_solana_cli()
        if not install_success:
            return WalletSetupResult(
                success=False,
                wallet_address=None,
                identity_name=identity_name,
                is_new_wallet=False,
                solana_cli_installed=False,
                solana_cli_version=None,
                sol_balance=None,
                botvegas_balance=None,
                funding_instructions="",
                botvegas_pump_fun_url=CONFIG.PUMP_FUN_URL,
                solscan_wallet_url=None,
                error=f"Failed to install Solana CLI: {install_msg}. "
                f"Please install manually: https://docs.solanalabs.com/cli/install",
            )
        cli_installed, cli_version = check_solana_installed()

    # Step 2: Check for existing keypair
    existing_keypair = retrieve_keypair(identity_name)
    is_new_wallet = existing_keypair is None or force_new

    if is_new_wallet:
        # Generate new keypair
        keypair_bytes = generate_keypair()
        if keypair_bytes is None:
            return WalletSetupResult(
                success=False,
                wallet_address=None,
                identity_name=identity_name,
                is_new_wallet=True,
                solana_cli_installed=cli_installed,
                solana_cli_version=cli_version,
                sol_balance=None,
                botvegas_balance=None,
                funding_instructions="",
                botvegas_pump_fun_url=CONFIG.PUMP_FUN_URL,
                solscan_wallet_url=None,
                error="Failed to generate keypair. Please ensure solders is installed or Solana CLI is available.",
            )

        # Store securely
        if not store_keypair(identity_name, keypair_bytes):
            return WalletSetupResult(
                success=False,
                wallet_address=None,
                identity_name=identity_name,
                is_new_wallet=True,
                solana_cli_installed=cli_installed,
                solana_cli_version=cli_version,
                sol_balance=None,
                botvegas_balance=None,
                funding_instructions="",
                botvegas_pump_fun_url=CONFIG.PUMP_FUN_URL,
                solscan_wallet_url=None,
                error="Failed to store keypair securely.",
            )
    else:
        keypair_bytes = existing_keypair

    # Step 3: Get wallet address
    wallet_address = get_address_from_keypair(keypair_bytes)

    # Step 4: Check balances (if wallet exists on-chain)
    sol_balance, botvegas_balance = get_wallet_balances(wallet_address)

    # Step 5: Build funding instructions
    solscan_url = CONFIG.SOLSCAN_WALLET_URL.format(address=wallet_address)
    funding_instructions = build_funding_instructions(wallet_address)

    return WalletSetupResult(
        success=True,
        wallet_address=wallet_address,
        identity_name=identity_name,
        is_new_wallet=is_new_wallet,
        solana_cli_installed=cli_installed,
        solana_cli_version=cli_version,
        sol_balance=sol_balance,
        botvegas_balance=botvegas_balance,
        funding_instructions=funding_instructions,
        botvegas_pump_fun_url=CONFIG.PUMP_FUN_URL,
        solscan_wallet_url=solscan_url,
    )


# =============================================================================
# Balance Check Response
# =============================================================================


@dataclass
class WalletBalanceResult:
    """Result of balance check operation."""

    wallet_address: str
    identity_name: str
    sol_balance: float
    botvegas_balance: float
    has_enough_sol_for_fees: bool
    has_enough_botvegas_to_play: bool
    can_play_staked_games: bool
    warnings: list[str]
    suggestions: list[str]
    solscan_url: str
    pump_fun_url: str

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for MCP response."""
        return {
            "wallet_address": self.wallet_address,
            "identity_name": self.identity_name,
            "sol_balance": self.sol_balance,
            "botvegas_balance": self.botvegas_balance,
            "has_enough_sol_for_fees": self.has_enough_sol_for_fees,
            "has_enough_botvegas_to_play": self.has_enough_botvegas_to_play,
            "can_play_staked_games": self.can_play_staked_games,
            "warnings": self.warnings,
            "suggestions": self.suggestions,
            "solscan_url": self.solscan_url,
            "pump_fun_url": self.pump_fun_url,
        }


def check_wallet_balance(identity_name: str) -> WalletBalanceResult:
    """Check wallet balance for an identity.

    Args:
        identity_name: Identity whose wallet to check

    Returns:
        WalletBalanceResult with balance info and recommendations

    Raises:
        ValueError: If no wallet found for identity
    """
    # Get keypair
    keypair_bytes = retrieve_keypair(identity_name)
    if keypair_bytes is None:
        raise ValueError(
            f"No wallet found for identity '{identity_name}'. "
            f"Run botvegas_setup_wallet first."
        )

    wallet_address = get_address_from_keypair(keypair_bytes)
    sol_balance, botvegas_balance = get_wallet_balances(wallet_address)

    # Default to 0 if couldn't fetch
    sol_balance = sol_balance or 0.0
    botvegas_balance = botvegas_balance or 0.0

    # Check thresholds
    has_enough_sol = sol_balance >= CONFIG.MIN_SOL_FOR_FEES
    has_enough_botvegas = botvegas_balance >= CONFIG.MIN_BOTVEGAS_TO_PLAY
    can_play = has_enough_sol and has_enough_botvegas

    # Build warnings and suggestions
    warnings = []
    suggestions = []

    if sol_balance == 0:
        warnings.append("⚠️ No SOL! You cannot pay transaction fees.")
        suggestions.append(
            f"Send at least {CONFIG.RECOMMENDED_SOL} SOL to {wallet_address}"
        )
    elif sol_balance < CONFIG.MIN_SOL_FOR_FEES:
        warnings.append(
            f"⚠️ Low SOL ({sol_balance:.4f}). May run out of fees soon."
        )
        suggestions.append(
            f"Top up with {CONFIG.RECOMMENDED_SOL - sol_balance:.4f} more SOL"
        )

    if botvegas_balance == 0:
        warnings.append("⚠️ No $BOTVEGAS! You cannot stake in games.")
        suggestions.append(f"Buy $BOTVEGAS on pump.fun: {CONFIG.PUMP_FUN_URL}")
    elif botvegas_balance < CONFIG.MIN_BOTVEGAS_TO_PLAY:
        warnings.append(
            f"⚠️ Low $BOTVEGAS ({botvegas_balance:.2f}). May not meet minimum buy-ins."
        )

    if can_play and not warnings:
        suggestions.append(
            "✅ Ready to play! Use botvegas_create_staked_game or botvegas_list_staked_games."
        )

    return WalletBalanceResult(
        wallet_address=wallet_address,
        identity_name=identity_name,
        sol_balance=sol_balance,
        botvegas_balance=botvegas_balance,
        has_enough_sol_for_fees=has_enough_sol,
        has_enough_botvegas_to_play=has_enough_botvegas,
        can_play_staked_games=can_play,
        warnings=warnings,
        suggestions=suggestions,
        solscan_url=CONFIG.SOLSCAN_WALLET_URL.format(address=wallet_address),
        pump_fun_url=CONFIG.PUMP_FUN_URL,
    )


# =============================================================================
# List Wallets
# =============================================================================


def list_agent_wallets() -> dict[str, Any]:
    """List all agent wallets stored on this machine.

    Returns:
        Dictionary with wallets list and totals
    """
    identities = list_stored_identities()
    wallets = []
    total_sol = 0.0
    total_botvegas = 0.0

    for identity_name in identities:
        keypair_bytes = retrieve_keypair(identity_name)
        if keypair_bytes:
            address = get_address_from_keypair(keypair_bytes)
            sol, botvegas = get_wallet_balances(address)
            sol = sol or 0.0
            botvegas = botvegas or 0.0

            wallets.append(
                {
                    "identity_name": identity_name,
                    "wallet_address": address,
                    "sol_balance": sol,
                    "botvegas_balance": botvegas,
                    "solscan_url": CONFIG.SOLSCAN_WALLET_URL.format(address=address),
                }
            )

            total_sol += sol
            total_botvegas += botvegas

    return {
        "wallets": wallets,
        "total_sol": total_sol,
        "total_botvegas": total_botvegas,
        "wallet_count": len(wallets),
    }
