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

namespace MajacloudSdk
{
    public enum MajacloudAuthType { Guest, Email, Google, Linked, Unknown }

    public sealed class MajacloudPlayer
    {
        public string Id { get; internal set; }
        public string PlayerId { get; internal set; }
        public string DisplayName { get; internal set; }
        public string Username { get; internal set; }
        public string EmailMasked { get; internal set; }
        public bool EmailVerified { get; internal set; }
        public MajacloudAuthType AuthType { get; internal set; }
        public string RawAuthType { get; internal set; }
        public bool HasPassword { get; internal set; }
        public bool HasGoogle { get; internal set; }
        public string AvatarUrl { get; internal set; }
        public bool IsGuest => AuthType == MajacloudAuthType.Guest;

        internal static MajacloudPlayer FromJson(JsonNode node)
        {
            if (node == null || !node.IsObject) return null;
            var raw = node.GetString("auth_type") ?? "guest";
            MajacloudAuthType type;
            switch (raw)
            {
                case "guest": type = MajacloudAuthType.Guest; break;
                case "email": type = MajacloudAuthType.Email; break;
                case "google": type = MajacloudAuthType.Google; break;
                case "linked": type = MajacloudAuthType.Linked; break;
                default: type = MajacloudAuthType.Unknown; break;
            }
            return new MajacloudPlayer
            {
                Id = node.GetString("id"),
                PlayerId = node.GetString("player_id"),
                DisplayName = node.GetString("display_name") ?? "Player",
                Username = node.GetString("username"),
                EmailMasked = node.GetString("email_masked"),
                EmailVerified = node.GetBool("email_verified"),
                AuthType = type,
                RawAuthType = raw,
                HasPassword = node.GetBool("has_password"),
                HasGoogle = node.GetBool("has_google"),
                AvatarUrl = node.GetString("avatar_url"),
            };
        }

        public override string ToString() => DisplayName + " (" + PlayerId + ", " + RawAuthType + ")";
    }

    public sealed class MajacloudProjectInfo
    {
        public string Id { get; internal set; }
        public string Name { get; internal set; }
        public string Plan { get; internal set; }
        public string PlanLabel { get; internal set; }
        public long ConfigVersion { get; internal set; }
        public string ConfigPublicKeyXml { get; internal set; }
        public string ConfigSigningKeyId { get; internal set; }
        public IReadOnlyList<string> Services { get; internal set; }

        internal static MajacloudProjectInfo FromJson(JsonNode node)
        {
            var services = new List<string>();
            foreach (var field in node.GetObject("services").Fields ?? new List<KeyValuePair<string, JsonNode>>())
            {
                if (field.Value != null && field.Value.Type == JsonType.Bool && field.Value.BoolValue) services.Add(field.Key);
            }
            return new MajacloudProjectInfo
            {
                Id = node.GetString("id"),
                Name = node.GetString("name"),
                Plan = node.GetString("plan"),
                PlanLabel = node.GetString("plan_label"),
                ConfigVersion = node.GetLong("config_version"),
                ConfigPublicKeyXml = node.GetString("config_public_key_xml"),
                ConfigSigningKeyId = node.GetString("config_signing_key_id"),
                Services = services,
            };
        }
    }

    // --- Leaderboards ------------------------------------------------------------

    public enum MajacloudLeaderboardPeriod { Current, Previous }

    public sealed class MajacloudPeriodInfo
    {
        public string Key { get; internal set; }
        public DateTime? EndsAt { get; internal set; }

        internal static MajacloudPeriodInfo FromJson(JsonNode node)
        {
            if (node == null || !node.IsObject) return null;
            return new MajacloudPeriodInfo { Key = node.GetString("key"), EndsAt = node.GetDateTime("ends_at") ?? node.GetDateTime("ended_at") };
        }
    }

    public sealed class MajacloudLeaderboardInfo
    {
        public string Slug { get; internal set; }
        public string Name { get; internal set; }
        public string SortType { get; internal set; }
        public string ScoreMode { get; internal set; }
        public string ResetRule { get; internal set; }

        internal static MajacloudLeaderboardInfo FromJson(JsonNode node)
        {
            return new MajacloudLeaderboardInfo
            {
                Slug = node.GetString("slug"),
                Name = node.GetString("name"),
                SortType = node.GetString("sort_type"),
                ScoreMode = node.GetString("score_mode"),
                ResetRule = node.GetString("reset_rule"),
            };
        }
    }

    public sealed class MajacloudLeaderboardEntry
    {
        public int? Rank { get; internal set; }
        public string PlayerId { get; internal set; }
        public string PlayerName { get; internal set; }
        public double Score { get; internal set; }
        public DateTime? UpdatedAt { get; internal set; }
        public bool IsMe { get; internal set; }

        internal static MajacloudLeaderboardEntry FromJson(JsonNode node)
        {
            if (node == null || !node.IsObject) return null;
            return new MajacloudLeaderboardEntry
            {
                Rank = node.GetNullableInt("rank"),
                PlayerId = node.GetString("player_id"),
                PlayerName = node.GetString("player_name"),
                Score = node.GetDouble("score"),
                UpdatedAt = node.GetDateTime("score_updated_at"),
                IsMe = node.GetBool("is_me"),
            };
        }
    }

    public sealed class MajacloudLeaderboardPage
    {
        public MajacloudLeaderboardInfo Leaderboard { get; internal set; }
        public MajacloudPeriodInfo Period { get; internal set; }
        public IReadOnlyList<MajacloudLeaderboardEntry> Entries { get; internal set; }
        public int Total { get; internal set; }
        public int Limit { get; internal set; }
        public int Offset { get; internal set; }
        public int? MaxEntries { get; internal set; }
        public bool HasMore => Offset + Entries.Count < Total;
    }

    public sealed class MajacloudScoreSubmission
    {
        public bool Accepted { get; internal set; }
        public MajacloudLeaderboardEntry Entry { get; internal set; }
        public MajacloudPeriodInfo Period { get; internal set; }
    }

    public sealed class MajacloudRankInfo
    {
        public MajacloudLeaderboardEntry Entry { get; internal set; }
        public int Total { get; internal set; }
        public MajacloudPeriodInfo Period { get; internal set; }
        public bool IsRanked => Entry != null && Entry.Rank.HasValue;
        public int? Rank => Entry?.Rank;
        public double? Score => Entry?.Score;
    }

    public sealed class MajacloudAroundMe
    {
        public IReadOnlyList<MajacloudLeaderboardEntry> Entries { get; internal set; }
        public MajacloudLeaderboardEntry Me { get; internal set; }
        public int Total { get; internal set; }
        public MajacloudPeriodInfo Period { get; internal set; }
    }

    // --- Remote config -------------------------------------------------------------

    public enum MajacloudConfigType { String, Number, Boolean, Json }
    public enum MajacloudConfigSource { None, Cache, CacheValidated, Network }

    public sealed class MajacloudConfigValue
    {
        public string Key { get; internal set; }
        public MajacloudConfigType Type { get; internal set; }
        /// <summary>The raw value as served (strings as-is, numbers/booleans/JSON as text).</summary>
        public string Raw { get; internal set; }

        public string AsString() => Raw ?? string.Empty;

        public double? AsNumber()
        {
            if (Raw == null) return null;
            if (double.TryParse(Raw, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var value)) return value;
            if (Type == MajacloudConfigType.Boolean) return AsBool() == true ? 1 : 0;
            return null;
        }

        public bool? AsBool()
        {
            if (Raw == null) return null;
            var s = Raw.Trim().ToLowerInvariant();
            if (s == "true" || s == "1" || s == "yes" || s == "on") return true;
            if (s == "false" || s == "0" || s == "no" || s == "off" || s == "") return false;
            if (double.TryParse(s, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var n)) return Math.Abs(n) > double.Epsilon;
            return null;
        }

        public T As<T>(T fallback = default)
        {
            if (Raw == null) return fallback;
            try
            {
                return Majacloud.JsonSerializer.FromJson<T>(Raw);
            }
            catch (Exception)
            {
                return fallback;
            }
        }
    }

    public sealed class MajacloudConfigFetchResult
    {
        public long Version { get; internal set; }
        public MajacloudConfigSource Source { get; internal set; }
        public bool Changed { get; internal set; }
        public bool SignatureVerified { get; internal set; }
        public int Count { get; internal set; }
    }

    // --- Cloud save ----------------------------------------------------------------------

    public sealed class MajacloudSaveReceipt
    {
        public string Key { get; internal set; }
        public long Revision { get; internal set; }
        public long SizeBytes { get; internal set; }
        public DateTime? UpdatedAt { get; internal set; }
        public bool Created { get; internal set; }
    }

    public sealed class MajacloudCloudSaveItem<T>
    {
        public string Key { get; internal set; }
        public T Data { get; internal set; }
        public string Json { get; internal set; }
        public long Revision { get; internal set; }
        public DateTime? UpdatedAt { get; internal set; }
    }

    public sealed class MajacloudCloudSaveEntry
    {
        public string Key { get; internal set; }
        public long Revision { get; internal set; }
        public long SizeBytes { get; internal set; }
        public DateTime? UpdatedAt { get; internal set; }
    }

    // --- Rewards -------------------------------------------------------------------------

    public sealed class MajacloudRewardInfo
    {
        public string RewardKey { get; internal set; }
        public string Name { get; internal set; }
        public double Amount { get; internal set; }
        public string Currency { get; internal set; }
        public string Cooldown { get; internal set; }
        public bool? Claimable { get; internal set; }
        public DateTime? NextClaimAt { get; internal set; }

        internal static MajacloudRewardInfo FromJson(JsonNode node)
        {
            return new MajacloudRewardInfo
            {
                RewardKey = node.GetString("reward_key"),
                Name = node.GetString("name"),
                Amount = node.GetDouble("amount"),
                Currency = node.GetString("currency") ?? "coins",
                Cooldown = node.GetString("cooldown"),
                Claimable = node.Has("claimable") ? node.GetBool("claimable") : (bool?)null,
                NextClaimAt = node.GetDateTime("next_claim_at"),
            };
        }
    }

    public sealed class MajacloudRewardClaim
    {
        public bool Accepted { get; internal set; }
        public string RewardKey { get; internal set; }
        public double Amount { get; internal set; }
        public string Currency { get; internal set; }
        public double Balance { get; internal set; }
        public DateTime? NextClaimAt { get; internal set; }
    }

    public sealed class MajacloudWallet
    {
        public IReadOnlyDictionary<string, double> Balances { get; internal set; }
        public double Get(string currency, double fallback = 0) => Balances != null && Balances.TryGetValue(currency, out var value) ? value : fallback;
        public double Coins => Get("coins");
    }
}
