import React, { useRef } from 'react';
import type WebView from 'react-native-webview';
import {
  RequestRefreshEvent,
  UnitComponentsMessage,
} from '../../messages/webMessages/unitComponentsMessages';
import { WebComponent } from '../../webComponent/WebComponent';
import type { WebViewMessage } from '../../messages/webMessages';
import { PresentationMode, WebComponentType } from '../../types/internal/webComponent.types';
import { withReduxStore } from '../../helpers/store/helpers';
import { 
  UNConnectedAccount, 
  UNCounterparty, 
  UNComponentsError, 
  UNComponentsOnLoadResponse, 
  UNPayeeManagementComponentPaginationType, 
  UNPayeeManagementMenuItem, 
  UNPlaidAccountFilter 
} from '../../types/shared';
import { getPayeeParams, getPayeeManagementScript, injectRefreshEventIfNeeded, injectUnitPlaidResponse, injectUnitOpenPlaid, injectUnitPlaidExit } from './UNPayeeManagementComponent.utils';
import { BottomSheetRenderingType, BottomSheetSlotData, SlotRendering } from '../../types/internal/bottomSheet.types';
import { RequestRenderingEvent } from '../../messages/webMessages/unitComponentsMessages';
import { eventBus } from '../../utils/eventBus';
import { BottomSheetNativeMessage } from '../../messages/nativeMessages/bottomSheetMessage';
import { useEventListener } from '../../hooks/useEventListener';
import { LinkExit, LinkSuccess, open as openPlaidLink, create as createPlaidLink } from 'react-native-plaid-link-sdk';
import { OpenPlaidEvent, PlaidMessage } from '../../messages/webMessages/plaidMessages';
import { PayeeConnectedAccountEvent, PayeeCounterpartyEvent, PayeeManagementMessage, PayeeMicroDepositeConnectionEvent } from '../../messages/webMessages/payeeManagementMessage';
import { UNBaseView } from '../../nativeComponents/UNBaseView';
import { RESPONSE_KEYS, UnitOnLoadResponseEvent } from '../../messages/webMessages/onLoadMessage';

export interface UNPayeeManagementComponentProps {
  // inputs
  menuItems?: UNPayeeManagementMenuItem[];
  queryFilter?: string;
  paginationType?: UNPayeeManagementComponentPaginationType;
  counterPartiesPerPage?: number;

  // ui
  theme?: string;
  language?: string;

  hideTitle?: boolean;
  hideCounterpartyActionsButton?: boolean;
  showAccountTypeSelector?: boolean;

  menuPlaceholder?: string;
  flowPlaceholder?: string;
  payeeCreationPlaceholder?: string;
  microDepositConnectionPlaceholder?: string;

  plaidAccountFilters?: UNPlaidAccountFilter[];
  plaidLinkCustomizationName?: string;

  // events
  onLoad?: (response: UNComponentsOnLoadResponse<[UNCounterparty]>) => void;
  onCounterpartyCreated?: (counterparty: UNCounterparty) => void;
  onCounterpartyDeleted?: (counterparty: UNCounterparty) => void;

  onConnectedAccountCreated?: (connectedAccount: UNConnectedAccount) => void;
  onMicroDepositConnectionCreated?: (onnectedAccount: UNCounterparty) => void;
  onMicroDepositConnectionRejected?: (connectedAccount: UNConnectedAccount) => void;
}

const UNPayeeManagementComponent = (props: UNPayeeManagementComponentProps) => {
  const webRef = useRef<WebView>(null);

  const requestRefresh = (data: RequestRefreshEvent) => {
    injectRefreshEventIfNeeded(webRef.current, data);
  };

  const openPlaid = (openPlaidData: OpenPlaidEvent) => {
    injectUnitOpenPlaid(webRef.current, openPlaidData);

    const linkToken = openPlaidData.plaidLinkToken.attributes.linkToken;
    createPlaidLink({ token: linkToken });

    openPlaidLink({
      onSuccess: (success: LinkSuccess) => {
        injectUnitPlaidResponse(webRef.current, success);
      },
      onExit: (exit: LinkExit) => {
        injectUnitPlaidExit(webRef.current, exit.error);
      }
    });
  };

  const reportCounterpartyDeleted = (counterparty: UNCounterparty) => {
    props.onCounterpartyDeleted && props.onCounterpartyDeleted(counterparty);
  };

  useEventListener({ busEventKey: UnitComponentsMessage.UNIT_REQUEST_REFRESH, action: requestRefresh });
  useEventListener({ busEventKey: PlaidMessage.UNIT_OPEN_PLAID, action: openPlaid });
  useEventListener({ busEventKey: PayeeManagementMessage.UNIT_COUNTERPARTY_DELETED, action: reportCounterpartyDeleted });

  const handleRequestRendering = (requestRenderingEvent : RequestRenderingEvent) => {
    switch (requestRenderingEvent.data.nativeComponentName) {
      default: {
        const slotData: BottomSheetSlotData = {
          componentName: requestRenderingEvent.data.nativeComponentName,
          requestRenderingEvent: requestRenderingEvent,
        };
        const data = {
          type: BottomSheetRenderingType.Slot,
          data: slotData,
        } as SlotRendering;
        eventBus.emit(BottomSheetNativeMessage.REQUEST_RENDERING, data);
      }
    }
  };

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

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

    if (RESPONSE_KEYS.counterpartiesResult in response) {
      props.onLoad(response[RESPONSE_KEYS.counterpartiesResult] as UNComponentsOnLoadResponse<[UNCounterparty]>);
      return;
    }

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

  const handleMessage = (message: WebViewMessage) => {
    switch (message.type) {
      case UnitComponentsMessage.UNIT_REQUEST_RENDERING: {
        const requestRenderingEvent = message.details as RequestRenderingEvent;
        handleRequestRendering(requestRenderingEvent);
        break;
      }
      case UnitComponentsMessage.UNIT_ON_LOAD: {
        handleUnitOnLoad(message.details as UnitOnLoadResponseEvent);
        break;
      }
      case PayeeManagementMessage.UNIT_CONNECTED_ACCOUNT_CREATED: {
        const account: UNConnectedAccount = (message.details as PayeeConnectedAccountEvent).account;
        props.onConnectedAccountCreated && props.onConnectedAccountCreated(account);
        break;
      }
      case PayeeManagementMessage.UNIT_COUNTERPARTY_CREATED: {
        const counterparty: UNCounterparty = (message.details as PayeeCounterpartyEvent).counterparty;
        props.onCounterpartyCreated &&  props.onCounterpartyCreated(counterparty);
        break;
      }
      case PayeeManagementMessage.UNIT_MICRO_DEPOSIT_CONNECTION_CREATED: {
        const counterparty: UNCounterparty = (message.details as PayeeMicroDepositeConnectionEvent).account;
        props.onMicroDepositConnectionCreated && props.onMicroDepositConnectionCreated(counterparty);
        break;
      }
      case PayeeManagementMessage.UNIT_MICRO_DEPOSIT_CONNECTION_REJECTED: {
        const account: UNConnectedAccount = (message.details as PayeeConnectedAccountEvent).account;
        props.onMicroDepositConnectionRejected && props.onMicroDepositConnectionRejected(account);
        break;
      }
    }
  };

  return (
    <UNBaseView style={{ height: '100%' }} fallback={<></>}>
      <WebComponent
        ref={webRef}
        type={WebComponentType.payeeManagement}
        presentationMode={PresentationMode.Inherit}
        params={getPayeeParams(props)}
        script={getPayeeManagementScript()}
        theme={props.theme}
        nestedScrollEnabled={true}
        language={props.language}
        onMessage={message => handleMessage(message)}
      />
    </UNBaseView>
  );
};

export default withReduxStore<UNPayeeManagementComponentProps>(UNPayeeManagementComponent);
