import {
  ConfigStatusResponseCode,
  IdentityProvider,
  TokenResponseCode,
} from '@axinom/mosaic-id-utils';
import { act, render } from '@testing-library/react';
import React from 'react';
import { setImmediate } from 'timers';
import { describe, expect, it, vi } from 'vitest';
import * as ISP from '../IdentityServiceProvider/IdentityServiceProvider';
import { EnsureAuthentication } from './EnsureAuthentication';

vi.mock('../AccountInactiveError/AccountInactiveError', () => ({
  AccountInactiveError: () => <div data-testid="account-inactive-error" />,
}));
vi.mock('../ConfigurationInvalidError/ConfigurationInvalidError', () => ({
  ConfigurationInvalidError: () => <div data-testid="config-invalid-error" />,
}));
vi.mock('../Login/Login', () => ({
  Login: () => <div data-testid="login-component" />,
}));

const flushPromises = () => new Promise(setImmediate);

const successValue = {
  addTokenChangedHandler: vi.fn(),
  getConfiguration: () => {
    return {
      status: ConfigStatusResponseCode.SUCCESS,
      providers: [
        { title: 'Google', idpId: IdentityProvider.GOOGLE, enabled: true },
      ],
    } as any;
  },
  getToken: () => {
    return {
      code: TokenResponseCode.SUCCESS,
      user: {
        email: 'test@test.com',
        id: '1234',
        name: 'Test User',
        profilePictureUrl: 'PictureUrl',
        token: {
          accessToken: 'accessToken',
          expiresInSeconds: Date.now() + 3000,
          permissions: {},
          tags: [],
        },
      },
    } as any;
  },
} as any;

describe('EnsureAuthentication', () => {
  it('renders content when user is authenticated', async () => {
    const value = { ...successValue };
    vi.spyOn(ISP, 'useIdentityService').mockImplementation(() => value);

    const { container } = render(
      <EnsureAuthentication>
        <div id="test" />
      </EnsureAuthentication>,
    );

    await act(async () => {
      await flushPromises();
    });

    expect(container.querySelector('#test')).toBeInTheDocument();
  });

  it('renders the login from when user is not authenticated', async () => {
    const value = {
      ...successValue,
      getToken: () => {
        return { code: TokenResponseCode.NEEDS_LOGIN };
      },
      getWellKnownUrls: () => {
        return {
          jwksEndpoint: 'http://localhost:10503/.well-known/jwks.json',
          accessManagementGraphQlEndpoint: 'http://localhost:10502/graphql',
          authGraphQlEndpoint: 'http://localhost:10503/graphql',
          axinomAuthBaseUrl: 'https://portal-int.axtest.net',
          axAuthManagementGraphQlEndpoint: 'http://localhost:10601/graphql',
        };
      },
    };
    vi.spyOn(ISP, 'useIdentityService').mockImplementation(() => value);

    const { container } = render(
      <EnsureAuthentication>
        <div id="test" />
      </EnsureAuthentication>,
    );

    await act(async () => {
      await flushPromises();
    });

    expect(container.querySelector('#test')).not.toBeInTheDocument();
    expect(
      container.querySelector('[data-testid="login-component"]'),
    ).toBeInTheDocument();
  });

  it.each([
    {
      code: ConfigStatusResponseCode.SUCCESS,
      enabledIdentityProviders: [],
    },
    {
      code: ConfigStatusResponseCode.TENANT_NOT_ACTIVE,
    },
    {
      code: ConfigStatusResponseCode.ENVIRONMENT_NOT_ACTIVE,
    },
    {
      code: ConfigStatusResponseCode.IDP_MISCONFIGURATION,
    },
    {
      code: ConfigStatusResponseCode.ERROR,
    },
  ])(
    'renders a configuration error from when the configuration is bad',
    async (config) => {
      const value = {
        ...successValue,
        getConfiguration: () => config,
      };
      vi.spyOn(ISP, 'useIdentityService').mockImplementation(() => value);

      const { container } = render(
        <EnsureAuthentication>
          <div id="test" />
        </EnsureAuthentication>,
      );

      await act(async () => {
        await flushPromises();
      });

      expect(container.querySelector('#test')).not.toBeInTheDocument();
      expect(
        container.querySelector('[data-testid="config-invalid-error"]'),
      ).toBeInTheDocument();
    },
  );

  it('renders an account inactive error if the account is not active', async () => {
    const value = {
      ...successValue,
      getToken: () => ({
        code: TokenResponseCode.ACCOUNT_NOT_ACTIVE,
      }),
    };
    vi.spyOn(ISP, 'useIdentityService').mockImplementation(() => value);

    const { container } = render(
      <EnsureAuthentication>
        <div id="test" />
      </EnsureAuthentication>,
    );

    await act(async () => {
      await flushPromises();
    });

    expect(container.querySelector('#test')).not.toBeInTheDocument();
    expect(
      container.querySelector('[data-testid="account-inactive-error"]'),
    ).toBeInTheDocument();
  });
});
