using System;
using System.Threading.Tasks;
using UnityEngine;
using Arkadium.Interop;

namespace Arkadium
{
    [Serializable]
    public class Tournament
    {
        public string id;
        public string title;
        public string description;
        public int duration;
        public int category;
        public int sortOrder;
        public int size;
        public int maxSize;
        public int maxNumScore;
        public bool canEnter;
        public long nextReset;
        public object metadata;
        public long createTime;
        public long startTime;
        public long endTime;
        public long startActive;
        public long endActive;
    }

    [Serializable]
    public class TournamentEntry
    {
        public string ownerId;
        public int rank;
        public int score;
        public int subscore;
        public string username;
        public int numScore;
        public int maxNumScore;
        public object metadata;
    }

    [Serializable]
    public class TournamentArrayWrapper
    {
        public Tournament[] tournaments;
    }

    [Serializable]
    public class TournamentEntryArrayWrapper
    {
        public TournamentEntry[] entries;
    }

    [Serializable]
    public class SubmitTournamentScoreParams
    {
        public string tournamentId;
        public int score;
        public int subscore;
        public object metadata;
    }

    public interface IArkadiumTournaments
    {
        Task<Tournament[]> GetTournamentsAsync();
        Task<bool> HasJoinedTournamentAsync(string tournamentId);
        Task<bool> JoinTournamentAsync(string tournamentId);
        Task<bool> CanSubmitScoreToTournamentAsync(string tournamentId);
        Task<bool> SubmitTournamentScoreAsync(SubmitTournamentScoreParams parameters);
        Task<TournamentEntry[]> GetTopTournamentEntriesAsync(string tournamentId, int top);
        Task<TournamentEntry> GetTournamentUserEntryAsync(string tournamentId);
        Task<TournamentEntry[]> GetTournamentUsersEntriesAsync(string tournamentId, string[] userIds);
        Task<TournamentEntry[]> GetTournamentEntriesAroundUserAsync(string tournamentId, int amount = 10);

        // Legacy callback-based methods for backward compatibility
        void GetTournaments(Action<Tournament[]> cb);
        void HasJoinedTournament(string tournamentId, Action<bool> cb);
        void JoinTournament(string tournamentId, Action<bool> cb);
        void CanSubmitScoreToTournament(string tournamentId, Action<bool> cb);
        void SubmitTournamentScore(SubmitTournamentScoreParams parameters, Action<bool> cb);
        void GetTopTournamentEntries(string tournamentId, int top, Action<TournamentEntry[]> cb);
        void GetTournamentUserEntry(string tournamentId, Action<TournamentEntry> cb);
        void GetTournamentUsersEntries(string tournamentId, string[] userIds, Action<TournamentEntry[]> cb);
        void GetTournamentEntriesAroundUser(string tournamentId, int amount, Action<TournamentEntry[]> cb);
    }

    public class ArkadiumTournaments : IArkadiumTournaments
    {
        /// <summary>
        /// Get all available tournaments
        /// </summary>
        public async Task<Tournament[]> GetTournamentsAsync()
        {
            try
            {
                var result = await WebGLInterop.InvokeAsync<string>("tournaments.getTournaments");
                if (string.IsNullOrEmpty(result) || result == "[]")
                    return new Tournament[0];

                var wrapper = JsonUtility.FromJson<TournamentArrayWrapper>(result);
                return wrapper?.tournaments ?? new Tournament[0];
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error getting tournaments: {ex.Message}");
                return new Tournament[0];
            }
        }

        /// <summary>
        /// Check if the current user has joined a specific tournament
        /// </summary>
        public async Task<bool> HasJoinedTournamentAsync(string tournamentId)
        {
            try
            {
                var result = await WebGLInterop.InvokeAsync<bool>("tournaments.hasJoinedTournament", new { tournamentId });
                return result;
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error checking if joined tournament: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Join a tournament
        /// </summary>
        public async Task<bool> JoinTournamentAsync(string tournamentId)
        {
            try
            {
                var result = await WebGLInterop.InvokeAsync<bool>("tournaments.joinTournament", new { tournamentId });
                return result;
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error joining tournament: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Check if the user can submit a score to a tournament
        /// </summary>
        public async Task<bool> CanSubmitScoreToTournamentAsync(string tournamentId)
        {
            try
            {
                var result = await WebGLInterop.InvokeAsync<bool>("tournaments.canSubmitScoreToTournament", new { tournamentId });
                return result;
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error checking if can submit score: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Submit a score to a tournament
        /// </summary>
        public async Task<bool> SubmitTournamentScoreAsync(SubmitTournamentScoreParams parameters)
        {
            try
            {
                var result = await WebGLInterop.InvokeAsync<bool>("tournaments.submitTournamentScore", parameters);
                return result;
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error submitting tournament score: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Get the top entries for a tournament
        /// </summary>
        public async Task<TournamentEntry[]> GetTopTournamentEntriesAsync(string tournamentId, int top)
        {
            try
            {
                var parameters = new { tournamentId, top };
                var result = await WebGLInterop.InvokeAsync<string>("tournaments.getTopTournamentEntries", parameters);
                if (string.IsNullOrEmpty(result) || result == "[]")
                    return new TournamentEntry[0];

                var wrapper = JsonUtility.FromJson<TournamentEntryArrayWrapper>(result);
                return wrapper?.entries ?? new TournamentEntry[0];
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error getting top tournament entries: {ex.Message}");
                return new TournamentEntry[0];
            }
        }

        /// <summary>
        /// Get the current user's entry for a tournament
        /// </summary>
        public async Task<TournamentEntry> GetTournamentUserEntryAsync(string tournamentId)
        {
            try
            {
                var result = await WebGLInterop.InvokeAsync<string>("tournaments.getTournamentUserEntry", new { tournamentId });
                if (string.IsNullOrEmpty(result) || result == "null")
                    return null;

                return JsonUtility.FromJson<TournamentEntry>(result);
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error getting tournament user entry: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// Get tournament entries for specific users
        /// </summary>
        public async Task<TournamentEntry[]> GetTournamentUsersEntriesAsync(string tournamentId, string[] userIds)
        {
            try
            {
                var parameters = new { tournamentId, userIds };
                var result = await WebGLInterop.InvokeAsync<string>("tournaments.getTournamentUsersEntries", parameters);
                if (string.IsNullOrEmpty(result) || result == "[]")
                    return new TournamentEntry[0];

                var wrapper = JsonUtility.FromJson<TournamentEntryArrayWrapper>(result);
                return wrapper?.entries ?? new TournamentEntry[0];
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error getting tournament users entries: {ex.Message}");
                return new TournamentEntry[0];
            }
        }

        /// <summary>
        /// Get tournament entries around the current user
        /// </summary>
        public async Task<TournamentEntry[]> GetTournamentEntriesAroundUserAsync(string tournamentId, int amount = 10)
        {
            try
            {
                var parameters = new { tournamentId, amount };
                var result = await WebGLInterop.InvokeAsync<string>("tournaments.getTournamentEntriesAroundUser", parameters);
                if (string.IsNullOrEmpty(result) || result == "[]")
                    return new TournamentEntry[0];

                var wrapper = JsonUtility.FromJson<TournamentEntryArrayWrapper>(result);
                return wrapper?.entries ?? new TournamentEntry[0];
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error getting tournament entries around user: {ex.Message}");
                return new TournamentEntry[0];
            }
        }

        // Legacy callback-based methods for backward compatibility
        public void GetTournaments(Action<Tournament[]> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await GetTournamentsAsync();
                cb?.Invoke(result);
            });
        }

        public void HasJoinedTournament(string tournamentId, Action<bool> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await HasJoinedTournamentAsync(tournamentId);
                cb?.Invoke(result);
            });
        }

        public void JoinTournament(string tournamentId, Action<bool> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await JoinTournamentAsync(tournamentId);
                cb?.Invoke(result);
            });
        }

        public void CanSubmitScoreToTournament(string tournamentId, Action<bool> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await CanSubmitScoreToTournamentAsync(tournamentId);
                cb?.Invoke(result);
            });
        }

        public void SubmitTournamentScore(SubmitTournamentScoreParams parameters, Action<bool> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await SubmitTournamentScoreAsync(parameters);
                cb?.Invoke(result);
            });
        }

        public void GetTopTournamentEntries(string tournamentId, int top, Action<TournamentEntry[]> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await GetTopTournamentEntriesAsync(tournamentId, top);
                cb?.Invoke(result);
            });
        }

        public void GetTournamentUserEntry(string tournamentId, Action<TournamentEntry> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await GetTournamentUserEntryAsync(tournamentId);
                cb?.Invoke(result);
            });
        }

        public void GetTournamentUsersEntries(string tournamentId, string[] userIds, Action<TournamentEntry[]> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await GetTournamentUsersEntriesAsync(tournamentId, userIds);
                cb?.Invoke(result);
            });
        }

        public void GetTournamentEntriesAroundUser(string tournamentId, int amount, Action<TournamentEntry[]> cb)
        {
            _ = InvokeAsyncCallback(async () =>
            {
                var result = await GetTournamentEntriesAroundUserAsync(tournamentId, amount);
                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}");
            }
        }
    }
}