using System.Numerics;
using Microsoft.Extensions.Caching.Memory;
using Nethereum.ABI;
using Nethereum.Util;

namespace Managing.Infrastructure.Evm.Services.Gmx;

public class GmxV2Encoder
{
    private readonly MemoryCache _dataCache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 10000 });
    private readonly MemoryCache _stringCache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 10000 });
    private readonly ABIEncode _abiEncode = new ABIEncode();
    private readonly MemoryCacheEntryOptions _absoluteExpiration = new MemoryCacheEntryOptions { Size = 1 };


    public byte[] HashData(List<(string dataType, object dataValues)> datas)
    {
        var key = GenerateKey(datas);

        if (_dataCache.TryGetValue(key, out byte[] cachedHash))
        {
            return cachedHash;
        }

        var abiValues = new List<ABIValue>();
        foreach (var data in datas)
        {
            var abiValue = new ABIValue(data.dataType, data.dataValues);
            abiValues.Add(abiValue);
        }

        var abiEncode = new ABIEncode();
        var encoded = abiEncode.GetABIEncoded(abiValues.ToArray());
        var hash = new Sha3Keccack().CalculateHash(encoded);
        _dataCache.Set(key, hash, _absoluteExpiration);

        return hash;
    }


    public byte[] HashString(string input)
    {
        if (_stringCache.TryGetValue(input, out byte[] cachedHash))
        {
            return cachedHash;
        }

        // Adjusted to directly work with byte arrays

        var hash = HashData(new List<(string dataType, object dataValues)>() { ("string", input) });
        _stringCache.Set(input, hash, _absoluteExpiration);

        return hash;
    }

    private string GenerateKey(List<(string dataType, object dataValues)> datas)
    {
        var combined = new List<string>();
        foreach (var data in datas)
        {
            combined.Add(
                $"{data.dataType}:{(data.dataValues is BigInteger bigInt ? bigInt.ToString() : data.dataValues)}");
        }

        return string.Join(",", combined);
    }
}