using System;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
namespace Watermelon
{
public static class User
{
private const string LocalIdKey = "user_local_id_v1";
private static string localId;
private static string customId;
/// Stable local ID (generated once and stored in PlayerPrefs).
public static string LocalId => localId ??= LoadOrCreateLocalId();
/// Custom ID (e.g., Firebase UID). Not persisted unless you save it manually.
public static string CustomId => customId;
/// Whether a custom ID is set.
public static bool HasCustomId => !string.IsNullOrWhiteSpace(customId);
/// Main ID for the system: uses CustomId if present, otherwise LocalId.
public static string GetId() => HasCustomId ? customId : LocalId;
public static void SetCustomId(string customId)
{
User.customId = Sanitize(customId);
}
/// SHA-256 hash in hex (64 characters) of GetId(). Useful for Google ObfuscatedAccountId.
public static string GetIdSha256Hex() => Sha256Hex(GetId());
///
/// Guid created from the first 16 bytes of SHA-256 hash of GetId(). Useful for Apple AppAccountToken.
///
public static Guid GetIdAsGuidFromSha256()
{
using (SHA256 sha = SHA256.Create())
{
byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(GetId() ?? ""));
byte[] guidBytes = new byte[16];
Buffer.BlockCopy(hash, 0, guidBytes, 0, 16);
return new Guid(guidBytes);
}
}
private static string LoadOrCreateLocalId()
{
string id = PlayerPrefs.GetString(LocalIdKey, null);
if (string.IsNullOrEmpty(id))
{
id = Guid.NewGuid().ToString("N");
PlayerPrefs.SetString(LocalIdKey, id);
PlayerPrefs.Save();
}
return id;
}
private static string Sanitize(string s)
{
s = s?.Trim();
return string.IsNullOrEmpty(s) ? null : s;
}
private static string Sha256Hex(string s)
{
using (SHA256 sha = SHA256.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(s ?? "");
byte[] hash = sha.ComputeHash(bytes);
StringBuilder sb = new StringBuilder(hash.Length * 2);
foreach (byte b in hash) sb.Append(b.ToString("x2"));
return sb.ToString();
}
}
}
}