import React, { useRef, useState } from 'react';
import { WebComponent } from '../../webComponent/WebComponent';
import type { WebViewMessage } from '../../messages/webMessages';
import type {
  UNComponentsOnLoadResponse, 
  UNComponentsError, 
  UNComponentsOnLoadResponseData, 
  UNACHOnLoadData, 
  UNACHPayment, 
  UNPlaidAccountFilter, 
  UNCounterparty,
  UNAccount
} from '../../types/shared';
import { HeightEvent, PageMessage } from '../../messages/webMessages/pageMessage';
import {
  PlaidLinkTokenData,
  PlaidTokenCreatedEvent,
  RequestExternalSdkEvent,
  RequestRenderingEvent,
  UnitComponentsMessage,
} from '../../messages/webMessages/unitComponentsMessages';
import { getACHDebitParams, getACHDebitScript, injectRequestCounterpartyDeletedScript, injectUnitPlaidResponse } from './UNACHDebitComponent.utils';
import { LinkExit, LinkSuccess, open as openPlaidLink, create as createPlaidLink } from 'react-native-plaid-link-sdk';
import type WebView from 'react-native-webview';
import { PaymentMessage } from '../../messages/webMessages/paymentsMessage';
import type { ACHEvent } from '../../messages/webMessages/achCreditMessage';
import { ACHOnLoadResponse, RESPONSE_KEYS, UnitOnLoadResponseEvent } from '../../messages/webMessages/onLoadMessage';
import { PresentationMode, WebComponentType } from '../../types/internal/webComponent.types';
import { withReduxStore } from '../../helpers/store/helpers';
import { PlaidMessage } from '../../messages/webMessages/plaidMessages';
import { UNBaseView } from '../../nativeComponents/UNBaseView';
import { ensureArray } from '../../utils/onLoadMessages.utils';
import { BottomSheetRenderingType, BottomSheetSlotData, SlotRendering } from '../../types/internal/bottomSheet.types';
import { BottomSheetNativeMessage } from '../../messages/nativeMessages/bottomSheetMessage';
import { eventBus } from '../../utils/eventBus';
import { useEventListener } from '../../hooks/useEventListener';
import { PayeeManagementMessage } from '../../messages/webMessages/payeeManagementMessage';

export interface UNACHDebitComponentProps {
  accountId?: string;
  theme?: string;
  language?: string;
  isAutoFocus?: boolean;
  sameDay?: boolean;
  showSameDaySelection?: boolean;
  onPaymentCreated?: (data: UNACHPayment) => void;
  onLoad?: (response: UNComponentsOnLoadResponse<UNACHOnLoadData>) => void;
  onInitialStageBackButtonClicked?: () => void;
  onFinalStageDoneButtonClicked?: () => void;
  plaidAccountFilters?: UNPlaidAccountFilter[];
  plaidLinkCustomizationName?: string;
  initialStageBackButton?: boolean;
  finalStageDoneButton?: boolean;
  allowCounterpartyDelete?: boolean;
}

const UNACHDebitComponent = (props: UNACHDebitComponentProps) => {
  const [height, setHeight] = useState<number>(0);
  const webRef = useRef<WebView>(null);
  const [presentationMode, setPresentationMode] = useState<PresentationMode>(PresentationMode.Inherit);

  const handleCounterpartyDeleted = (data: UNCounterparty) => {
    injectRequestCounterpartyDeletedScript(webRef.current, data);
  };
  
  useEventListener({ busEventKey: PayeeManagementMessage.UNIT_COUNTERPARTY_DELETED, action: handleCounterpartyDeleted });

  const handleUnitOnLoad = (response: UnitOnLoadResponseEvent) => {
    if (!props.onLoad) {
      return;
    }

    if (RESPONSE_KEYS.errors in response) {
      props.onLoad(response as UNComponentsError);
      return;
    }

    if (RESPONSE_KEYS.account in response && RESPONSE_KEYS.counterpartyAccount in response) {
      const account = response[RESPONSE_KEYS.account].data;
      // ACHOnLoadResponse;
      const achOnload: UNComponentsOnLoadResponseData<UNACHOnLoadData> = {
        data: {
          [RESPONSE_KEYS.account]: ensureArray(account),
          [RESPONSE_KEYS.counterpartyAccount]: response[RESPONSE_KEYS.counterpartyAccount].data,
        },
      };
      props.onLoad(achOnload);
      return;
    }

    if (RESPONSE_KEYS.counterpartiesResult in response) {
      const counterparties = response[RESPONSE_KEYS.counterpartiesResult].data;
      const accountResponse = response as Partial<ACHOnLoadResponse>;
      const accountData = accountResponse[RESPONSE_KEYS.account]?.data;
      const achOnload: UNComponentsOnLoadResponseData<UNACHOnLoadData> = {
        data: {
          [RESPONSE_KEYS.account]: (accountData ? ensureArray(accountData) : []) as [UNAccount],
          [RESPONSE_KEYS.counterpartyAccount]: counterparties,
        },
      };
      props.onLoad(achOnload);
      return;
    }

    console.error('On Load Error: unexpected response type');
    return;
  };

  const handleWebViewMessage = (message: WebViewMessage) => {
    if (!message || !message.details) return;

    switch (message.type) {
      case PaymentMessage.INITIAL_STAGE_BACK_BUTTON_CLICKED: {
        props.onInitialStageBackButtonClicked && props.onInitialStageBackButtonClicked();
        break;
      }
      case PaymentMessage.FINAL_STAGE_DONE_BUTTON_CLICKED: {
        props.onFinalStageDoneButtonClicked && props.onFinalStageDoneButtonClicked();
        break;
      }
      case PaymentMessage.PAYMENT_CREATED: {
        const achDebitEvent = message.details as ACHEvent;
        props.onPaymentCreated && props.onPaymentCreated(achDebitEvent.data as UNACHPayment);
        break;
      }
      case UnitComponentsMessage.UNIT_ON_LOAD:
        handleUnitOnLoad(message.details as UnitOnLoadResponseEvent);
        break;
      case UnitComponentsMessage.UNIT_REQUEST_RENDERING: {
        const slotData: BottomSheetSlotData = {
          componentName: WebComponentType.achDebitPayment,
          componentResourceId: props.accountId,
          requestRenderingEvent: message.details as RequestRenderingEvent,
        };
      
        const data = {
          type: BottomSheetRenderingType.Slot,
          data: slotData,
        } as SlotRendering;
        eventBus.emit(BottomSheetNativeMessage.REQUEST_RENDERING, data);
      
        break;
      }
      case PageMessage.PAGE_HEIGHT: {
        const currentHeight = (message.details as HeightEvent).height;
        setHeight(currentHeight);
        if (presentationMode === PresentationMode.Inherit && currentHeight === 0) {
          setPresentationMode(PresentationMode.Default);
        }
        break;
      }
      case PlaidMessage.UNIT_PLAID_TOKEN_CREATED: {
        const linkTokenEvent = message.details as PlaidTokenCreatedEvent;
        const linkTokenData = linkTokenEvent.linkToken as PlaidLinkTokenData;
        const link: string = linkTokenData.attributes.linkToken;
        createPlaidLink({ token: link });  
        break;
      }

      case UnitComponentsMessage.UNIT_REQUEST_EXTERNAL_SDK: {
        const externalSdkType = (message.details as RequestExternalSdkEvent).type;
        if (externalSdkType !== 'plaid') return;
        openPlaidLink({
          onSuccess: (success: LinkSuccess) => {
            injectUnitPlaidResponse(webRef.current, success);
          },
          onExit: (exit: LinkExit) => {
            console.log(exit);
          }
        });
        break;
      }
    }
  };

  const style = presentationMode === PresentationMode.Inherit ? { flex: 1 } : { height: height };

  return (
    <UNBaseView style={style} onLoadError={ handleUnitOnLoad } fallback={<></>}>
      <WebComponent
        ref={webRef}
        type={WebComponentType.achDebitPayment}
        presentationMode={presentationMode}
        params={getACHDebitParams(props)}
        script={getACHDebitScript()}
        onMessage={(message: WebViewMessage) => handleWebViewMessage(message)}
        isScrollable={true}
        theme={props.theme}
        language={props.language}
      />
    </UNBaseView>
  );
};

export default withReduxStore<UNACHDebitComponentProps>(UNACHDebitComponent);
