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

namespace Baya.Infrastructure.Persistence.Repositories;

internal class UserAccountRepository : BaseAsyncRepository<User>, IUserAccountRepository
{
    public UserAccountRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public Task<UserAccountSnapshot> GetAccountSnapshotAsync(int userId, CancellationToken cancellationToken)
    {
        // UserRoles carries a RevokedAt-is-null global filter, so revoked grants never surface here.
        return TableNoTracking
            .Where(u => u.Id == userId)
            .Select(u => new UserAccountSnapshot(
                u.Id,
                u.PhoneNumber,
                u.Name,
                u.FamilyName,
                u.Gender,
                u.IsActive,
                u.UserRoles.Select(ur => ur.Role.Name).ToList()))
            .FirstOrDefaultAsync(cancellationToken);
    }

    public Task<User> GetTrackedByIdAsync(int userId, CancellationToken cancellationToken)
        => Table.FirstOrDefaultAsync(u => u.Id == userId, cancellationToken);

    public Task<Role> GetRoleByNameAsync(string roleName, CancellationToken cancellationToken)
    {
        return DbContext.Set<Role>()
            .FirstOrDefaultAsync(r => r.Name == roleName, cancellationToken);
    }

    public Task<UserRole> GetUserRoleIncludingRevokedAsync(int userId, int roleId, CancellationToken cancellationToken)
    {
        // Bypasses the revoked-filter on purpose: a revoked grant must be re-activated, not
        // re-inserted (composite PK).
        return DbContext.Set<UserRole>()
            .IgnoreQueryFilters()
            .FirstOrDefaultAsync(ur => ur.UserId == userId && ur.RoleId == roleId, cancellationToken);
    }

    public async Task AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken)
    {
        await DbContext.Set<UserRole>().AddAsync(userRole, cancellationToken);
    }
}