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

namespace NerdRank
{
    public class NerdRankClient
    {
        private const string ApiKeyHeader = "X-API-Key";

        private readonly string baseUrl;
        private readonly string apiKey;

        public NerdRankClient(NerdRankSettings settings)
            : this(settings.BaseUrl, settings.ApiKey)
        {
        }

        public NerdRankClient(string baseUrl, string apiKey)
        {
            this.baseUrl = NormalizeBaseUrl(baseUrl);
            this.apiKey = apiKey;
        }

        public IEnumerator GetLeaderboard(
            string leaderboardId,
            int entries,
            Action<NerdRankResult<LeaderboardResponse>> completed)
        {
            if (entries < 1)
            {
                completed?.Invoke(NerdRankResult<LeaderboardResponse>.Fail("Entries must be greater than zero.", 0));
                yield break;
            }

            var url = $"{baseUrl}/api/Leaderboards/{UnityWebRequest.EscapeURL(leaderboardId)}?entries={entries}";

            using var request = UnityWebRequest.Get(url);
            yield return request.SendWebRequest();

            completed?.Invoke(ReadJsonResponse<LeaderboardResponse>(request));
        }

        public IEnumerator AddLeaderboardEntry(
            string leaderboardId,
            string playerName,
            double score,
            string userId,
            Action<NerdRankResult<LeaderboardEntryResponse>> completed)
        {
            var payload = JsonUtility.ToJson(new AddLeaderboardEntryRequest(playerName, score, userId));
            var url = $"{baseUrl}/api/Leaderboards/{UnityWebRequest.EscapeURL(leaderboardId)}/entries";

            using var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST);
            var body = Encoding.UTF8.GetBytes(payload);
            request.uploadHandler = new UploadHandlerRaw(body);
            request.downloadHandler = new DownloadHandlerBuffer();
            request.SetRequestHeader("Content-Type", "application/json");
            request.SetRequestHeader(ApiKeyHeader, apiKey);

            yield return request.SendWebRequest();

            completed?.Invoke(ReadJsonResponse<LeaderboardEntryResponse>(request));
        }

        private static NerdRankResult<T> ReadJsonResponse<T>(UnityWebRequest request)
        {
            if (request.result != UnityWebRequest.Result.Success)
            {
                var message = string.IsNullOrWhiteSpace(request.downloadHandler.text)
                    ? request.error
                    : request.downloadHandler.text;

                return NerdRankResult<T>.Fail(message, request.responseCode);
            }

            try
            {
                var value = JsonUtility.FromJson<T>(request.downloadHandler.text);
                return NerdRankResult<T>.Ok(value, request.responseCode);
            }
            catch (Exception exception)
            {
                return NerdRankResult<T>.Fail(exception.Message, request.responseCode);
            }
        }

        private static string NormalizeBaseUrl(string url)
        {
            return string.IsNullOrWhiteSpace(url)
                ? string.Empty
                : url.Trim().TrimEnd('/');
        }
    }
}
