using OmsorgCore.Application.Abstractions;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Domain.Enums;

namespace OmsorgCore.Application.Services;

public class RoleService : IRoleService
{
    private readonly IRoleRepository _roleRepository;
    private readonly IPermissionService _permissionService;
    private readonly IUserRepository _userRepository;

    public RoleService(IRoleRepository roleRepository, IPermissionService permissionService, IUserRepository userRepository)
    {
        _roleRepository = roleRepository;
        _permissionService = permissionService;
        _userRepository = userRepository;
    }

    public Task<IReadOnlyList<Role>> GetAllAsync(CancellationToken cancellationToken = default)
        => _roleRepository.GetAllAsync(cancellationToken);

    public async Task<CreateRoleResult> CreateAsync(string name, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(name))
        {
            return CreateRoleResult.Fail(CreateRoleFailureReason.NameRequired);
        }

        if (await _roleRepository.ExistsByNameAsync(name, cancellationToken))
        {
            return CreateRoleResult.Fail(CreateRoleFailureReason.NameTaken);
        }

        var role = new Role { Name = name };
        await _roleRepository.AddAsync(role, cancellationToken);
        await _roleRepository.SaveChangesAsync(cancellationToken);

        return CreateRoleResult.Ok(role);
    }

    public Task<Role?> GetByIdWithPermissionsAsync(Guid id, CancellationToken cancellationToken = default)
        => _roleRepository.GetByIdWithPermissionsAsync(id, cancellationToken);

    public async Task<UpdateRolePermissionsResult> UpdatePermissionsAsync(
        Guid roleId,
        IReadOnlyList<(ModuleType Module, PermissionAction Action, PermissionScope Scope)> permissions,
        CancellationToken cancellationToken = default)
    {
        var role = await _roleRepository.GetByIdWithPermissionsAsync(roleId, cancellationToken);
        if (role is null)
        {
            return UpdateRolePermissionsResult.Fail(UpdateRolePermissionsFailureReason.RoleNotFound);
        }

        role.RolePermissions.Clear();
        await _roleRepository.SaveChangesAsync(cancellationToken);

        var newPermissions = permissions.DistinctBy(p => (p.Module, p.Action))
            .Select(p => new RolePermission { RoleId = role.Id, Module = p.Module, Action = p.Action, Scope = p.Scope })
            .ToList();
        await _roleRepository.AddPermissionRangeAsync(newPermissions, cancellationToken);
        await _roleRepository.SaveChangesAsync(cancellationToken);
        await _permissionService.InvalidateRolePermissionsAsync(roleId, cancellationToken);
        return UpdateRolePermissionsResult.Ok();
    }

    public async Task<DeleteRoleResult> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var role = await _roleRepository.GetByIdAsync(id, cancellationToken);
        if (role is null)
        {
            return DeleteRoleResult.Fail(DeleteRoleFailureReason.RoleNotFound);
        }

        var assignedUserIds = await _userRepository.GetUserIdsByRoleAsync(id, cancellationToken);
        if (assignedUserIds.Count > 0)
        {
            return DeleteRoleResult.Fail(DeleteRoleFailureReason.InUse, assignedUserIds.Count);
        }

        await _roleRepository.RemoveAsync(role, cancellationToken);
        await _roleRepository.SaveChangesAsync(cancellationToken);
        return DeleteRoleResult.Ok();
    }
}
