/**
 * @jest-environment node
 */
import { renderToString } from 'react-dom/server';

import en from './i18n/en.json';

import * as exposedLibraryItems from '.';

type ExposedLibraryItemsType = typeof exposedLibraryItems;
type ExposedLibraryKeysType = keyof ExposedLibraryItemsType;

const excluded = new Set<ExposedLibraryKeysType | string>([
  // hooks
  'useSnackbar',
  'useDirection',
  'useLayout',
  'useScreenSize',

  // specific components
  'Provider',
  'BottomSheet',
  'DropFade',
  'SelectInputTriggerButton',
  'Panel',
  'ResponsivePanel',

  // enums
  'Size',
  'Width',
  'Sentiment',
  'Variant',
  'Priority',
  'ControlType',
  'Type',
  'Theme',
  'DateMode',
  'MonthFormat',
  'Position',
  'Scroll',
  'ProfileType',
  'Breakpoint',
  'Layout',
  'Status',
  'MarkdownNodeType',
  'AvatarType',
  'InfoPresentation',
  'UploadStep',
  'DecisionPresentation',
  'DecisionType',
  'AlertArrowPosition',
  'LogoType',
  'FileType',
  'Direction',
  'Typography',

  // types
  'SearchInputProps',
  'SelectInputItem',
  'SelectInputOptionContentProps',
  'SelectInputProps',
  'UploadedFile',
  'UploadError',
  'UploadResponse',
  'LayoutDirection',

  // utils
  'SUPPORTED_LANGUAGES',
  'RTL_LANGUAGES',
  'DEFAULT_LANG',
  'DEFAULT_LOCALE',
  'adjustLocale',
  'getLangFromLocale',
  'getCountryFromLocale',
  'getDirectionFromLocale',
  'getLocaleCurrencyName',
  'isServerSide',
  'isBrowser',

  // other
  'translations',
]);

function isNotExcluded(item: ExposedLibraryKeysType) {
  return !excluded.has(item);
}

describe('Server side rendering', () => {
  const componentNames = (
    Object.keys(exposedLibraryItems) as (keyof ExposedLibraryItemsType)[]
  ).filter((moduleName) => isNotExcluded(moduleName));

  // stick all possible properties we might need to render all components in here
  const allProps: Record<string, unknown> = {
    actionPrimary: { label: 'Action', onClick: jest.fn(), href: '#' },
    currencies: [],
    currencySelector: { options: [] },
    steps: [],
    stepper: {
      steps: [],
    },
    items: [],
    children: 'yo',
    id: '1',
    text: 'test',
    timeout: 0,
    timestamp: 1,
    title: 'trolo',
    name: 'lolo',
    label: 'hello',
    content: 'world',
    currency: 'XYZ',
    amount: 0,
    options: [],
    model: {},
    fields: {},
    media: <h1>Hello</h1>,
    onClick: jest.fn(),
    onChange: jest.fn(),
    status: 'processing',
    size: 'sm',
    body: 'body',
    onClose: jest.fn(),
    onDismiss: jest.fn(),
    onRemove: jest.fn(),
    radios: [
      {
        id: 'id-test-0',
        label: 'Radio1',
      },
      {
        id: 'id-test-0',
        label: 'Radio1',
      },
    ],
    position: 'left',
    open: true,
    tabs: [],
    direction: {
      xs: 'column',
      sm: 'row',
      md: 'column',
      lg: 'row',
    },
    alt: '',
    src: '',
    details: 'yo',
    icon: <svg />,
    badge: <svg />,
    link: 'link',
    href: '#',
    description: 'description',
    'aria-label': 'a label',
    logo: <svg />,
    chips: [
      {
        value: 1,
        label: 'One',
      },
      {
        value: 2,
        label: 'Two',
      },
    ],
    segments: [
      {
        id: '1',
        value: 'accounting',
        label: 'Accounting',
      },
      {
        id: '2',
        value: 'payroll',
        label: 'Payroll',
      },
      {
        id: '3',
        value: 'reporting',
        label: 'Reporting',
      },
    ],
  };

  // Override props in case of name collision.
  const overrideProps: Partial<Record<keyof ExposedLibraryItemsType, Record<string, unknown>>> = {
    ActionPrompt: { action: { label: 'Action', onClick: jest.fn(), href: '#' } },
    Alert: { children: undefined, message: 'Fluffy kittens', size: undefined },
    Card: { isExpanded: true },
    CheckboxButton: { children: undefined, onChange: jest.fn() },
    Typeahead: { size: 'md' },
    InputWithDisplayFormat: { displayPattern: '**-**' },
    TextareaWithDisplayFormat: { displayPattern: '**-**' },
    Sticky: { position: 'top' },
    MoneyInput: { selectedCurrency: { currency: 'EUR' } },
    Tabs: { selected: 1, onTabSelect: jest.fn() },
    Markdown: { children: '# Markdown string' },
    SnackbarConsumer: {
      children: jest.fn(),
    },
    SnackbarContext: {
      children: jest.fn(),
    },
    Popover: {
      children: <div />,
    },
    Progress: {
      progress: { value: 50, max: 100 },
    },
    ProgressBar: {
      progress: { value: 50, max: 100 },
    },
    Field: {
      model: 'a model',
      type: 'text',
      children: <input />,
    },
    Input: {
      children: undefined,
    },
    SearchInput: {
      children: undefined,
    },
    TextArea: {
      children: undefined,
    },
    Summary: {
      status: 'done',
      content: undefined,
    },
    Modal: { position: 'top' },
    ActionOption: { action: 'hello' },
    DateLookup: { value: new Date() },
    Link: { size: 16 },
    Tooltip: { children: <>yo</> },
  };

  const { Provider } = exposedLibraryItems;
  type ComponentWithProvider = React.ComponentType<unknown> & {
    Provider?: React.ComponentType<unknown>;
  };

  componentNames.forEach((componentName: ExposedLibraryKeysType) => {
    it(`can render component: ${componentName}`, () => {
      const Component = exposedLibraryItems[componentName] as ComponentWithProvider;
      const newProps = { ...allProps };

      Object.keys(overrideProps[componentName] ?? {}).forEach((propertyToOverrideKey) => {
        const key = propertyToOverrideKey as keyof (typeof overrideProps)[typeof componentName];
        newProps[key] = overrideProps?.[componentName]?.[key];
      });

      const view = renderToString(
        <Provider i18n={{ locale: 'en-GB', messages: en }}>
          {componentName.endsWith('Context') && Component.Provider ? (
            <Component.Provider {...newProps} />
          ) : (
            <div>
              <Component {...newProps} />
            </div>
          )}
        </Provider>,
      );

      expect(view).toStrictEqual(expect.any(String));
    });
  });
});
