using System.Security.Cryptography;

namespace Meezi.Core.Utilities;

/// <summary>
/// Admin-generated café recovery keys. The raw key is shown to the admin exactly
/// once; only its SHA-256 hash is stored, looked up at login by exact hash match.
/// A salt is unnecessary (and would break lookup) because the key carries ~190
/// bits of entropy — the same model password-manager / API-token systems use.
/// </summary>
public static class RecoveryKeyGenerator
{
    // Crockford-ish base32 alphabet, no easily-confused chars (0/O, 1/I/L).
    private const string Alphabet = "23456789ABCDEFGHJKMNPQRSTUVWXYZ";
    private const int GroupCount = 4;
    private const int GroupSize = 5;

    /// <summary>Make a fresh key. Returns the raw key (give to the owner) and its
    /// hash (store on the café). Format: <c>MZ-XXXXX-XXXXX-XXXXX-XXXXX</c>.</summary>
    public static (string RawKey, string Hash) Generate()
    {
        var chars = new char[GroupCount * GroupSize];
        for (var i = 0; i < chars.Length; i++)
            chars[i] = Alphabet[RandomNumberGenerator.GetInt32(Alphabet.Length)];

        var groups = new string[GroupCount];
        for (var g = 0; g < GroupCount; g++)
            groups[g] = new string(chars, g * GroupSize, GroupSize);

        var rawKey = "MZ-" + string.Join("-", groups);
        return (rawKey, HashOf(rawKey));
    }

    /// <summary>SHA-256 hex of a normalized key, for storage and exact-match lookup.</summary>
    public static string HashOf(string rawKey)
    {
        var normalized = Normalize(rawKey);
        var bytes = SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(normalized));
        return Convert.ToHexString(bytes).ToLowerInvariant();
    }

    /// <summary>Uppercase, trim, and collapse spaces so a hand-typed key still matches.</summary>
    public static string Normalize(string rawKey) =>
        rawKey.Trim().ToUpperInvariant().Replace(" ", "");
}
