using Newtonsoft.Json;
using System;
using System.Dynamic;
using System.Net;
using System.Security.Cryptography;
using System.Text;

namespace PlayFab
{

    public class PlayFab
    {

        public static string PLAYFAB_VERSION = "XPlatCppSdk-3.6.190304";
        public static string CPP_REST_VERSION = "cpprestsdk/2.9.0";

        public static string TITLE_ID = "20CA2";
        public static string TITLE_SECRET = "S8RS53ZEIGMYTYG856U3U19AORWXQXF41J7FT3X9YCWAC7I35X";
        public static byte[] PUBLIC_KEY = null;


        public static string ENTITY_TOKEN = null;
        public static string ENTITY_ID = null;
        public static string ENTITY_TYPE = null;



        public static dynamic GetProductInformation(string uuid)
        {
            dynamic dyn = new ExpandoObject();
            dyn.ItemId = uuid;
            dyn.ETag = "";

            return POST(dyn, "/Catalog/GetPublishedItem");
        }
        public static dynamic Search(string query, string filter = "", int skip = 0, string orderBy= "creationDate DESC")
        {
            dynamic dyn = new ExpandoObject();
            dyn.count = true;
            dyn.filter = filter;
            dyn.search = query;
            dyn.orderBy = orderBy;
            dyn.top = 300;
            dyn.skip = skip;

            return POST(dyn, "/Catalog/Search");
        }

        public static dynamic POST(dynamic data, string url, string clientsecret = null, bool incSDK = false)
        {
            McClient wc = new McClient();
            string body = JsonConvert.SerializeObject(data, (incSDK ? Formatting.Indented : Formatting.None));

            // Pretend to be minecraft ...

            wc.Headers.Set("Accept", "application/json");
            wc.Headers.Set("Accept-Language", "en-US");
            wc.Headers.Set("Accept-Encoding", "gzip, deflate, br");
            wc.Headers.Set("User-Agent", CPP_REST_VERSION);
            wc.Headers.Set("x-playfabsdk", PLAYFAB_VERSION);
            wc.Headers.Set("x-reporterrorassuccess", "true");
            wc.Headers.Set("content-type", "application/json; charset=utf-8");

            if (clientsecret != null)
            {

                string signature;
                string timestamp = DateTime.UtcNow.ToString("O");

                using (SHA256 hash = SHA256.Create())
                {
                    string msg = body + "." + timestamp + "." + clientsecret;
                    byte[] bytesToHash = Encoding.UTF8.GetBytes(msg);
                    signature = Convert.ToBase64String(hash.ComputeHash(bytesToHash));
                }

                wc.Headers.Set("x-playfab-signature", signature);
                wc.Headers.Set("x-playfab-timestamp", timestamp);
            }
            if (ENTITY_TOKEN != null)
                wc.Headers.Set("x-entitytoken", ENTITY_TOKEN);
            string response = wc.UploadString(GetPlayfabApiUrl() + url + (incSDK ? SdkMsg() : ""), body);
            dynamic resp = JsonConvert.DeserializeObject(response);
            wc.Dispose();

            return resp.data;

        }
        public static string RefreshEntityTokenBullshit()
        {
            dynamic dyn = new ExpandoObject();
            dyn.Entity = new ExpandoObject();
            dyn.Entity.Id = ENTITY_ID;
            dyn.Entity.Type = ENTITY_TYPE;

            dynamic resp = POST(dyn, "/Authentication/GetEntityToken", null, true);
            ENTITY_TOKEN = resp.EntityToken.ToString();
            return ENTITY_TOKEN;
        }
        public static string SdkMsg()
        {
            return "?sdk=" + PLAYFAB_VERSION;
        }
        public static string GenerateClientSecret()
        {
            Random r = new Random(Guid.NewGuid().GetHashCode());
            byte[] clientSecret = new byte[0x20];
            r.NextBytes(clientSecret);
            return Convert.ToBase64String(clientSecret);
        }
        public static string GenerateCustomId()
        {
            Random r = new Random(Guid.NewGuid().GetHashCode());
            byte[] customId = new byte[0x10];
            r.NextBytes(customId);
            return "MCPF" + BitConverter.ToString(customId).Replace("-", "").ToUpper();
        }

        public static string GetPlayfabApiUrl()
        {
            return "https://" + TITLE_ID + ".playfabapi.com";
        }

        // How 2 find TitleSharedSecret:
        // 1- delete all mc data
        // 2- open fiddler
        // 3- open minecraft
        // 4- check for https://20ca2.playfabapi.com/Client/GetTitlePublicKey...
        public static void GetPublicKeyAndMicrosoftTakesABigL()
        {
            dynamic dyn = new ExpandoObject();
            dyn.TitleId = TITLE_ID;
            dyn.TitleSharedSecret = TITLE_SECRET;

            dynamic resp = POST(dyn, "/Client/GetTitlePublicKey", null, true);
            PUBLIC_KEY = Convert.FromBase64String(resp.RSAPublicKey.ToString());
        }

        public static string EncryptCustomIdLoginToken(string customId, string clientSecret)
        {
            dynamic dyn = new ExpandoObject();
            dyn.CustomId = customId;
            dyn.PlayerSecret = clientSecret;


            return EncryptRequest(JsonConvert.SerializeObject(dyn));
        }

        public static string LoginWithCustomId(bool newUser = true, string customId = null, string clientSecret = null)
        {
            dynamic dyn = new ExpandoObject();

            if (newUser)
                dyn.CreateAccount = true;
            else
                dyn.CreateAccount = null;
            dyn.CustomId = customId;

            if (clientSecret == null)
                clientSecret = GenerateClientSecret();

            if (customId == null)
                customId = GenerateCustomId();

            if (newUser)
                dyn.EncryptedRequest = EncryptCustomIdLoginToken(customId, clientSecret);
            else
                dyn.EncryptedRequest = null;

            dyn.InfoRequestParameters = new ExpandoObject();
            dyn.InfoRequestParameters.GetCharacterInventories = false;
            dyn.InfoRequestParameters.GetCharacterList = false;
            dyn.InfoRequestParameters.GetPlayerProfile = true;
            dyn.InfoRequestParameters.GetPlayerStatistics = false;
            dyn.InfoRequestParameters.GetTitleData = false;
            dyn.InfoRequestParameters.GetUserAccountInfo = true;
            dyn.InfoRequestParameters.GetUserData = false;
            dyn.InfoRequestParameters.GetUserInventory = false;
            dyn.InfoRequestParameters.GetUserReadOnlyData = false;
            dyn.InfoRequestParameters.PlayerStatisticNames = null;
            dyn.InfoRequestParameters.ProfileConstraints = null;
            dyn.InfoRequestParameters.TitleDataKeys = null;
            dyn.InfoRequestParameters.UserDataKeys = null;
            dyn.InfoRequestParameters.UserReadOnlyDataKeys = null;

            dyn.PlayerSecret = null;
            dyn.TitleId = TITLE_ID;


            dynamic resp = POST(dyn, "/Client/LoginWithCustomID", newUser ? null : clientSecret, true);

            Config.WriteConfValue("CustomId", customId);
            Config.WriteConfValue("ClientSecret", clientSecret);

            ENTITY_TOKEN = resp.EntityToken.EntityToken.ToString();
            ENTITY_ID = resp.PlayFabId.ToString();
            ENTITY_TYPE = "master_player_account";

            return ENTITY_TOKEN;
        }

        public static string EncryptRequest(string plaintext)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportCspBlob(PUBLIC_KEY);
                byte[] bytesToEncrypt = Encoding.UTF8.GetBytes(plaintext);
                byte[] encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
                return Convert.ToBase64String(encryptedBytes);
            }
        }

        public static string PullEntityTokenOutOfMyAss()
        {

            if (Config.GetConfValue("CustomId") == null && Config.GetConfValue("ClientSecret") == null)
            {
                GetPublicKeyAndMicrosoftTakesABigL();
                LoginWithCustomId();
                return RefreshEntityTokenBullshit();
            }
            else
            {
                string customId = Config.GetConfValue("CustomId");
                string clientSecret = Config.GetConfValue("ClientSecret");

                LoginWithCustomId(false, customId, clientSecret);
                return RefreshEntityTokenBullshit();
            }

        }

    }
}
