import type {
  CreateCardForTemporaryUsePayload,
  DuffelCardFormActions,
  DuffelCardFormProps,
  SaveCardPayload,
  UseSavedCardPayload,
} from '../../index';
import React from 'react';
import { CreateCardForTemporaryUseForm } from './forms/CreateCardForTemporaryUseForm';
import { SaveCardForm } from './forms/SaveCardForm';
import { UseSavedCardForm } from './forms/UseSavedCardForm';
import { DEFAULT_STRINGS } from './strings';
import { getDuffelCardsAPIClient } from './utils/getDuffelCardsAPIClient';
import type { VirtualFields } from './utils/validateFieldFn';

export const DuffelCardForm = React.forwardRef<
  DuffelCardFormActions,
  DuffelCardFormProps
>(
  (
    {
      clientKey,
      intent,
      savedCardData,
      tokenProxyEnvironment = 'production',
      styles,
      onValidateSuccess,
      onValidateFailure,
      onCreateCardForTemporaryUseSuccess,
      onCreateCardForTemporaryUseFailure,
      onSaveCardSuccess,
      onSaveCardFailure,
      customStrings = {},
    },
    ref
  ) => {
    // =======
    // Validate required prop
    if (!clientKey) {
      throw new Error(
        'Attempted to render `DuffelCardForm` without a clientKey.'
      );
    }

    if (
      !intent ||
      ![
        'to-create-card-for-temporary-use',
        'to-use-saved-card',
        'to-save-card',
      ].includes(intent)
    ) {
      throw new Error(
        'Attempted to render `DuffelCardForm` without an `intent`. Make sure your provide one of the following: `create-card-for-temporary-use`, `save-card`, `use-saved-card`.'
      );
    }

    if (
      intent === 'to-create-card-for-temporary-use' &&
      !onCreateCardForTemporaryUseSuccess
    ) {
      throw new Error(
        'Attempted to render `DuffelCardForm` with intent `to-create-card-for-temporary-use` but `onCreateCardForTemporaryUseSuccess` prop is missing. Make sure you provide a function to handle the success case.'
      );
    }
    if (
      intent === 'to-create-card-for-temporary-use' &&
      !onCreateCardForTemporaryUseFailure
    ) {
      throw new Error(
        'Attempted to render `DuffelCardForm` with intent `to-create-card-for-temporary-use` but `onCreateCardForTemporaryUseFailure` prop is missing. Make sure you provide a function to handle the failure case.'
      );
    }

    if (intent === 'to-save-card' && !onSaveCardSuccess) {
      throw new Error(
        'Attempted to render `DuffelCardForm` with intent `to-save-card` but `onSaveCardSuccess` prop is missing. Make sure you provide a function to handle the success case.'
      );
    }
    if (intent === 'to-save-card' && !onSaveCardFailure) {
      throw new Error(
        'Attempted to render `DuffelCardForm` with intent `to-save-card` but `onSaveCardFailure` prop is missing. Make sure you provide a function to handle the failure case.'
      );
    }

    if (intent === 'to-use-saved-card' && !savedCardData) {
      throw new Error(
        "Attempted to render `DuffelCardForm`to use a saved card but the `savedCardData` prop is missing. Make sure you provide the id of the saved card you'd like to use."
      );
    }
    if (intent === 'to-use-saved-card' && !onValidateSuccess) {
      throw new Error(
        'Attempted to render `DuffelCardForm` with intent `to-use-saved-card` but `onValidateSuccess` prop is missing. Make sure you provide a function to handle the success case.'
      );
    }
    if (intent === 'to-use-saved-card' && !onValidateFailure) {
      throw new Error(
        'Attempted to render `DuffelCardForm` with intent `to-use-saved-card` but `onValidateFailure` prop is missing. Make sure you provide a function to handle the failure case.'
      );
    }

    // =======
    // Validate custom strings prop
    const nonStringFields = Object.entries(customStrings).filter(
      ([_customStringsKeys, value]) => typeof value !== 'string'
    );

    if (nonStringFields.length >= 1) {
      throw new Error(
        `The following 'customStrings' prop values are not strings: ${nonStringFields.map(([key]) => key).join(', ')}`
      );
    }

    const unknownFields = Object.keys(customStrings).filter(
      (customStringsKeys) =>
        !Object.keys(DEFAULT_STRINGS).includes(customStringsKeys)
    );
    if (unknownFields.length >= 1) {
      console.warn(
        `The following custom strings are not recognized: ${unknownFields.join(', ')}`
      );
    }

    const strings = {
      ...DEFAULT_STRINGS,
      ...customStrings,
    };

    // =======
    // Sets up form state
    const [
      createCardForTemporaryUseFormData,
      onChangeCreateCardForTemporaryUseFormData,
    ] = React.useState<CreateCardForTemporaryUsePayload & VirtualFields>({
      number: '',
      expiry_month: '',
      expiry_year: '',
      expiry_date: null,
      cvc: '',
      name: '',
      address_line_1: '',
      address_line_2: '',
      address_city: '',
      address_region: '',
      address_postal_code: '',
      address_country_code: '',
      brand: '',
      multi_use: false,
    });

    const [saveCardFormData, onChangeSaveCardFormData] = React.useState<
      SaveCardPayload & VirtualFields
    >({
      number: '',
      expiry_month: '',
      expiry_year: '',
      expiry_date: null,
      name: '',
      address_line_1: '',
      address_line_2: '',
      address_city: '',
      address_region: '',
      address_postal_code: '',
      address_country_code: '',
      brand: '',
      multi_use: true,
    });

    const [useSavedCardFormData, onChangeUseSavedCardFormData] =
      React.useState<UseSavedCardPayload>({
        card_id: savedCardData?.id ?? '',
        brand: savedCardData?.brand ?? '',
        cvc: '',
      });

    // =======
    // Sets up messages that should be pushed into the iframe
    React.useImperativeHandle(ref, () => {
      const client = getDuffelCardsAPIClient(tokenProxyEnvironment, clientKey);
      return {
        saveCard: async () => {
          if (typeof onSaveCardSuccess !== 'function') {
            throw new Error(
              'Attempted to call `saveCard` but prop `onSaveCardSuccess` is not a function.'
            );
          }

          if (typeof onSaveCardFailure !== 'function') {
            throw new Error(
              'Attempted to call `saveCard` but prop `onSaveCardFailure` is not a function.'
            );
          }

          if (intent !== 'to-save-card') {
            throw new Error(
              'Attempted to call `saveCard`. But this function is only supported for intent: `to-save-card`'
            );
          }

          try {
            const response = await client.saveCard(saveCardFormData);
            if ('errors' in response) {
              onSaveCardFailure(response);
            } else {
              onSaveCardSuccess(response.data);
            }
          } catch (error) {
            onSaveCardFailure(error as Error);
          }
        },
        createCardForTemporaryUse: async () => {
          if (typeof onCreateCardForTemporaryUseSuccess !== 'function') {
            throw new Error(
              'Attempted to call `createCardForTemporaryUse` but prop `onCreateCardForTemporaryUseSuccess` is not a function.'
            );
          }

          if (typeof onCreateCardForTemporaryUseFailure !== 'function') {
            throw new Error(
              'Attempted to call `createCardForTemporaryUse` but prop `onCreateCardForTemporaryUseFailure` is not a function.'
            );
          }

          if (
            intent !== 'to-use-saved-card' &&
            intent !== 'to-create-card-for-temporary-use'
          ) {
            throw new Error(
              'Attempted to call `createCardForTemporaryUse`. But this function is only supported for intents: `to-create-card-for-temporary-use` or `to-use-saved-card`'
            );
          }

          try {
            const response =
              intent === 'to-use-saved-card'
                ? await client.useSavedCard(useSavedCardFormData)
                : await client.createCardForTemporaryUse(
                    createCardForTemporaryUseFormData
                  );

            if ('errors' in response) {
              onCreateCardForTemporaryUseFailure(response);
            } else {
              onCreateCardForTemporaryUseSuccess(response.data);
            }
          } catch (error) {
            onCreateCardForTemporaryUseFailure(error as Error);
          }
        },
      };
    }, [
      intent,
      tokenProxyEnvironment,
      clientKey,
      saveCardFormData,
      useSavedCardFormData,
      createCardForTemporaryUseFormData,
      onCreateCardForTemporaryUseSuccess,
      onCreateCardForTemporaryUseFailure,
      onSaveCardSuccess,
      onSaveCardFailure,
    ]);

    if (intent === 'to-create-card-for-temporary-use') {
      return (
        <CreateCardForTemporaryUseForm
          styles={styles}
          data={createCardForTemporaryUseFormData}
          onChange={onChangeCreateCardForTemporaryUseFormData}
          onValidateSuccess={onValidateSuccess}
          onValidateFailure={onValidateFailure}
          customStrings={strings}
        />
      );
    }

    if (intent === 'to-save-card') {
      return (
        <SaveCardForm
          styles={styles}
          data={saveCardFormData}
          onChange={onChangeSaveCardFormData}
          onValidateSuccess={onValidateSuccess}
          onValidateFailure={onValidateFailure}
          customStrings={strings}
        />
      );
    }

    if (intent === 'to-use-saved-card') {
      return (
        <UseSavedCardForm
          data={useSavedCardFormData}
          onChange={onChangeUseSavedCardFormData}
          onValidateSuccess={onValidateSuccess}
          onValidateFailure={onValidateFailure}
          inputStyle={styles?.input}
          formFieldStyle={styles?.formField}
          errorMessageStyle={styles?.errorMessage}
          customStrings={strings}
        />
      );
    }

    // Should never reach this case
    return null;
  }
);
