import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import type WebView from 'react-native-webview';
import { HeightEvent, PageMessage } from '../..//messages/webMessages/pageMessage';
import {
  RequestRefreshEvent,
  RequestRenderingEvent,
  UnitComponentsMessage,
  UnitRequestRefreshEventTypes,
} from '../../messages/webMessages/unitComponentsMessages';
import { WebComponent } from '../../webComponent/WebComponent';
import type { WebViewMessage } from '../../messages/webMessages';
import {
  getAccountParams,
  getAccountScript,
  injectOpenActionsMenuScript,
  injectRefreshEventIfNeeded,
  injectRequestAccountActionScript,
} from './UNAccountComponent.utils';
import type { 
  UNAccount, 
  UNAccountMenuAction, 
  UNAccountMenuItem, 
  UNCreditAccount, 
  UNComponentsError, 
  UNComponentsOnLoadResponse, 
  UNComponentsOnLoadResponseData, 
  UNAccountStatusPosition
} from '../../types/shared';
import { AccountMessage } from '../../messages/webMessages/accountMessage';
import { RESPONSE_KEYS, UnitOnLoadResponseEvent } from '../../messages/webMessages/onLoadMessage';
import {
  BottomSheetRenderingType,
  SlotRendering,
} from '../../types/internal/bottomSheet.types';
import type { BottomSheetSlotData } from '../../types/internal/bottomSheet.types';
import { PresentationMode, WebComponentType } from '../../types/internal/webComponent.types';
import { BottomSheetNativeMessage } from '../../messages/nativeMessages/bottomSheetMessage';
import { withReduxStoreAndRefForwarding } from '../../helpers/store/helpers';
import { eventBus } from '../../utils/eventBus';
import { useEventListener } from '../../hooks/useEventListener';
import { UNBaseView } from '../../nativeComponents/UNBaseView';

export interface UNAccountComponentProps {
  // inputs
  accountId?: string;
  menuItems?: UNAccountMenuItem[];

  // ui
  theme?: string;
  language?: string;
  hideActionsMenuButton?: boolean;
  hideSelectionMenuButton?: boolean;
  hideAccountStatus?: boolean;
  accountStatusPosition?: UNAccountStatusPosition;
  hideAccountDetails?: boolean;
  hideAccountButtons?: boolean;
  hideAccountCtaBanner?: boolean;
  showLeftToSpend?: boolean;
  enableFundAccountButton?: boolean;
  enablePaySomeoneAccountButton?: boolean;

  // events
  onLoad?: (response: UNComponentsOnLoadResponse<[UNAccount]>) => void;
  onAccountChanged?: (account: UNAccount) => void;
  onRequestLeftToSpendDetails?: (account: UNCreditAccount) => void;
}

export interface UNAccountRef {
  openActionsMenu: () => void;
  openAction: (action: UNAccountMenuAction) => void;
  refresh: () => void;
}

export enum UNAccountAction {
  List = 'account-list',
  Menu = 'account-menu'
}

const UNAccountComponent = React.forwardRef<UNAccountRef, UNAccountComponentProps>(function UNAccountComponent(props, accountRef) {

  const [height, setHeight] = useState(0);
  const webRef = useRef<WebView>(null);
  // currentAccountId is used to store the current account id for the "imperative" refresh event.
  const accountIdRef = useRef<string | undefined>(undefined);
  
  useEffect(() => {
    accountIdRef.current = props.accountId;
  }, [props.accountId]);

  const handleAccountChanged = (account: UNAccount) => {
    accountIdRef.current = account.id;
    props.onAccountChanged && props.onAccountChanged(account);
  };

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

  const handleRequestLeftToSpendDetails = (accountData: UNCreditAccount) => {
    props.onRequestLeftToSpendDetails && props.onRequestLeftToSpendDetails(accountData);
  };

  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) {
      // AccountsOnLoadResponse;
      const accountResponse = response[RESPONSE_KEYS.account] as UNComponentsOnLoadResponseData<[UNAccount]>;
      props.onLoad(accountResponse);
      // if accountId is not provided, set the first account as current
      if (!props.accountId && accountResponse.data && accountResponse.data.length > 0) {
        accountIdRef.current = accountResponse.data[0].id;
      }
      return;
    }

    console.error('On Load Error: unexpected response type.');
    return;
  };
  useEventListener({ busEventKey: UnitComponentsMessage.UNIT_REQUEST_REFRESH, action: requestRefresh });
  useEventListener({ busEventKey: AccountMessage.UNIT_ACCOUNT_CHANGED, action: handleAccountChanged });

  useImperativeHandle(accountRef, () => ({
    openActionsMenu() {
      injectOpenActionsMenuScript(webRef.current);
    },
    openAction(action: UNAccountMenuAction) {
      injectRequestAccountActionScript(webRef.current, action);
    },
    refresh() {
      requestRefresh({
        type: UnitRequestRefreshEventTypes.REQUEST_REFRESH_EVENT,
        refEvent: undefined,
        dependencies: [WebComponentType.account.valueOf()],
        resourceId: accountIdRef.current,
      });
    },
  }));

  const handleMessage = (message: WebViewMessage) => {
    switch (message.type) {
      case UnitComponentsMessage.UNIT_ON_LOAD:
        handleUnitOnLoad(message.details as UnitOnLoadResponseEvent);
        break;
      case UnitComponentsMessage.UNIT_REQUEST_RENDERING: {
        const slotData: BottomSheetSlotData = {
          componentName: WebComponentType.account,
          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:
        setHeight((message.details as HeightEvent).height);
        break;
      case UnitComponentsMessage.UNIT_REQUEST_LEFT_TO_SPEND_DETAILS:
        handleRequestLeftToSpendDetails(message.details as UNCreditAccount);
        break;
    }
  };

  return (
    <UNBaseView style={{ height, width: '100%' }} onLoadError={ handleUnitOnLoad } fallback={<></>}>
      <WebComponent
        ref={webRef}
        type={WebComponentType.account}
        presentationMode={PresentationMode.Default}
        params={getAccountParams(props)}
        script={getAccountScript()}
        theme={props.theme}
        language={props.language}
        onMessage={message => handleMessage(message)}
        isScrollable={false}
      />
    </UNBaseView>
  );
});

export default withReduxStoreAndRefForwarding<UNAccountComponentProps, UNAccountRef>(UNAccountComponent);
