using System;
using System.Threading;
using UnityEngine;

namespace RudderSdk.Unity
{
  public sealed class PlayerPrefsUnityKeyValueStore : IUnityKeyValueStore
  {
    private readonly SynchronizationContext _unityContext;
    private readonly int _mainThreadId;

    public PlayerPrefsUnityKeyValueStore()
    {
      _unityContext = SynchronizationContext.Current;
      _mainThreadId = Thread.CurrentThread.ManagedThreadId;
    }

    public string GetString(string key)
    {
      return RunOnMainThread(() =>
      {
        var value = PlayerPrefs.GetString(key, string.Empty);
        return string.IsNullOrEmpty(value) ? null : value;
      });
    }

    public void SetString(string key, string value)
    {
      RunOnMainThread(() => PlayerPrefs.SetString(key, value));
    }

    public void DeleteKey(string key)
    {
      RunOnMainThread(() => PlayerPrefs.DeleteKey(key));
    }

    public void Save()
    {
      RunOnMainThread(PlayerPrefs.Save);
    }

    private void RunOnMainThread(Action action)
    {
      RunOnMainThread(() =>
      {
        action();
        return true;
      });
    }

    private T RunOnMainThread<T>(Func<T> action)
    {
      if (Thread.CurrentThread.ManagedThreadId == _mainThreadId || _unityContext == null)
        return action();

      T result = default;
      Exception captured = null;
      using (var completed = new ManualResetEventSlim(false))
      {
        _unityContext.Post(_ =>
        {
          try
          {
            result = action();
          }
          catch (Exception ex)
          {
            captured = ex;
          }
          finally
          {
            completed.Set();
          }
        }, null);

        completed.Wait();
      }

      if (captured != null)
        throw captured;

      return result;
    }
  }
}
