using System.Collections.Concurrent;
using System.Security.Cryptography;

namespace Ikar.Server.Infrastructure.Auth;

public sealed class PhoneAuthChallengeStore
{
    private static readonly TimeSpan ChallengeLifetime = TimeSpan.FromMinutes(10);
    private readonly ConcurrentDictionary<string, PhoneAuthChallenge> _challenges = new(StringComparer.Ordinal);

    public PhoneAuthChallenge Create(string normalizedPhoneNumber, bool isRegistered)
    {
        CleanupExpired();

        var challenge = new PhoneAuthChallenge(
            Guid.NewGuid().ToString("N"),
            normalizedPhoneNumber,
            RandomNumberGenerator.GetInt32(0, 1_000_000).ToString("D6"),
            DateTimeOffset.UtcNow.Add(ChallengeLifetime),
            isRegistered);

        _challenges[challenge.Id] = challenge;
        return challenge;
    }

    public PhoneAuthChallenge? Consume(string challengeId, string normalizedPhoneNumber, string code)
    {
        CleanupExpired();

        if (!_challenges.TryGetValue(challengeId, out var challenge))
        {
            return null;
        }

        if (!string.Equals(challenge.NormalizedPhoneNumber, normalizedPhoneNumber, StringComparison.Ordinal) ||
            !string.Equals(challenge.Code, code, StringComparison.Ordinal) ||
            challenge.ExpiresAt <= DateTimeOffset.UtcNow)
        {
            return null;
        }

        _challenges.TryRemove(challenge.Id, out _);
        return challenge;
    }

    private void CleanupExpired()
    {
        var now = DateTimeOffset.UtcNow;
        foreach (var pair in _challenges)
        {
            if (pair.Value.ExpiresAt <= now)
            {
                _challenges.TryRemove(pair.Key, out _);
            }
        }
    }

    public sealed record PhoneAuthChallenge(
        string Id,
        string NormalizedPhoneNumber,
        string Code,
        DateTimeOffset ExpiresAt,
        bool IsRegistered);
}
