using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

namespace MajacloudSdk.Internal
{
    internal interface IMajacloudStorage
    {
        string GetString(string key, string fallback = null);
        void SetString(string key, string value);
        void Delete(string key);
        bool Has(string key);
        void Flush();
    }

    internal sealed class PlayerPrefsStorage : IMajacloudStorage
    {
        public string GetString(string key, string fallback = null)
        {
            try
            {
                return PlayerPrefs.HasKey(key) ? PlayerPrefs.GetString(key) : fallback;
            }
            catch (Exception)
            {
                return fallback;
            }
        }

        public void SetString(string key, string value)
        {
            try
            {
                PlayerPrefs.SetString(key, value ?? string.Empty);
            }
            catch (Exception e)
            {
                Debug.LogWarning("[Majacloud] PlayerPrefs write failed for " + key + ": " + e.Message);
            }
        }

        public void Delete(string key)
        {
            try
            {
                PlayerPrefs.DeleteKey(key);
            }
            catch (Exception)
            {
            }
        }

        public bool Has(string key)
        {
            try
            {
                return PlayerPrefs.HasKey(key);
            }
            catch (Exception)
            {
                return false;
            }
        }

        public void Flush()
        {
            try
            {
                PlayerPrefs.Save();
            }
            catch (Exception)
            {
            }
        }
    }

    internal sealed class MemoryStorage : IMajacloudStorage
    {
        private readonly Dictionary<string, string> _values = new Dictionary<string, string>();

        public string GetString(string key, string fallback = null) => _values.TryGetValue(key, out var value) ? value : fallback;
        public void SetString(string key, string value) => _values[key] = value ?? string.Empty;
        public void Delete(string key) => _values.Remove(key);
        public bool Has(string key) => _values.ContainsKey(key);
        public void Flush() { }
    }

    /// <summary>
    /// Light obfuscation for locally stored tokens (XOR with a device-derived
    /// key). This is not encryption: a modded build can always read its own
    /// PlayerPrefs. It only keeps tokens out of casual plain-text view.
    /// </summary>
    internal static class Obfuscator
    {
        public static string Protect(string plain, string key)
        {
            if (string.IsNullOrEmpty(plain)) return string.Empty;
            var bytes = Encoding.UTF8.GetBytes(plain);
            var mask = MajacloudCrypto.Sha256(Encoding.UTF8.GetBytes(key + "|majacloud.session.v2"));
            for (int i = 0; i < bytes.Length; i++) bytes[i] ^= mask[i % mask.Length];
            return "v2." + Convert.ToBase64String(bytes);
        }

        public static string Unprotect(string value, string key)
        {
            if (string.IsNullOrEmpty(value) || !value.StartsWith("v2.", StringComparison.Ordinal)) return null;
            try
            {
                var bytes = Convert.FromBase64String(value.Substring(3));
                var mask = MajacloudCrypto.Sha256(Encoding.UTF8.GetBytes(key + "|majacloud.session.v2"));
                for (int i = 0; i < bytes.Length; i++) bytes[i] ^= mask[i % mask.Length];
                return Encoding.UTF8.GetString(bytes);
            }
            catch (Exception)
            {
                return null;
            }
        }
    }

    internal static class StorageKeys
    {
        public const string DeviceId = "Majacloud.DeviceId";
        public static string Session(string projectId) => "Majacloud.Session." + projectId;
        public static string Config(string projectId) => "Majacloud.Config." + projectId;
        public static string ConfigEtag(string projectId) => "Majacloud.Config." + projectId + ".etag";
    }
}
