// 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 {
  PromiseRejectionCode,
  type CvvDetails,
  type CvvUpdateToken,
  type PaymentCardCvvViewMethods,
  type PaymentCardCvvViewProps,
  type PromiseRejection,
} from '../definitions';

import type { CvvDetailsNativeProps } from './definitions/PaymentCardCvvViewNativeTypes';

import usePaymentCardCvvViewManager from '../hooks/usePaymentCardCvvViewManager';

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

//prettier-ignore
const CardCvvNative: HostComponent<CvvDetailsNativeProps> = requireNativeComponent('PaymentCardCvvView');
const ViewManagerMethods = usePaymentCardCvvViewManager();

//prettier-ignore
export const PaymentCardCvvView = forwardRef<PaymentCardCvvViewMethods, PaymentCardCvvViewProps>(
  (
    { 
      componentStyles, 
      displayErrorMessages,
      cvvStyles,
      errorStyles,
      viewProps, 
      isEnabled,
      customErrorMessages,
      placeholder,
      onCvvChange,
      onFocus,
      onBlur,
      ...props },
    ref: any
  ) => {
    const [displayedError, setDisplayedError] = useState("");
    const [uneditedFieldError, setUneditedFieldError] = useState("");
    const inputRef = useRef(null);
    
    const onCvvChangeHandler = useCallback((event: NativeSyntheticEvent<CvvDetails>) => {
        const cvvDetails = {
          state: event.nativeEvent.state,
          errors: event.nativeEvent.errors
        };
        updateErrorState(cvvDetails)
        onCvvChange?.(cvvDetails);
      },
      [onCvvChange, uneditedFieldError, displayedError]
    );
  
    const onFocusEventHandler = useCallback((event: NativeSyntheticEvent<CvvDetails>) => {
        const cvvDetails = {
          state: event.nativeEvent.state,
          errors: event.nativeEvent.errors
        };
        updateErrorState(cvvDetails)
        onFocus?.(cvvDetails);
      },
      [onFocus, uneditedFieldError, displayedError]
    );

    const onBlurEventHandler = useCallback((event: NativeSyntheticEvent<CvvDetails>) => {
        const cvvDetails = {
          state: event.nativeEvent.state,
          errors: event.nativeEvent.errors
        };
        updateErrorState(cvvDetails)
        onBlur?.(cvvDetails);
      },
      [onBlur, uneditedFieldError, displayedError]
    );

    const updateErrorState = (details: CvvDetails) => {
      setUneditedFieldError(details.errors?.uneditedFieldError ?? "");
      setDisplayedError(details.errors?.editedFieldError ?? "");
    }

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

    const createCvvUpdateToken = async (): Promise<CvvUpdateToken> => {
      try {
        return await ViewManagerMethods.createCvvUpdateToken(inputRef);
      } catch (error) {
        let rejection = error as PromiseRejection;
        if (rejection?.code === PromiseRejectionCode.invalidCvv) {
          setDisplayedError(rejection.message);
        } else {
          setDisplayedError(uneditedFieldError);
        }

        throw error;
      }
    };

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

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

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

    return (
      <View style={componentStyles} {...viewProps}>
        <CardCvvNative
          style={[defaultStyles.nativeCard, cvvStyles?.styles]}
          ref={inputRef}
          onCvvChangeEvent={onCvvChangeHandler}
          onFocusEvent={onFocusEventHandler}
          onBlurEvent={onBlurEventHandler}
          isEnabled={isEnabled ?? true}
          customErrorMessages={customErrorMessages}
          placeholder={placeholder}
          cvvStyles={{
            borderWidth: cvvStyles?.borderWidth ?? sharedComponentStyles.defaultStyles.borderWidth,
            backgroundColor: cvvStyles?.backgroundColor ?? sharedComponentStyles.defaultStyles.backgroundColor,
            borderColor: cvvStyles?.borderColor ?? sharedComponentStyles.defaultStyles.borderColor,
            cornerRadius: cvvStyles?.cornerRadius ?? sharedComponentStyles.defaultStyles.cornerRadius,
            cursorColor: cvvStyles?.cursorColor ?? sharedComponentStyles.defaultStyles.cursorColor,
            errorTextColor: cvvStyles?.errorTextColor ?? sharedComponentStyles.defaultStyles.errorTextColor,
            fontFamily: cvvStyles?.fontFamily ?? sharedComponentStyles.defaultStyles.fontFamily,
            fontSize: cvvStyles?.fontSize ?? sharedComponentStyles.defaultStyles.fontSize,
            placeholderColor: cvvStyles?.placeholderColor ?? sharedComponentStyles.defaultStyles.placeholderColor,
            textPaddingLeft: cvvStyles?.textPaddingLeft ?? sharedComponentStyles.defaultStyles.paddingLeft,
            textPaddingRight: cvvStyles?.textPaddingRight ?? sharedComponentStyles.defaultStyles.paddingRight,
            textColor: cvvStyles?.textColor ?? sharedComponentStyles.defaultStyles.textColor,
            fontWeight: cvvStyles?.fontWeight ?? sharedComponentStyles.defaultStyles.fontWeight,
            italic: cvvStyles?.italic ?? sharedComponentStyles.defaultStyles.italic,
            textAlign: cvvStyles?.textAlign ?? "left",
          }}
          {...props}
        />
        <Text style={[sharedErrorStyles.defaultStyles, errorStyles]}>{displayErrorMessages ? displayedError : ""}</Text>
      </View>
    );
  }
);

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