using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MajacloudSdk.Internal;

namespace MajacloudSdk
{
    /// <summary>Server-authoritative rewards and wallet balances. Nothing is computed on the client.</summary>
    public sealed class MajacloudRewards
    {
        private readonly MajacloudHttp _http;

        internal MajacloudRewards(MajacloudHttp http)
        {
            _http = http;
        }

        public async Task<MajacloudResult<IReadOnlyList<MajacloudRewardInfo>>> ListAsync(CancellationToken cancellationToken = default)
        {
            var result = await _http.SendJsonAsync(new HttpRequest { Method = "GET", Path = MajacloudEndpoints.Rewards, Auth = HttpAuth.BearerIfAvailable, Idempotent = true }, cancellationToken);
            if (!result.Ok) return MajacloudResult<IReadOnlyList<MajacloudRewardInfo>>.Fail(result.Error);
            var list = new List<MajacloudRewardInfo>();
            foreach (var node in result.Value.GetArray("rewards").Elements()) list.Add(MajacloudRewardInfo.FromJson(node));
            return MajacloudResult<IReadOnlyList<MajacloudRewardInfo>>.Success(list);
        }

        /// <summary>Claim a reward. On cooldown the result is a Conflict error with <c>Error.GetDetailDateTime("next_claim_at")</c>.</summary>
        public async Task<MajacloudResult<MajacloudRewardClaim>> ClaimAsync(string rewardKey, CancellationToken cancellationToken = default)
        {
            if (!MajacloudEndpoints.IsValidIdentifier(rewardKey)) return MajacloudResult<MajacloudRewardClaim>.Fail(MajacloudErrorCode.Validation, "Invalid reward key.");
            var result = await _http.SendJsonAsync(new HttpRequest { Method = "POST", Path = MajacloudEndpoints.RewardsClaim, Auth = HttpAuth.Bearer, JsonBody = JsonNode.Object().Set("reward_key", rewardKey).ToJson(), Idempotent = false }, cancellationToken);
            if (!result.Ok) return MajacloudResult<MajacloudRewardClaim>.Fail(result.Error);
            var json = result.Value;
            return MajacloudResult<MajacloudRewardClaim>.Success(new MajacloudRewardClaim
            {
                Accepted = json.GetBool("accepted", true),
                RewardKey = json.GetString("reward_key") ?? rewardKey,
                Amount = json.GetDouble("amount"),
                Currency = json.GetString("currency") ?? "coins",
                Balance = json.GetDouble("balance"),
                NextClaimAt = json.GetDateTime("next_claim_at"),
            });
        }

        public async Task<MajacloudResult<MajacloudWallet>> GetWalletAsync(CancellationToken cancellationToken = default)
        {
            var result = await _http.SendJsonAsync(new HttpRequest { Method = "GET", Path = MajacloudEndpoints.Wallet, Auth = HttpAuth.Bearer, Idempotent = true }, cancellationToken);
            if (!result.Ok) return MajacloudResult<MajacloudWallet>.Fail(result.Error);
            var balances = new Dictionary<string, double>();
            foreach (var node in result.Value.GetArray("wallets").Elements())
            {
                var currency = node.GetString("currency");
                if (!string.IsNullOrEmpty(currency)) balances[currency] = node.GetDouble("balance");
            }
            return MajacloudResult<MajacloudWallet>.Success(new MajacloudWallet { Balances = balances });
        }

        // --- Coroutine variants -------------------------------------------------------------------

        public IEnumerator List(Action<MajacloudResult<IReadOnlyList<MajacloudRewardInfo>>> callback) => MajacloudCoroutine.Run(ListAsync(), callback);
        public IEnumerator Claim(string rewardKey, Action<MajacloudResult<MajacloudRewardClaim>> callback) => MajacloudCoroutine.Run(ClaimAsync(rewardKey), callback);
        public IEnumerator GetWallet(Action<MajacloudResult<MajacloudWallet>> callback) => MajacloudCoroutine.Run(GetWalletAsync(), callback);
    }
}
