# Migration Guide: Promise-based WebGL Interop

This guide explains how to migrate from the old callback-based interop approach to the new promise-based approach.

## Overview

The new approach provides:
- **Async/await support** for cleaner, more readable code
- **Automatic memory management** - no manual callback cleanup
- **Better error handling** with try/catch blocks
- **Reduced boilerplate** - single interop layer handles all calls
- **Type safety** with generic methods

## Before (Old Approach)

```csharp
// Old callback-based approach
public class ArkadiumWallet : IArkadiumWallet
{
    private static Action<bool> _isGemsSupportedCb;
    private static Action<int> _getGemsCb;
    private static Action<bool> _consumeGemsCb;

    public void IsGemsSupported(Action<bool> cb)
    {
        _isGemsSupportedCb = cb;
        _IsGemsSupported(IsGemsSupportedCb);
    }

    [MonoPInvokeCallback(typeof(Action<int>))]
    private static void IsGemsSupportedCb(int result)
    {
        var isSupported = result == 1;
        _isGemsSupportedCb?.Invoke(isSupported);
        _isGemsSupportedCb = null; // Manual cleanup required
    }
}

// Usage
wallet.IsGemsSupported((isSupported) => {
    if (isSupported) {
        wallet.GetGems((gems) => {
            Debug.Log($"Gems: {gems}");
        });
    }
});
```

## After (New Approach)

```csharp
// New promise-based approach
public class ArkadiumWalletV2 : IArkadiumWalletV2
{
    public async Task<bool> IsGemsSupportedAsync()
    {
        try
        {
            var result = await WebGLInterop.InvokeAsync<string>("wallet.isGemsSupported");
            return result.ToLower() == "true";
        }
        catch (Exception ex)
        {
            Debug.LogError($"Error: {ex.Message}");
            return false;
        }
    }
}

// Usage
var isSupported = await wallet.IsGemsSupportedAsync();
if (isSupported) {
    var gems = await wallet.GetGemsAsync();
    Debug.Log($"Gems: {gems}");
}
```

## Migration Steps

### Step 1: Add the new interop files

Ensure these files are in your project:
- `Assets/ArkadiumSDK/Scripts/Interop/WebGLInterop.cs`
- `Assets/ArkadiumSDK/Plugins/WebGLInterop.jslib`

### Step 2: Create new async methods

For each API method, create an async version:

```csharp
// Old method
public void GetGems(Action<int> cb);

// New method
public async Task<int> GetGemsAsync();
```

### Step 3: Implement the async method

```csharp
public async Task<int> GetGemsAsync()
{
    try
    {
        var result = await WebGLInterop.InvokeAsync<string>("wallet.getGems");
        return int.TryParse(result, out var gems) ? gems : 0;
    }
    catch (Exception ex)
    {
        Debug.LogError($"Error getting gems: {ex.Message}");
        return -1;
    }
}
```

### Step 4: Keep legacy methods for backward compatibility

```csharp
public void GetGems(Action<int> cb)
{
    _ = InvokeAsyncCallback(async () =>
    {
        var result = await GetGemsAsync();
        cb?.Invoke(result);
    });
}

/// <summary>
/// Helper method to properly handle async callbacks in Unity WebGL
/// </summary>
private async Task InvokeAsyncCallback(Func<Task> asyncOperation)
{
    try
    {
        await asyncOperation();
    }
    catch (Exception ex)
    {
        Debug.LogError($"Error in async callback: {ex.Message}");
    }
}
```

### Step 5: Update usage gradually

You can migrate one method at a time:

```csharp
// Old way (still works)
wallet.GetGems((gems) => Debug.Log($"Gems: {gems}"));

// New way (recommended)
var gems = await wallet.GetGemsAsync();
Debug.Log($"Gems: {gems}");
```

## Benefits of the New Approach

### 1. Cleaner Code
```csharp
// Before: Callback hell
wallet.IsGemsSupported((isSupported) => {
    if (isSupported) {
        wallet.GetGems((gems) => {
            if (gems > 0) {
                wallet.ConsumeGems(100, (success) => {
                    Debug.Log($"Consumed: {success}");
                });
            }
        });
    }
});

// After: Clean async/await
var isSupported = await wallet.IsGemsSupportedAsync();
if (isSupported) {
    var gems = await wallet.GetGemsAsync();
    if (gems > 0) {
        var success = await wallet.ConsumeGemsAsync(100);
        Debug.Log($"Consumed: {success}");
    }
}
```

### 2. Better Error Handling
```csharp
try
{
    var gems = await wallet.GetGemsAsync();
    Debug.Log($"Gems: {gems}");
}
catch (Exception ex)
{
    Debug.LogError($"Failed to get gems: {ex.Message}");
    // Handle error gracefully
}
```

### 3. Parallel Operations
```csharp
// Run multiple operations in parallel
var isSupportedTask = wallet.IsGemsSupportedAsync();
var gemsTask = wallet.GetGemsAsync();

await Task.WhenAll(isSupportedTask, gemsTask);

var isSupported = isSupportedTask.Result;
var gems = gemsTask.Result;
```

### 4. Type Safety
```csharp
// The interop layer handles type conversion automatically
var isSupported = await WebGLInterop.InvokeAsync<bool>("wallet.isGemsSupported");
var gems = await WebGLInterop.InvokeAsync<int>("wallet.getGems");
```

## Common Patterns

### Method with Parameters
```csharp
public async Task<bool> ConsumeGemsAsync(int value)
{
    var result = await WebGLInterop.InvokeAsync<string>("wallet.consumeGems", new { value });
    return result.ToLower() == "true";
}
```

### Method with Complex Parameters
```csharp
public async Task<bool> ShowAdAsync(int duration, string placement)
{
    var parameters = new { duration, placement };
    var result = await WebGLInterop.InvokeAsync<string>("ads.showAd", parameters);
    return result.ToLower() == "true";
}
```

### Void Methods
```csharp
public void OnGameStart()
{
    WebGLInterop.InvokeVoid("lifecycle.onGameStart");
}
```

## Testing

Use the `WalletExample.cs` script to test the new approach:

1. Attach the script to a GameObject
2. Run the scene
3. Check the console for examples of both approaches
4. Use the UI buttons to test different scenarios

## Troubleshooting

### Common Issues

1. **Task not completing**: Ensure the JavaScript method exists and returns a promise
2. **Type conversion errors**: Check that the return type matches what the JavaScript method returns
3. **Memory leaks**: The new approach automatically manages memory, but ensure you're not holding references to tasks
4. **Task.Run not working in WebGL**: Unity WebGL runs in a single-threaded environment, so `Task.Run` may not work as expected. Use the `InvokeAsyncCallback` helper method instead.

### Important Notes

**Unity WebGL Threading**: Unity WebGL builds run in a single-threaded environment, which means:
- `Task.Run` may not work as expected
- All operations should run on the main thread
- Use the `InvokeAsyncCallback` helper method for legacy callback compatibility
- Consider using Unity's coroutine system for complex async operations

### Debug Tips

1. Enable logging in the WebGLInterop to see what's happening
2. Check the browser console for JavaScript errors
3. Use try/catch blocks to handle errors gracefully

## Next Steps

1. Start with one simple method (like `IsGemsSupported`)
2. Test thoroughly in both editor and WebGL builds
3. Gradually migrate other methods
4. Remove legacy methods once all code is migrated
5. Consider using the code generation approach for future APIs 