// 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 {
  CardField,
  type CardDetails,
  type OloError,
  type PaymentCardDetailsViewMethods,
  type PaymentCardDetailsViewProps,
  type PaymentMethod,
  type PaymentMethodResult,
} 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';

// Fabric-only architecture
const CardDetailsNative: HostComponent<CardDetailsNativeProps> =
  require('./specs/PaymentCardDetailsViewNativeComponent').default;

//prettier-ignore
export const PaymentCardDetailsView = forwardRef<PaymentCardDetailsViewMethods, PaymentCardDetailsViewProps>(
  (
    {
      componentStyles,
      cardStyles,
      errorStyles,
      viewProps,
      onCardChange,
      onFocus,
      onBlur,
      onFocusField,
      onPaymentMethodResult,
      postalCodeEnabled,
      disabled,
      customErrorMessages,
      displayErrorMessages,
      placeholders,
      ...props
    },
    ref: any
  ) => {
    const ViewManagerMethods = usePaymentCardDetailsViewManager();
    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]
    );

    // Handle payment method result event
    const onPaymentMethodResultHandler = useCallback((event: NativeSyntheticEvent<{paymentMethod?: PaymentMethod; error?: OloError}>) => {
      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 hasPaymentMethod = eventData.paymentMethod?.id;

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

      // Update error display if there's an error
      if (hasError) {
        setDisplayedError(eventData.error?.message ?? "");
      }

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

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

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

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

    const focus = (field: CardField = CardField.number): void => {
      ViewManagerMethods.focus(inputRef, field);
    };

    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}
          onPaymentMethodResultEvent={onPaymentMethodResultHandler}
          postalCodeEnabled={postalCodeEnabled ?? true}
          isEnabled={!disabled}
          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,
  },
});
