#!/usr/bin/env python3
# Copyright (c) Subzero Labs, Inc.
# SPDX-License-Identifier: Apache-2.0

"""
Program Management Example - Deploying and Invoking Programs

This example demonstrates the new program management capabilities in the Python CDK:
- Program deployment with the Loader V4 system
- Program invocation with custom instructions
- Error handling and transaction monitoring
"""

from rialo_py_cdk import (
    generate_keypair,
    Wallet,
    HttpRpcClient,
    ProgramDeployment,
    ProgramInvocation,
    PublicKey,
    get_localnet_url,
    RialoException,
)
import os
import tempfile


def program_management_example():
    """Demonstrate program deployment and invocation capabilities."""
    print('🚀 Starting Rialo CDK Program Management Example\n')

    try:
        # 1. Create a wallet for program deployment
        print('1. Creating deployment wallet...')
        keypair = generate_keypair()
        wallet = Wallet('deployer-wallet', keypair, None, None)
        print(f'   Deployer public key: {wallet.get_public_key_string()}')

        # 2. Create RPC client
        print('2. Creating RPC client...')
        client = HttpRpcClient(get_localnet_url())
        print(f'   Connected to: {get_localnet_url()}')

        # 3. Create a mock program file for demonstration
        print('3. Creating mock program binary...')
        with tempfile.NamedTemporaryFile(mode='wb', suffix='.so', delete=False) as f:
            # Create a minimal ELF-like file (starts with ELF magic)
            f.write(b'\x7fELF')  # ELF magic number
            f.write(b'\x00' * 100)  # Padding to make it look like a real program
            mock_program_path = f.name
        
        print(f'   Mock program created at: {mock_program_path}')

        # 4. Deploy the program
        print('4. Deploying program...')
        try:
            deployment = ProgramDeployment(mock_program_path)
            
            # Note: In a real deployment, you would need sufficient funds
            # For this demo, we'll catch the likely funding error
            program_id = deployment.deploy(client, wallet)
            print(f'   ✅ Program deployed successfully!')
            print(f'   Program ID: {program_id}')
            
            # 5. Create a program invocation
            print('5. Creating program invocation...')
            invocation = ProgramInvocation(program_id)
            
            # Add accounts and instruction data
            wallet_pubkey = PublicKey(wallet.get_public_key().to_bytes())
            invocation.add_signer_account(wallet_pubkey, True)
            invocation.with_data([1, 2, 3, 4])  # Custom instruction data
            
            print('   Program invocation configured')
            
            # 6. Simulate the program invocation
            print('6. Simulating program invocation...')
            invocation.simulate(client, wallet)
            print('   ✅ Simulation successful!')
            
            # 7. Execute the program invocation (this would likely fail without proper setup)
            print('7. Attempting to invoke program...')
            try:
                signature = invocation.invoke(client, wallet)
                print(f'   ✅ Program invoked successfully!')
                print(f'   Transaction signature: {signature}')
            except RialoException as e:
                print(f'   ⚠️ Program invocation failed (expected in demo): {e}')
                print('   (This is normal - the mock program is not executable)')
                
        except RialoException as e:
            print(f'   ⚠️ Program deployment failed (expected in demo): {e}')
            print('   (This is likely due to insufficient funds or network not running)')
            
            # Continue with invocation demo using a mock program ID
            print('\n   Continuing with invocation demo using mock program ID...')
            mock_program_id = PublicKey(bytes([42] * 32))
            
            invocation = ProgramInvocation(mock_program_id)
            wallet_pubkey = PublicKey(wallet.get_public_key().to_bytes())
            invocation.add_signer_account(wallet_pubkey, True)
            invocation.with_data([1, 2, 3, 4])
            
            try:
                invocation.simulate(client, wallet)
                print('   ✅ Mock invocation simulation successful!')
            except RialoException as e:
                print(f'   ⚠️ Mock invocation simulation failed: {e}')

        # 8. Demonstrate different invocation patterns
        print('8. Demonstrating invocation patterns...')
        
        # Transfer-like pattern
        from_pubkey = PublicKey(wallet.get_public_key().to_bytes())
        to_pubkey = PublicKey(bytes([i % 256 for i in range(32)]))
        mock_program_id = PublicKey(bytes([42] * 32))
        
        # Create transfer-like invocation
        transfer_invocation = ProgramInvocation(mock_program_id)
        transfer_invocation.add_signer_account(from_pubkey, True)  # From account (signer + writable)
        transfer_invocation.add_writable_account(to_pubkey)        # To account (writable)
        transfer_invocation.with_data([0, 1, 0, 0, 100, 0, 0, 0])  # Transfer 100 units
        
        print('   Transfer-like invocation created')
        
        # Initialize-like pattern  
        init_invocation = ProgramInvocation(mock_program_id)
        init_invocation.add_signer_account(from_pubkey, True)     # Initializer
        init_invocation.add_writable_account(to_pubkey)          # Account to initialize
        init_invocation.with_data([1])                           # Initialize command
        
        print('   Initialize-like invocation created')

        print('\n✅ Program management demo completed!')
        print('\n📋 Summary of capabilities demonstrated:')
        print('   • Program deployment with Loader V4')
        print('   • Program invocation with custom instructions')
        print('   • Account management for instructions')
        print('   • Transaction simulation')
        print('   • Common invocation patterns')

    except Exception as error:
        print(f'❌ Error during program management demo: {error}')
        raise error
        
    finally:
        # Clean up the temporary file
        if 'mock_program_path' in locals():
            try:
                os.unlink(mock_program_path)
                print('   🧹 Cleaned up temporary files')
            except:
                pass


def main():
    """Run the program management example."""
    try:
        program_management_example()
        print('\n🎉 Program management example finished!')
    except Exception as error:
        print(f'\n💥 Example failed: {error}')
        import traceback
        traceback.print_exc()
        exit(1)


if __name__ == "__main__":
    main()