using System;
using System.Collections;
using System.Threading.Tasks;
using MajacloudSdk.Internal;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace MajacloudSdk.Tests
{
    /// <summary>
    /// End-to-end against a real backend. Set MAJACLOUD_TEST_BASE_URL,
    /// MAJACLOUD_TEST_PROJECT_ID and MAJACLOUD_TEST_CLIENT_KEY to run it;
    /// otherwise the test is ignored.
    /// </summary>
    public class SmokeTests
    {
        [Serializable]
        private class Progress
        {
            public int level;
            public string nickname;
        }

        [UnityTest]
        [Category("Smoke")]
        [Timeout(90000)]
        public IEnumerator GoldenPath()
        {
            var baseUrl = Environment.GetEnvironmentVariable("MAJACLOUD_TEST_BASE_URL");
            var projectId = Environment.GetEnvironmentVariable("MAJACLOUD_TEST_PROJECT_ID");
            var clientKey = Environment.GetEnvironmentVariable("MAJACLOUD_TEST_CLIENT_KEY");
            if (string.IsNullOrEmpty(baseUrl) || string.IsNullOrEmpty(projectId) || string.IsNullOrEmpty(clientKey))
            {
                Assert.Ignore("Set MAJACLOUD_TEST_BASE_URL / PROJECT_ID / CLIENT_KEY to run the smoke test.");
                yield break;
            }
            var settings = ScriptableObject.CreateInstance<MajacloudSettings>();
            settings.Configure(baseUrl, projectId, clientKey);
            Majacloud.Shutdown();
            Assert.IsTrue(Majacloud.InitializeInternal(settings, new MemoryStorage()));
            Majacloud.Log.Level = MajacloudLogLevel.Verbose;
            Majacloud.Log.LogRequests = true;

            var project = Majacloud.Project.GetAsync();
            yield return MajacloudCoroutine.Wait(project);
            Assert.IsTrue(project.Result.Ok, project.Result.Error?.ToString());
            settings.SetProjectInfo(project.Result.Value.Name, project.Result.Value.Plan, project.Result.Value.ConfigPublicKeyXml, project.Result.Value.ConfigSigningKeyId);

            var signIn = Majacloud.Auth.SignInAnonymouslyAsync();
            yield return MajacloudCoroutine.Wait(signIn);
            Assert.IsTrue(signIn.Result.Ok, signIn.Result.Error?.ToString());
            Assert.IsTrue(Majacloud.Auth.IsSignedIn);

            var score = Majacloud.Leaderboards.SubmitScoreAsync("coins", UnityEngine.Random.Range(1, 1000));
            yield return MajacloudCoroutine.Wait(score);
            Assert.IsTrue(score.Result.Ok, score.Result.Error?.ToString());

            var top = Majacloud.Leaderboards.GetTopAsync("coins", 10);
            yield return MajacloudCoroutine.Wait(top);
            Assert.IsTrue(top.Result.Ok, top.Result.Error?.ToString());
            Assert.IsTrue(top.Result.Value.Entries.Count > 0);

            var rank = Majacloud.Leaderboards.GetMyRankAsync("coins");
            yield return MajacloudCoroutine.Wait(rank);
            Assert.IsTrue(rank.Result.Ok && rank.Result.Value.IsRanked, rank.Result.Error?.ToString());

            var config = Majacloud.RemoteConfig.FetchAsync(true);
            yield return MajacloudCoroutine.Wait(config);
            Assert.IsTrue(config.Result.Ok, config.Result.Error?.ToString());
            Assert.IsTrue(Majacloud.RemoteConfig.IsSignatureVerified, "Remote Config must be RSA-verified against the project public key.");

            var key = "smoke_" + Guid.NewGuid().ToString("N").Substring(0, 8);
            var save = Majacloud.CloudSave.SaveAsync(key, new Progress { level = 3, nickname = "Nova" });
            yield return MajacloudCoroutine.Wait(save);
            Assert.IsTrue(save.Result.Ok, save.Result.Error?.ToString());
            Assert.AreEqual(1, save.Result.Value.Revision);

            var load = Majacloud.CloudSave.LoadAsync<Progress>(key);
            yield return MajacloudCoroutine.Wait(load);
            Assert.IsTrue(load.Result.Ok && load.Result.Value.Data.level == 3, load.Result.Error?.ToString());

            var stale = Majacloud.CloudSave.SaveAsync(key, new Progress { level = 4 }, 0);
            yield return MajacloudCoroutine.Wait(stale);
            Assert.IsFalse(stale.Result.Ok);
            Assert.AreEqual(MajacloudErrorCode.RevisionMismatch, stale.Result.Error.Code);

            var delete = Majacloud.CloudSave.DeleteAsync(key);
            yield return MajacloudCoroutine.Wait(delete);
            Assert.IsTrue(delete.Result.Ok && delete.Result.Value);

            var claim = Majacloud.Rewards.ClaimAsync("daily_coins");
            yield return MajacloudCoroutine.Wait(claim);
            Assert.IsTrue(claim.Result.Ok || claim.Result.Error.Code == MajacloudErrorCode.Conflict || claim.Result.Error.Code == MajacloudErrorCode.NotFound, claim.Result.Error?.ToString());

            var wallet = Majacloud.Rewards.GetWalletAsync();
            yield return MajacloudCoroutine.Wait(wallet);
            Assert.IsTrue(wallet.Result.Ok, wallet.Result.Error?.ToString());

            var signOut = Majacloud.Auth.SignOutAsync();
            yield return MajacloudCoroutine.Wait(signOut);
            Assert.IsFalse(Majacloud.Auth.IsSignedIn);
            Majacloud.Shutdown();
        }
    }
}
