using Microsoft.EntityFrameworkCore;
using OmsorgCore.Application.Abstractions;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Infrastructure.Persistence;

namespace OmsorgCore.Infrastructure.Repositories;

public class PasswordResetCodeRepository : IPasswordResetCodeRepository
{
    private readonly OmsorgCoreDbContext _db;

    public PasswordResetCodeRepository(OmsorgCoreDbContext db)
    {
        _db = db;
    }

    public async Task AddAsync(PasswordResetCode code, CancellationToken cancellationToken = default)
        => await _db.PasswordResetCodes.AddAsync(code, cancellationToken);

    public Task<PasswordResetCode?> GetActiveByUserIdAsync(Guid userId, int maxAttempts, CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        return _db.PasswordResetCodes
            .Where(c => c.UserId == userId
                && c.InvalidatedAt == null
                && c.ConsumedAt == null
                && c.ExpiresAt > now
                && c.AttemptCount < maxAttempts)
            .OrderByDescending(c => c.CreatedAt)
            .FirstOrDefaultAsync(cancellationToken);
    }

    public Task<PasswordResetCode?> GetLatestByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
        => _db.PasswordResetCodes
            .Where(c => c.UserId == userId)
            .OrderByDescending(c => c.CreatedAt)
            .FirstOrDefaultAsync(cancellationToken);

    public Task<PasswordResetCode?> GetByResetTokenHashAsync(string resetTokenHash, CancellationToken cancellationToken = default)
        => _db.PasswordResetCodes
            .Include(c => c.User)
            .FirstOrDefaultAsync(c => c.ResetTokenHash == resetTokenHash, cancellationToken);

    public async Task InvalidateActiveForUserAsync(Guid userId, CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        var activeCodes = await _db.PasswordResetCodes
            .Where(c => c.UserId == userId
                && c.InvalidatedAt == null
                && c.ConsumedAt == null
                && c.ExpiresAt > now)
            .ToListAsync(cancellationToken);

        foreach (var code in activeCodes)
        {
            code.InvalidatedAt = now;
        }
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => _db.SaveChangesAsync(cancellationToken);
}
