using DigitalOps.Authority.Services;
using DigitalOps.Dal;
using DigitalOps.Dal.DbEntity;
using FluentValidation;
using Microsoft.EntityFrameworkCore;

namespace DigitalOps.Authority.Validators;

public class HasAccessToOrganizationValidator : AbstractValidator<long>
{
  private readonly OrganizationRole _role;
  private readonly MainDbContext _dbContext;
  private readonly UserIdentityService _userIdentityService;
  
  public HasAccessToOrganizationValidator(OrganizationRole role, MainDbContext dbContext, UserIdentityService userIdentityService)
  {
    _role = role;
    _userIdentityService = userIdentityService;
    _dbContext = dbContext;
    
    RuleFor(organizationId => organizationId)
      .MustAsync(HasAccess)
      .WithMessage("You do not have the proper role to this organization for this action.");
  }

  private async Task<bool> HasAccess(long organizationId, CancellationToken cancellationToken)
  {
    var user = await _userIdentityService.GetUserOrDefaultAsync(cancellationToken);
    if (user is null)
      return false;

    // todo: extract this into a reusable helper class
    var roles = new List<OrganizationRole>();
    if (_role == OrganizationRole.Owner)
      roles.AddRange([OrganizationRole.Owner]);
    
    if (_role == OrganizationRole.Admin)
      roles.AddRange([OrganizationRole.Owner, OrganizationRole.Admin]);
    
    if (_role == OrganizationRole.Member)
      roles.AddRange([OrganizationRole.Owner, OrganizationRole.Admin, OrganizationRole.Member]);

    var result = await _dbContext.OrganizationUsers
      .AsNoTracking()
      .Where(organizationUser => organizationUser.OrganizationId == organizationId)
      .Where(organizationUser => roles.Contains(organizationUser.Role))
      .AnyAsync(cancellationToken);

    return result;
  }
}