import { ForbiddenException } from '@nestjs/common';
import { TeamPermissionsService } from './team-permissions.service';

describe('TeamPermissionsService', () => {
  const teamRepository = { findOne: jest.fn() };
  const teamSettingRepository = {
    save: jest.fn((s) => Promise.resolve(s)),
    create: jest.fn((s) => s),
  };
  const access = { assertMember: jest.fn(), assertManager: jest.fn() };
  const logger = { info: jest.fn() };
  let service: TeamPermissionsService;

  beforeEach(() => {
    jest.resetAllMocks();
    teamSettingRepository.save.mockImplementation((s) => Promise.resolve(s));
    teamSettingRepository.create.mockImplementation((s) => s);
    service = new TeamPermissionsService(
      teamRepository as any,
      teamSettingRepository as any,
      access as any,
      logger as any,
    );
  });

  const teamWithSettings = (overrides: Record<string, string> = {}) => ({
    id: 9,
    settings: [
      { key: 'transaction_create_min_role', value: overrides.transaction_create_min_role ?? '2' },
      { key: 'transaction_reverse_min_role', value: overrides.transaction_reverse_min_role ?? '2' },
      { key: 'invite_min_role', value: overrides.invite_min_role ?? '3' },
      { key: 'member_manage_min_role', value: overrides.member_manage_min_role ?? '3' },
      { key: 'penalty_manage_min_role', value: overrides.penalty_manage_min_role ?? '3' },
      { key: 'public_access_manage_min_role', value: overrides.public_access_manage_min_role ?? '3' },
    ],
  });

  it('requires at least team membership to read permissions', async () => {
    access.assertMember.mockRejectedValue(new ForbiddenException());
    await expect(service.getPermissions(2, 9)).rejects.toBeInstanceOf(
      ForbiddenException,
    );
    expect(teamRepository.findOne).not.toHaveBeenCalled();
  });

  it('maps the stored settings to the permissions DTO', async () => {
    access.assertMember.mockResolvedValue(undefined);
    teamRepository.findOne.mockResolvedValue(teamWithSettings());

    await expect(service.getPermissions(2, 9)).resolves.toEqual({
      transactionCreateMinRole: 2,
      transactionReverseMinRole: 2,
      inviteMinRole: 3,
      memberManageMinRole: 3,
      penaltyManageMinRole: 3,
      publicAccessManageMinRole: 3,
    });
  });

  it('falls back to defaults for missing settings', async () => {
    access.assertMember.mockResolvedValue(undefined);
    teamRepository.findOne.mockResolvedValue({ id: 9, settings: [] });

    await expect(service.getPermissions(2, 9)).resolves.toEqual({
      transactionCreateMinRole: 2,
      transactionReverseMinRole: 2,
      inviteMinRole: 3,
      memberManageMinRole: 3,
      penaltyManageMinRole: 3,
      publicAccessManageMinRole: 3,
    });
  });

  it('requires manager rights to update permissions', async () => {
    access.assertManager.mockRejectedValue(new ForbiddenException());
    await expect(
      service.updatePermissions(2, 9, { inviteMinRole: 4 }),
    ).rejects.toBeInstanceOf(ForbiddenException);
    expect(teamSettingRepository.save).not.toHaveBeenCalled();
  });

  it('updates only the fields present in the DTO and leaves the rest untouched', async () => {
    access.assertManager.mockResolvedValue(undefined);
    const team = teamWithSettings();
    teamRepository.findOne.mockResolvedValue(team);

    await service.updatePermissions(2, 9, { inviteMinRole: 4 });

    expect(teamSettingRepository.save).toHaveBeenCalledTimes(1);
    const saved = teamSettingRepository.save.mock.calls[0][0];
    expect(saved).toMatchObject({ key: 'invite_min_role', value: '4' });
    expect(logger.info).toHaveBeenCalledWith(
      expect.objectContaining({ event: 'team_permissions_update', userId: 2 }),
    );
  });

  it('creates a new setting row if none exists yet for that key', async () => {
    access.assertManager.mockResolvedValue(undefined);
    teamRepository.findOne.mockResolvedValue({ id: 9, settings: [] });

    await service.updatePermissions(2, 9, { memberManageMinRole: 4 });

    expect(teamSettingRepository.create).toHaveBeenCalledWith(
      expect.objectContaining({ key: 'member_manage_min_role', value: '4' }),
    );
    expect(teamSettingRepository.save).toHaveBeenCalledTimes(1);
  });
});
