using System.Globalization;

namespace Prospect.Unreal.Net.Dtls;

/// <summary>
/// Table identité DTLS (= PlayFab user_id) -> PSK 32 octets.
///
/// Les clés NE SONT JAMAIS en dur ni commitées : elles viennent de l'environnement
/// (rendu depuis Vault). Format de PROSPECT_DTLS_PSKS :
///     user_id:hex32[,user_id:hex32...]
/// ex. "92EBCFE8C3EAF3AC:1E02B3F2...410F1"
///
/// La dérivation par user_id vit dans le client (code packé) ; tant qu'on ne l'a
/// pas, on fournit les PSK connues (une par joueur) via cette table.
/// </summary>
public sealed class DtlsPskStore
{
    private readonly Dictionary<string, byte[]> _byIdentity = new(StringComparer.OrdinalIgnoreCase);

    public static DtlsPskStore FromEnvironment(string variable = "PROSPECT_DTLS_PSKS")
    {
        return Parse(Environment.GetEnvironmentVariable(variable));
    }

    public static DtlsPskStore Parse(string? spec)
    {
        var store = new DtlsPskStore();
        if (string.IsNullOrWhiteSpace(spec))
        {
            return store;
        }

        foreach (var entry in spec.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
        {
            var sep = entry.IndexOf(':');
            if (sep <= 0)
            {
                continue;
            }

            var identity = entry[..sep].Trim();
            var psk = HexToBytes(entry[(sep + 1)..].Trim());
            if (psk.Length == 32)
            {
                store._byIdentity[identity] = psk;
            }
        }

        return store;
    }

    public bool HasKeys => _byIdentity.Count > 0;

    public int Count => _byIdentity.Count;

    /// <summary>PSK pour une identité (user_id), ou null si inconnue.</summary>
    public byte[]? Get(string identity)
    {
        return _byIdentity.TryGetValue(identity, out var psk) ? psk : null;
    }

    private static byte[] HexToBytes(string hex)
    {
        if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
        {
            hex = hex[2..];
        }

        if (hex.Length % 2 != 0)
        {
            return Array.Empty<byte>();
        }

        var bytes = new byte[hex.Length / 2];
        for (var i = 0; i < bytes.Length; i++)
        {
            if (!byte.TryParse(hex.AsSpan(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bytes[i]))
            {
                return Array.Empty<byte>();
            }
        }

        return bytes;
    }
}
