import { canView26As, canViewForm16Submission, getForm16ViewerConfig } from '../services/form16Permission.service';
import { sequelize } from '../config/database';
import { getDealerCodeForUser } from '../services/form16.service';

jest.mock('../config/database', () => ({
  sequelize: {
    query: jest.fn(),
  },
}));

jest.mock('../services/form16.service', () => ({
  getDealerCodeForUser: jest.fn(),
}));

describe('Form16 Permission Service (strict RBAC)', () => {
  const mockedQuery = sequelize.query as jest.Mock;
  const mockedGetDealerCodeForUser = getDealerCodeForUser as jest.Mock;

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('returns normalized viewer lists from config', async () => {
    mockedQuery.mockResolvedValue([
      {
        config_value: JSON.stringify({
          submissionViewerEmails: [' User1@royalenfield.com '],
          twentySixAsViewerEmails: ['USER2@royalenfield.com'],
        }),
      },
    ]);

    const config = await getForm16ViewerConfig();

    expect(config.submissionViewerEmails).toEqual(['user1@royalenfield.com']);
    expect(config.twentySixAsViewerEmails).toEqual(['user2@royalenfield.com']);
  });

  it('ADMIN always has submission and 26AS access', async () => {
    expect(await canViewForm16Submission('admin@royalenfield.com', 'u-admin', 'ADMIN')).toBe(true);
    expect(await canView26As('admin@royalenfield.com', 'ADMIN')).toBe(true);
  });

  it('dealer always has submission access, but not implicit 26AS access', async () => {
    mockedGetDealerCodeForUser.mockResolvedValue('DLR001');
    mockedQuery.mockResolvedValue([{ config_value: JSON.stringify({ submissionViewerEmails: [], twentySixAsViewerEmails: [] }) }]);

    expect(await canViewForm16Submission('dealer@royalenfield.com', 'u-dealer', 'USER')).toBe(true);
    expect(await canView26As('dealer@royalenfield.com', 'USER')).toBe(false);
  });

  it('non-admin RE user gets submission access only when listed in submission viewers', async () => {
    mockedGetDealerCodeForUser.mockResolvedValue(null);
    mockedQuery.mockResolvedValue([
      {
        config_value: JSON.stringify({
          submissionViewerEmails: ['submissions@royalenfield.com'],
          twentySixAsViewerEmails: [],
        }),
      },
    ]);

    expect(await canViewForm16Submission('submissions@royalenfield.com', 'u1', 'USER')).toBe(true);
    expect(await canViewForm16Submission('other@royalenfield.com', 'u2', 'USER')).toBe(false);
  });

  it('26AS viewers implicitly have submission access', async () => {
    mockedGetDealerCodeForUser.mockResolvedValue(null);
    mockedQuery.mockResolvedValue([
      {
        config_value: JSON.stringify({
          submissionViewerEmails: [],
          twentySixAsViewerEmails: ['twentysix@royalenfield.com'],
        }),
      },
    ]);

    expect(await canViewForm16Submission('twentysix@royalenfield.com', 'u3', 'USER')).toBe(true);
    expect(await canView26As('twentysix@royalenfield.com', 'USER')).toBe(true);
  });

  it('strict deny when viewer lists are empty for non-admin RE user', async () => {
    mockedGetDealerCodeForUser.mockResolvedValue(null);
    mockedQuery.mockResolvedValue([
      {
        config_value: JSON.stringify({
          submissionViewerEmails: [],
          twentySixAsViewerEmails: [],
        }),
      },
    ]);

    expect(await canViewForm16Submission('re-user@royalenfield.com', 'u4', 'USER')).toBe(false);
    expect(await canView26As('re-user@royalenfield.com', 'USER')).toBe(false);
  });
});
