using System;
using System.Collections;
using System.Globalization;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

namespace MajacloudSdk.Samples.Starter
{
    /// <summary>
    /// Testing helper: creates extra guest players with random scores so a
    /// leaderboard has something to show. It calls the REST API with its own
    /// tokens, so your own session is left alone. Each one is a real player and
    /// counts toward the plan's player limit. Not meant for a shipped game.
    /// </summary>
    public static class TestPlayers
    {
        [Serializable]
        private class SessionBody
        {
            public string access_token;
        }

        [Serializable]
        private class ErrorBody
        {
            public ErrorInfo error;
        }

        [Serializable]
        private class ErrorInfo
        {
            public string message;
        }

        /// <summary>Run with StartCoroutine. <paramref name="report"/> is called once per player (or once with the first error).</summary>
        public static IEnumerator Create(string slug, int count, Action<bool, string> report)
        {
            var settings = Majacloud.Settings;
            if (settings == null)
            {
                report(false, "Majacloud is not set up.");
                yield break;
            }
            for (int i = 0; i < count; i++)
            {
                var name = "Test player " + UnityEngine.Random.Range(1000, 10000);
                var score = UnityEngine.Random.Range(10, 1000);
                string token = null, error = null;
                var deviceId = "test-" + Guid.NewGuid().ToString("N");
                yield return Send(settings, "POST", "/v1/auth/guest", "{\"device_id\":\"" + deviceId + "\"}", null, body => token = Parse<SessionBody>(body)?.access_token, e => error = e);
                if (error == null && string.IsNullOrEmpty(token)) error = "The server returned no access token.";
                if (error == null) yield return Send(settings, "PATCH", "/v1/players/me", "{\"display_name\":\"" + name + "\"}", token, null, e => error = e);
                if (error == null) yield return Send(settings, "POST", "/v1/leaderboards/" + UnityWebRequest.EscapeURL(slug) + "/scores", "{\"score\":" + score.ToString(CultureInfo.InvariantCulture) + "}", token, null, e => error = e);
                if (error != null)
                {
                    report(false, "Test player " + (i + 1) + " of " + count + ": " + error);
                    yield break;
                }
                report(true, name + " scored " + score + " on " + slug);
            }
        }

        private static IEnumerator Send(MajacloudSettings settings, string method, string path, string json, string token, Action<string> onSuccess, Action<string> onError)
        {
            using (var request = new UnityWebRequest(settings.BaseUrl + path, method))
            {
                request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json));
                request.downloadHandler = new DownloadHandlerBuffer();
                request.timeout = settings.RequestTimeoutSeconds;
                request.SetRequestHeader("Content-Type", "application/json");
                request.SetRequestHeader("Accept", "application/json");
                request.SetRequestHeader("X-Project-ID", settings.ProjectId);
                request.SetRequestHeader("X-Client-Key", settings.ClientKey);
                request.SetRequestHeader("X-Majacloud-SDK", "unity-sample/" + Majacloud.Version);
                if (token != null) request.SetRequestHeader("Authorization", "Bearer " + token);
                yield return request.SendWebRequest();
                var body = request.downloadHandler != null ? request.downloadHandler.text : string.Empty;
                if (request.result == UnityWebRequest.Result.Success)
                {
                    onSuccess?.Invoke(body);
                    yield break;
                }
                var message = Parse<ErrorBody>(body)?.error?.message;
                onError(string.IsNullOrEmpty(message) ? request.error ?? "Request failed" : message);
            }
        }

        private static T Parse<T>(string json) where T : class
        {
            if (string.IsNullOrEmpty(json)) return null;
            try
            {
                return JsonUtility.FromJson<T>(json);
            }
            catch (ArgumentException)
            {
                return null;
            }
        }
    }
}
