using Baya.Application.Contracts.Persistence;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;

namespace Baya.Infrastructure.Persistence.Repositories;

internal class UserSessionRepository : BaseAsyncRepository<UserSession>, IUserSessionRepository
{
    public UserSessionRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public Task AddAsync(UserSession session, CancellationToken cancellationToken)
    {
        return base.AddAsync(session);
    }

    public Task<UserSession> GetByTokenHashAsync(string refreshTokenHash, CancellationToken cancellationToken)
    {
        return Table
            .Include(s => s.User)
            .FirstOrDefaultAsync(s => s.RefreshTokenHash == refreshTokenHash, cancellationToken);
    }

    public Task<UserSession> GetActiveForUserByTokenHashAsync(int userId, string refreshTokenHash, CancellationToken cancellationToken)
    {
        return Table.FirstOrDefaultAsync(
            s => s.UserId == userId && s.RefreshTokenHash == refreshTokenHash && !s.IsRevoked,
            cancellationToken);
    }

    public async Task<int> RevokeAllActiveForUserAsync(int userId, DateTimeOffset revokedAt, CancellationToken cancellationToken)
    {
        // Sessions-per-user is small; tracked mutation keeps the revocation inside the caller's
        // single commit instead of an out-of-band ExecuteUpdate.
        var activeSessions = await Table
            .Where(s => s.UserId == userId && !s.IsRevoked)
            .ToListAsync(cancellationToken);

        foreach (var session in activeSessions)
            session.Revoke(revokedAt);

        return activeSessions.Count;
    }
}