import { BadRequestException } from '@nestjs/common';
import { RoleHierarchyService } from './role-hierarchy.service';

describe('RoleHierarchyService', () => {
  const roles = [
    { id: 'admin', name: 'Admin', is_system: true },
    { id: 'sales', name: 'Sales Team', is_system: true },
  ];

  const mockRoleRepo = {
    find: jest.fn(),
  };

  const mockHierarchyRepo = {
    find: jest.fn(),
    findOne: jest.fn(),
    save: jest.fn(),
    update: jest.fn(),
    softDelete: jest.fn(),
  };

  const service = new RoleHierarchyService(mockRoleRepo as never, mockHierarchyRepo as never);

  beforeEach(() => {
    jest.clearAllMocks();
    mockRoleRepo.find.mockResolvedValue(roles);
    mockHierarchyRepo.find.mockResolvedValue([
      { id: 'h1', role_id: 'admin', parent_role_id: null },
      { id: 'h2', role_id: 'sales', parent_role_id: 'admin' },
    ]);
  });

  it('rejects a parent set from outside this tenant', async () => {
    await expect(service.setParent('sales', 'other-tenant-role')).rejects.toThrow(
      BadRequestException,
    );
    expect(mockHierarchyRepo.update).not.toHaveBeenCalled();
  });

  it('rejects a parent role that is not in this tenant', async () => {
    await expect(
      service.replace({
        links: [
          { role_id: 'admin', parent_role_id: null },
          { role_id: 'sales', parent_role_id: 'other-tenant-role' },
        ],
      }),
    ).rejects.toThrow(BadRequestException);
    expect(mockHierarchyRepo.update).not.toHaveBeenCalled();
  });

  it('rejects a cycle', async () => {
    await expect(
      service.replace({
        links: [
          { role_id: 'admin', parent_role_id: 'sales' },
          { role_id: 'sales', parent_role_id: 'admin' },
        ],
      }),
    ).rejects.toThrow(BadRequestException);
  });

  it('returns the stored parent for each role', async () => {
    const nodes = await service.get();
    expect(nodes).toEqual([
      { id: 'admin', name: 'Admin', is_system: true, parent_role_id: null },
      { id: 'sales', name: 'Sales Team', is_system: true, parent_role_id: 'admin' },
    ]);
  });

  it('moves children up when a role leaves the tree', async () => {
    mockHierarchyRepo.findOne.mockResolvedValue({
      id: 'h-mid',
      role_id: 'mid',
      parent_role_id: 'admin',
    });
    mockHierarchyRepo.find.mockResolvedValue([
      { id: 'h-child', role_id: 'sales', parent_role_id: 'mid' },
    ]);

    await service.detach('mid');

    expect(mockHierarchyRepo.update).toHaveBeenCalledWith('h-child', { parent_role_id: 'admin' });
    expect(mockHierarchyRepo.softDelete).toHaveBeenCalledWith('h-mid');
  });
});
