// 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 {
  type HostComponent,
  type NativeSyntheticEvent,
  StyleSheet,
  Text,
  View,
} from 'react-native';

import {
  OloErrorCode,
  type CvvDetails,
  type CvvTokenResult,
  type PaymentCardCvvViewMethods,
  type PaymentCardCvvViewProps,
} from '../definitions';

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

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

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

// Fabric-only architecture
const CardCvvNative: HostComponent<CvvDetailsNativeProps> =
  require('./specs/PaymentCardCvvViewNativeComponent').default;

//prettier-ignore
export const PaymentCardCvvView = forwardRef<PaymentCardCvvViewMethods, PaymentCardCvvViewProps>(
  (
    {
      componentStyles,
      displayErrorMessages,
      cvvStyles,
      errorStyles,
      viewProps,
      disabled,
      customErrorMessages,
      placeholder,
      onCvvChange,
      onFocus,
      onBlur,
      onCvvTokenResult,
      ...props },
    ref: any
  ) => {
    const ViewManagerMethods = usePaymentCardCvvViewManager();
    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 ?? "");
    }

    // Handle CVV token result event
    const onCvvTokenResultHandler = useCallback((event: NativeSyntheticEvent<CvvTokenResult>) => {
      const eventData = event.nativeEvent;

      // Check for actual content (Fabric may send empty objects for unset optionals)
      const hasError = eventData.error?.code || eventData.error?.message;
      const hasToken = eventData.token?.id;

      // Normalize the result - only include error/token if they have actual content
      const normalizedResult: CvvTokenResult = {
        ...(hasToken ? { token: eventData.token } : {}),
        ...(hasError ? { error: eventData.error } : {}),
      };

      // Update error display if there's an error
      if (hasError) {
        if (eventData.error?.code === OloErrorCode.invalidCvv) {
          setDisplayedError(eventData.error?.message ?? "");
        } else {
          setDisplayedError(uneditedFieldError);
        }
      }

      // Call the user's callback with normalized result
      onCvvTokenResult?.(normalizedResult);
    }, [onCvvTokenResult, uneditedFieldError]);

    const clear = (): void => {
      ViewManagerMethods.clear(inputRef);
    };

    const createCvvUpdateToken = (): void => {
      ViewManagerMethods.createCvvUpdateToken(inputRef);
    };

    const blur = (): void => {
      ViewManagerMethods.blur(inputRef);
    };

    const focus = (): void => {
      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}
          onCvvTokenResultEvent={onCvvTokenResultHandler}
          isEnabled={!disabled}
          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,
  },
});
