// Copyright © 2022 Olo Inc. All rights reserved.
// This software is made available under the Olo Pay SDK License (See LICENSE.md file)
import React, {
  forwardRef,
  useCallback,
  useImperativeHandle,
  useRef,
  useState,
} from 'react';

import {
  HostComponent,
  NativeSyntheticEvent,
  requireNativeComponent,
  StyleSheet,
  Text,
  View,
} from 'react-native';

import type {
  CardDetails,
  PaymentCardDetailsViewMethods,
  PaymentCardDetailsViewProps,
  PaymentMethod,
  PromiseRejection,
} from '../definitions';

import type {
  FocusFieldDetails,
  CardDetailsNativeProps,
} from '../components/definitions/PaymentCardDetailsViewNativeTypes';

import {
  sharedComponentStyles,
  sharedErrorStyles,
} from './styles/sharedStyles';

import usePaymentCardDetailsViewManager from '../hooks/usePaymentCardDetailsViewManager';
import { sharedComponentValues } from './definitions/sharedValues';

//prettier-ignore
const CardDetailsNative: HostComponent<CardDetailsNativeProps> = requireNativeComponent('PaymentCardDetailsView');
const ViewManagerMethods = usePaymentCardDetailsViewManager();

//prettier-ignore
export const PaymentCardDetailsView = forwardRef<PaymentCardDetailsViewMethods, PaymentCardDetailsViewProps>(
  (
    {
      componentStyles,
      cardStyles,
      errorStyles,
      viewProps,
      onCardChange,
      onFocus,
      onBlur,
      onFocusField,
      postalCodeEnabled,
      isEnabled,
      customErrorMessages,
      displayErrorMessages,
      placeholders,
      ...props
    },
    ref: any
  ) => {
    const [displayedError, setDisplayedError] = useState("");
    const [allFieldsError, setAllFieldsError] = useState("");
    const inputRef = useRef(null);

    const onCardChangeHandler = useCallback((event: NativeSyntheticEvent<CardDetails>) => {
      const cardEvent = event.nativeEvent;

      const cardData: CardDetails = {
        isValid: cardEvent.isValid,
        cardType: cardEvent.cardType,
        invalidFields: cardEvent.invalidFields,
        emptyFields: cardEvent.emptyFields,
        errors: cardEvent.errors,
      };

      setAllFieldsError(cardEvent.errors?.allFieldsError ?? "");
      setDisplayedError(cardEvent.errors?.editedFieldsError ?? "");
      onCardChange?.(cardData);
    },
      [onCardChange, allFieldsError, displayedError]
    );


    const onFocusEventHandler = useCallback((_event: NativeSyntheticEvent<void>) => {
      onFocus?.();
    },
      [onFocus]
    );

    const onBlurEventHandler = useCallback((_event: NativeSyntheticEvent<void>) => {
      onBlur?.();
    },
      [onBlur]
    );

    const onFocusFieldEventHandler = useCallback((event: NativeSyntheticEvent<FocusFieldDetails>) => {
      onFocusField?.(event.nativeEvent.field);
    },
      [onFocusField]
    );

    const clear = async (): Promise<void> => {
      return await ViewManagerMethods.clear(inputRef);
    };

    const createPaymentMethod = async (): Promise<PaymentMethod> => {
      try {
        return await ViewManagerMethods.createPaymentMethod(inputRef);
      } catch (error) {
        let rejection = error as PromiseRejection;
        if (rejection) {
          setDisplayedError(rejection.message);
        } else {
          setDisplayedError(allFieldsError);
        }

        throw error;
      }
    };

    const blur = async (): Promise<void> => {
      return await ViewManagerMethods.blur(inputRef);
    };

    const focus = async (): Promise<void> => {
      return await ViewManagerMethods.focus(inputRef);
    };

    useImperativeHandle(ref, () => ({
      clear,
      createPaymentMethod,
      blur,
      focus,
    }));

    return (
      <View
        style={componentStyles}
        {...viewProps}>
        <CardDetailsNative
          style={[defaultStyles.nativeCard, cardStyles?.styles]}
          ref={inputRef}
          onCardChangeEvent={onCardChangeHandler}
          onFocusEvent={onFocusEventHandler}
          onBlurEvent={onBlurEventHandler}
          onFocusFieldEvent={onFocusFieldEventHandler}
          postalCodeEnabled={postalCodeEnabled ?? true}
          isEnabled={isEnabled ?? true}
          customErrorMessages={customErrorMessages}
          placeholders={{
            number: placeholders?.number ?? sharedComponentValues.defaultValues.singleLineNumberPlaceholder,
            expiration: placeholders?.expiration ?? sharedComponentValues.defaultValues.expirationPlaceholder,
            cvv: placeholders?.cvv ?? sharedComponentValues.defaultValues.cvvPlaceholder,
            postalCode: placeholders?.postalCode ?? sharedComponentValues.defaultValues.postalCodePlaceholder,
          }}
          cardStyles={{
            borderWidth: cardStyles?.borderWidth ?? sharedComponentStyles.defaultStyles.borderWidth,
            backgroundColor: cardStyles?.backgroundColor ?? sharedComponentStyles.defaultStyles.backgroundColor,
            borderColor: cardStyles?.borderColor ?? sharedComponentStyles.defaultStyles.borderColor,
            cornerRadius: cardStyles?.cornerRadius ?? sharedComponentStyles.defaultStyles.cornerRadius,
            cursorColor: cardStyles?.cursorColor ?? sharedComponentStyles.defaultStyles.cursorColor,
            errorTextColor: cardStyles?.errorTextColor ?? sharedComponentStyles.defaultStyles.errorTextColor,
            fontFamily: cardStyles?.fontFamily ?? sharedComponentStyles.defaultStyles.fontFamily,
            fontSize: cardStyles?.fontSize ?? sharedComponentStyles.defaultStyles.fontSize,
            placeholderColor: cardStyles?.placeholderColor ?? sharedComponentStyles.defaultStyles.placeholderColor,
            textPaddingLeft: cardStyles?.textPaddingLeft ?? sharedComponentStyles.defaultStyles.paddingLeft,
            textPaddingRight: cardStyles?.textPaddingRight ?? sharedComponentStyles.defaultStyles.paddingRight,
            textColor: cardStyles?.textColor ?? sharedComponentStyles.defaultStyles.textColor,
            fontWeight: cardStyles?.fontWeight ?? sharedComponentStyles.defaultStyles.fontWeight,
            italic: cardStyles?.italic ?? sharedComponentStyles.defaultStyles.italic,
          }}
          {...props}
        />
        <Text style={[sharedErrorStyles.defaultStyles, errorStyles]}>{displayErrorMessages ? displayedError : ""}</Text>
      </View>
    );
  }
);

const defaultStyles = StyleSheet.create({
  nativeCard: {
    minHeight: 50,
  },
});
