using MealPlan.Api.Data;
using MealPlan.Api.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace MealPlan.Api.Services;

/// <summary>Persists refresh tokens in PostgreSQL (survives API restarts).</summary>
public class DbRefreshTokenStore : IRefreshTokenStore
{
    private readonly IServiceScopeFactory _scopeFactory;

    public DbRefreshTokenStore(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;

    public void Add(RefreshToken token)
    {
        using var scope = _scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        db.RefreshTokens.Add(new RefreshTokenEntity
        {
            Token = token.Token,
            UserId = Guid.Parse(token.UserId),
            ExpiresAtUtc = token.ExpiresAtUtc,
            CreatedAtUtc = token.CreatedAtUtc,
            IsRevoked = false,
        });
        db.SaveChanges();
    }

    public RefreshToken? Get(string token)
    {
        using var scope = _scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var entity = db.RefreshTokens.AsNoTracking().FirstOrDefault(t => t.Token == token);
        return entity is null ? null : Map(entity);
    }

    public RefreshToken? Revoke(string token)
    {
        using var scope = _scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var entity = db.RefreshTokens.FirstOrDefault(t => t.Token == token);
        if (entity is null) return null;
        var wasActive = entity.IsActive;
        entity.IsRevoked = true;
        db.SaveChanges();
        return wasActive ? Map(entity) : null;
    }

    public void RemoveExpired()
    {
        using var scope = _scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var cutoff = DateTime.UtcNow;
        db.RefreshTokens
            .Where(t => t.IsRevoked || t.ExpiresAtUtc <= cutoff)
            .ExecuteDelete();
    }

    private static RefreshToken Map(RefreshTokenEntity entity) => new()
    {
        Token = entity.Token,
        UserId = entity.UserId.ToString(),
        ExpiresAtUtc = entity.ExpiresAtUtc,
        CreatedAtUtc = entity.CreatedAtUtc,
        IsRevoked = entity.IsRevoked,
    };
}
