using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Caching.Distributed;

namespace Infrastructure.Extensions;

public sealed class DistributedCacheTicketStore : ITicketStore
{
    private readonly IDistributedCache _cache;
    private readonly TicketSerializer _serializer = TicketSerializer.Default;
    private readonly string _keyPrefix;

    public DistributedCacheTicketStore(IDistributedCache cache, string keyPrefix = "AuthTicket:")
    {
        _cache = cache;
        _keyPrefix = keyPrefix;
    }

    private string BuildKey(string key) => _keyPrefix + key;

    private static DistributedCacheEntryOptions BuildEntryOptions(AuthenticationTicket ticket)
    {
        // CookieAuthenticationHandler usually sets ExpiresUtc.
        var expires = ticket.Properties.ExpiresUtc;

        if (expires.HasValue)
        {
            return new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = expires.Value
            };
        }

        // Fallback if ExpiresUtc is null.
        return new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(8)
        };
    }

    public async Task<string> StoreAsync(AuthenticationTicket ticket)
    {
        var key = Guid.NewGuid().ToString("N");
        await RenewAsync(key, ticket);
        return key;
    }

    public async Task RenewAsync(string key, AuthenticationTicket ticket)
    {
        var data = _serializer.Serialize(ticket);
        var options = BuildEntryOptions(ticket);
        await _cache.SetAsync(BuildKey(key), data, options);
    }

    public async Task<AuthenticationTicket?> RetrieveAsync(string key)
    {
        var data = await _cache.GetAsync(BuildKey(key));
        return data is null ? null : _serializer.Deserialize(data);
    }

    public Task RemoveAsync(string key) => _cache.RemoveAsync(BuildKey(key));
}