import { InvalidClientError } from '../../flows/domain/errors';
import {
  oAuthAuthenticateApplication,
  type OAuthFindApplicationPort,
} from './o-auth-authenticate-application.function';

const APPLICATION_CODE = 'aurora';
const APPLICATION_SECRET = 'top-secret-12345';

const buildApplication = (overrides: Record<string, unknown> = {}) => ({
  id: 'app-1111-1111-1111-111111111111',
  code: APPLICATION_CODE,
  secret: APPLICATION_SECRET,
  clients: [{ id: 'cli-1111', grantType: 'PASSWORD' }],
  ...overrides,
});

const buildFind = (impl: jest.Mock): OAuthFindApplicationPort => ({ main: impl });

const args = (overrides: Partial<Parameters<typeof oAuthAuthenticateApplication>[1]> = {}) => ({
  clientId: APPLICATION_CODE,
  clientSecret: APPLICATION_SECRET,
  ...overrides,
});

describe('oAuthAuthenticateApplication', () => {
  it('returns the application when code + secret match', async () => {
    const application = buildApplication();
    const findApplication = buildFind(jest.fn().mockResolvedValue(application));

    const result = await oAuthAuthenticateApplication(findApplication, args());

    expect(result).toBe(application);
  });

  it('queries the application by code with clients eager-loaded', async () => {
    const main = jest.fn().mockResolvedValue(buildApplication());

    await oAuthAuthenticateApplication(buildFind(main), args());

    expect(main).toHaveBeenCalledWith({
      where: { code: APPLICATION_CODE },
      include: [{ association: 'clients' }],
    });
  });

  it('throws InvalidClientError when the application is not found (repository throws)', async () => {
    const findApplication = buildFind(jest.fn().mockRejectedValue(new Error('NotFound')));

    await expect(oAuthAuthenticateApplication(findApplication, args())).rejects.toBeInstanceOf(
      InvalidClientError,
    );
  });

  it('throws InvalidClientError when the secret mismatches', async () => {
    const findApplication = buildFind(
      jest.fn().mockResolvedValue(buildApplication({ secret: 'different' })),
    );

    await expect(oAuthAuthenticateApplication(findApplication, args())).rejects.toBeInstanceOf(
      InvalidClientError,
    );
  });
});
