using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using Newtonsoft.Json;

namespace Arkadium.Interop
{
    /// <summary>
    /// Mock WebGL interop provider for testing and editor use
    /// </summary>
    public class MockWebGLInteropProvider : IWebGLInteropProvider
    {
        private readonly Dictionary<string, WalletInventoryItem> _walletInventory = new()
        {
            { "mock_item", new WalletInventoryItem { sku = "mock_item", amount = 3 } },
            { "mock_consumable", new WalletInventoryItem { sku = "mock_consumable", amount = 1 } }
        };

        private readonly Dictionary<string, WalletInventoryItem> _walletCatalog = new()
        {
            { "mock_item", new WalletInventoryItem { sku = "mock_item", amount = 0 } },
            { "mock_consumable", new WalletInventoryItem { sku = "mock_consumable", amount = 0 } },
            { "mock_bonus", new WalletInventoryItem { sku = "mock_bonus", amount = 0 } }
        };

        private readonly Dictionary<string, WalletBundle> _walletBundles = new()
        {
            {
                "mock_bundle",
                new WalletBundle
                {
                    sku = "mock_bundle",
                    price = 100,
                    salesPrice = 75,
                    salesEndDate = "2099-12-31T23:59:59Z",
                    isSalesActive = true,
                    contents = new[]
                    {
                        new WalletInventoryItem { sku = "mock_item", amount = 2 },
                        new WalletInventoryItem { sku = "mock_bonus", amount = 1 }
                    }
                }
            }
        };

        public async Task<T> InvokeAsync<T>(string methodName, object parameters = null)
        {
            // Simulate async behavior
            await Task.Delay(10);

            // Return mock data based on method name
            return GetMockResult<T>(methodName, parameters);
        }

        public void InvokeVoid(string methodName, object parameters = null)
        {
            // Log the call for debugging
            Debug.Log($"[Mock] InvokeVoid: {methodName} with params: {JsonUtility.ToJson(parameters)}");
        }

        private T GetMockResult<T>(string methodName, object parameters)
        {
            // Wallet methods
            if (methodName == "wallet.isGemsSupported")
                return (T)(object)true;

            if (methodName == "wallet.getGems")
                return (T)(object)10000;

            if (methodName == "wallet.consumeGems")
                return (T)(object)true;

            if (methodName == "wallet.getInventory")
                return (T)(object)GetMockWalletInventoryJson();

            if (methodName == "wallet.getInventoryItemAmount")
                return (T)(object)GetMockWalletInventoryItemAmount(parameters);

            if (methodName == "wallet.consumeInventoryItem")
                return (T)(object)ConsumeMockWalletInventoryItem(parameters);

            if (methodName == "wallet.hasInventoryItem")
                return (T)(object)HasMockWalletInventoryItem(parameters);

            if (methodName == "wallet.getBundles")
                return (T)(object)GetMockWalletBundlesJson();

            if (methodName == "wallet.purchaseBundle")
                return (T)(object)PurchaseMockWalletBundle(parameters);

            if (methodName == "wallet.getCatalog")
                return (T)(object)GetMockWalletCatalogJson();

            if (methodName == "wallet.grantCatalogItem")
                return (T)(object)GrantMockWalletCatalogItem(parameters);

            // Tournament methods
            if (methodName == "tournaments.getTournaments")
                return (T)(object)GetMockTournamentsJson();

            if (methodName == "tournaments.hasJoinedTournament")
                return (T)(object)(parameters?.ToString().Contains("tournament_1") == true);

            if (methodName == "tournaments.joinTournament")
                return (T)(object)true;

            if (methodName == "tournaments.canSubmitScoreToTournament")
                return (T)(object)(parameters?.ToString().Contains("tournament_1") == true);

            if (methodName == "tournaments.submitTournamentScore")
            {
                Debug.Log($"[Mock] Submitted tournament score: {JsonUtility.ToJson(parameters)}");
                return (T)(object)true;
            }

            if (methodName == "tournaments.getTopTournamentEntries")
                return (T)(object)GetMockTournamentEntriesJson();

            if (methodName == "tournaments.getTournamentUserEntry")
                return (T)(object)GetMockUserEntryJson();

            if (methodName == "tournaments.getTournamentUsersEntries")
                return (T)(object)GetMockTournamentEntriesJson();

            if (methodName == "tournaments.getTournamentEntriesAroundUser")
                return (T)(object)GetMockTournamentEntriesJson();

            // Default fallback
            Debug.LogWarning($"[Mock] Unknown method: {methodName}");
            return default(T);
        }

        private string GetMockWalletInventoryJson()
        {
            return JsonConvert.SerializeObject(_walletInventory);
        }

        private string GetMockWalletBundlesJson()
        {
            return JsonConvert.SerializeObject(_walletBundles);
        }

        private string GetMockWalletCatalogJson()
        {
            return JsonConvert.SerializeObject(_walletCatalog);
        }

        private int GetMockWalletInventoryItemAmount(object parameters)
        {
            var sku = GetMockWalletInventorySku(parameters);
            return sku != null && _walletInventory.TryGetValue(sku, out var item) ? item.amount : 0;
        }

        private bool ConsumeMockWalletInventoryItem(object parameters)
        {
            var sku = GetMockWalletInventorySku(parameters);
            var amount = GetMockWalletInventoryAmount(parameters);
            if (string.IsNullOrEmpty(sku) ||
                amount <= 0 ||
                !_walletInventory.TryGetValue(sku, out var item) ||
                item.amount < amount)
                return false;

            item.amount -= amount;
            if (item.amount == 0)
                _walletInventory.Remove(sku);

            return true;
        }

        private bool HasMockWalletInventoryItem(object parameters)
        {
            var amount = GetMockWalletInventoryAmount(parameters);
            return amount > 0 && GetMockWalletInventoryItemAmount(parameters) >= amount;
        }

        private bool PurchaseMockWalletBundle(object parameters)
        {
            var bundleSku = GetMockWalletInventorySku(parameters);
            var amount = GetMockWalletInventoryAmount(parameters);
            if (string.IsNullOrEmpty(bundleSku) ||
                amount <= 0 ||
                !_walletBundles.TryGetValue(bundleSku, out var bundle))
                return false;

            var additions = new Dictionary<string, int>();
            foreach (var item in bundle.contents ?? Array.Empty<WalletInventoryItem>())
            {
                if (!TryAccumulateMockWalletAddition(additions, item.sku, item.amount, amount))
                    return false;
            }

            foreach (var addition in additions)
            {
                if (!CanAddMockWalletInventoryItem(addition.Key, addition.Value))
                    return false;
            }

            foreach (var addition in additions)
            {
                AddMockWalletInventoryItem(addition.Key, addition.Value);
            }

            return true;
        }

        private bool GrantMockWalletCatalogItem(object parameters)
        {
            var itemSku = GetMockWalletInventorySku(parameters);
            var amount = GetMockWalletInventoryAmount(parameters);
            if (string.IsNullOrEmpty(itemSku) ||
                amount <= 0 ||
                !_walletCatalog.ContainsKey(itemSku))
                return false;

            if (!CanAddMockWalletInventoryItem(itemSku, amount))
                return false;

            AddMockWalletInventoryItem(itemSku, amount);
            return true;
        }

        private bool TryAccumulateMockWalletAddition(
            Dictionary<string, int> additions,
            string sku,
            int itemAmount,
            int multiplier)
        {
            if (string.IsNullOrEmpty(sku) || itemAmount <= 0 || multiplier <= 0)
                return false;

            try
            {
                var quantity = checked(itemAmount * multiplier);
                additions[sku] = additions.TryGetValue(sku, out var existing)
                    ? checked(existing + quantity)
                    : quantity;
                return true;
            }
            catch (OverflowException)
            {
                return false;
            }
        }

        private bool CanAddMockWalletInventoryItem(string sku, int amount)
        {
            if (string.IsNullOrEmpty(sku) || amount <= 0)
                return false;

            if (!_walletInventory.TryGetValue(sku, out var item))
                return true;

            try
            {
                return checked(item.amount + amount) > 0;
            }
            catch (OverflowException)
            {
                return false;
            }
        }

        private void AddMockWalletInventoryItem(string sku, int amount)
        {
            if (string.IsNullOrEmpty(sku) || amount <= 0)
                return;

            if (_walletInventory.TryGetValue(sku, out var item))
            {
                item.amount += amount;
                return;
            }

            _walletInventory[sku] = new WalletInventoryItem { sku = sku, amount = amount };
        }

        private string GetMockWalletInventorySku(object parameters)
        {
            return parameters is object[] values && values.Length > 0 ? values[0]?.ToString() : null;
        }

        private int GetMockWalletInventoryAmount(object parameters)
        {
            if (parameters is object[] values &&
                values.Length > 1 &&
                values[1] != null &&
                int.TryParse(values[1].ToString(), out var amount))
                return amount;

            return 1;
        }

        private string GetMockTournamentsJson()
        {
            var tournaments = new Tournament[]
            {
                new Tournament
                {
                    id = "tournament_1",
                    title = "Daily Challenge",
                    description = "Complete daily challenges to earn rewards",
                    duration = 86400,
                    category = 1,
                    sortOrder = 1,
                    size = 150,
                    maxSize = 1000,
                    maxNumScore = 3,
                    canEnter = true,
                    nextReset = DateTimeOffset.UtcNow.AddHours(24).ToUnixTimeSeconds(),
                    createTime = DateTimeOffset.UtcNow.AddDays(-1).ToUnixTimeSeconds(),
                    startTime = DateTimeOffset.UtcNow.AddDays(-1).ToUnixTimeSeconds(),
                    endTime = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds(),
                    startActive = DateTimeOffset.UtcNow.AddDays(-1).ToUnixTimeSeconds(),
                    endActive = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
                },
                new Tournament
                {
                    id = "tournament_2",
                    title = "Weekly Championship",
                    description = "Compete against the best players",
                    duration = 604800,
                    category = 2,
                    sortOrder = 1,
                    size = 75,
                    maxSize = 500,
                    maxNumScore = 5,
                    canEnter = true,
                    nextReset = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds(),
                    createTime = DateTimeOffset.UtcNow.AddDays(-7).ToUnixTimeSeconds(),
                    startTime = DateTimeOffset.UtcNow.AddDays(-7).ToUnixTimeSeconds(),
                    endTime = DateTimeOffset.UtcNow.AddDays(14).ToUnixTimeSeconds(),
                    startActive = DateTimeOffset.UtcNow.AddDays(-7).ToUnixTimeSeconds(),
                    endActive = DateTimeOffset.UtcNow.AddDays(14).ToUnixTimeSeconds()
                }
            };

            var wrapper = new TournamentArrayWrapper { tournaments = tournaments };
            return JsonUtility.ToJson(wrapper);
        }

        private string GetMockTournamentEntriesJson()
        {
            var entries = new TournamentEntry[]
            {
                new TournamentEntry
                {
                    ownerId = "user_1",
                    rank = 1,
                    score = 1000,
                    subscore = 50,
                    username = "Player1",
                    numScore = 1,
                    maxNumScore = 3
                },
                new TournamentEntry
                {
                    ownerId = "user_2",
                    rank = 2,
                    score = 900,
                    subscore = 45,
                    username = "Player2",
                    numScore = 1,
                    maxNumScore = 3
                },
                new TournamentEntry
                {
                    ownerId = "current_user",
                    rank = 3,
                    score = 750,
                    subscore = 35,
                    username = "CurrentPlayer",
                    numScore = 1,
                    maxNumScore = 3
                }
            };

            var wrapper = new TournamentEntryArrayWrapper { entries = entries };
            return JsonUtility.ToJson(wrapper);
        }

        private string GetMockUserEntryJson()
        {
            var entry = new TournamentEntry
            {
                ownerId = "current_user",
                rank = 3,
                score = 750,
                subscore = 35,
                username = "CurrentPlayer",
                numScore = 1,
                maxNumScore = 3
            };

            return JsonUtility.ToJson(entry);
        }
    }
}
