/* eslint-disable no-unsafe-optional-chaining */
import { Injectable, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of, Subject } from 'rxjs';
import { map, mergeMap, catchError, withLatestFrom, takeUntil } from 'rxjs/operators';
import { Location } from '@angular/common';
import { MatDialog } from '@angular/material/dialog';

import { LoggerService } from '../../shared/services/logger.service';
import { CommonService } from '../../shared/services/common.service';
import { SessionService } from '../../shared/services/session.service';
import { GetInfo, Fees, BlockchainBalance, NetworkInfo, GraphNode, Transaction, SwitchReq, ListInvoices,
  PendingChannelsSummary, UTXO, ListPayments, SavePeer, SaveInvoice, SaveChannel, CloseChannel, FetchInvoices, FetchPayments,
  SendPayment, LightningNode, GetNewAddress, ChannelsTransaction, GetQueryRoutes, QueryRoutes, InitWallet, ChannelLookup,
  SetRestoreChannelsList } from '../../shared/models/lndModels';
import { InvoiceInformationComponent } from '../transactions/invoice-information-modal/invoice-information.component';
import { ErrorMessageComponent } from '../../shared/components/data-modal/error-message/error-message.component';
import { API_URL, API_END_POINTS, RTLActions, LNDActions, AlertTypeEnum, APICallStatusEnum, FEE_LIMIT_TYPES, PAGE_SIZE, UI_MESSAGES, LNDWSEventTypeEnum, LND_DEFAULT_PAGE_SETTINGS, SortOrderEnum } from '../../shared/services/consts-enums-functions';
import { closeAllDialogs, closeSpinner, logout, openAlert, openSnackBar, openSpinner, setApiUrl, setNodeData } from '../../store/rtl.actions';
import { RTLState } from '../../store/rtl.state';

import { backupChannels, fetchBalanceBlockchain, fetchClosedChannels, fetchFees, fetchInfoLND, fetchInvoices, fetchNetwork, fetchPayments,
  fetchPeers, fetchPendingChannels, fetchTransactions, setForwardingHistory, setPeers, setQueryRoutes, setRestoreChannelsList,
  updateLNDAPICallStatus, updateInvoice, fetchChannels, updatePayment } from './lnd.actions';
import { allAPIsCallStatus, lndNodeInformation } from './lnd.selector';
import { ApiCallsListLND } from '../../shared/models/apiCallsPayload';
import { WebSocketClientService } from '../../shared/services/web-socket.service';

@Injectable()
export class LNDEffects implements OnDestroy {

  dialogRef: any;
  CHILD_API_URL = API_URL + '/lnd';
  private invoicesPageSettings = LND_DEFAULT_PAGE_SETTINGS.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'invoices');
  private paymentsPageSettings = LND_DEFAULT_PAGE_SETTINGS.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'payments');
  private flgInitialized = false;
  private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];

  constructor(
    private actions: Actions,
    private httpClient: HttpClient,
    private store: Store<RTLState>,
    private logger: LoggerService,
    private commonService: CommonService,
    private sessionService: SessionService,
    public dialog: MatDialog,
    private router: Router,
    private wsService: WebSocketClientService,
    private location: Location
  ) {
    this.store.select(allAPIsCallStatus).pipe(takeUntil(this.unSubs[0])).
      subscribe((allApisCallStatus: ApiCallsListLND) => {
        if (
          ((allApisCallStatus.FetchInfo.status === APICallStatusEnum.COMPLETED || allApisCallStatus.FetchInfo.status === APICallStatusEnum.ERROR) &&
            (allApisCallStatus.FetchFees.status === APICallStatusEnum.COMPLETED || allApisCallStatus.FetchFees.status === APICallStatusEnum.ERROR) &&
            (allApisCallStatus.FetchBalanceBlockchain.status === APICallStatusEnum.COMPLETED || allApisCallStatus.FetchBalanceBlockchain.status === APICallStatusEnum.ERROR) &&
            (allApisCallStatus.FetchAllChannels.status === APICallStatusEnum.COMPLETED || allApisCallStatus.FetchAllChannels.status === APICallStatusEnum.ERROR) &&
            (allApisCallStatus.FetchPendingChannels.status === APICallStatusEnum.COMPLETED || allApisCallStatus.FetchPendingChannels.status === APICallStatusEnum.ERROR)) &&
          !this.flgInitialized
        ) {
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.INITALIZE_NODE_DATA }));
          this.flgInitialized = true;
        }
      });
    this.wsService.lndWSMessages.pipe(takeUntil(this.unSubs[1])).
      subscribe((newMessage) => {
        this.logger.info('Received new message from the service: ' + JSON.stringify(newMessage));
        if (newMessage) {
          switch (newMessage.type) {
            case LNDWSEventTypeEnum.INVOICE:
              this.logger.info(newMessage);
              if (newMessage && newMessage.result && newMessage.result.payment_request) {
                this.store.dispatch(updateInvoice({ payload: newMessage.result }));
              }
              break;
            default:
              this.logger.info('Received Event from WS: ' + JSON.stringify(newMessage));
              break;
          }
        }
      });
  }

  infoFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_INFO_LND),
    mergeMap((action: { type: string, payload: { loadPage: string } }) => {
      this.flgInitialized = false;
      this.store.dispatch(setApiUrl({ payload: this.CHILD_API_URL }));
      this.store.dispatch(closeAllDialogs());
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_NODE_INFO }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get<GetInfo>(this.CHILD_API_URL + API_END_POINTS.GETINFO_API).pipe(
        takeUntil(this.actions.pipe(ofType(RTLActions.SET_SELECTED_NODE))),
        map((info) => {
          this.logger.info(info);
          if (info.chains && info.chains.length && info.chains[0] && (
            (typeof info.chains[0] === 'string' && info.chains[0].toLowerCase().indexOf('bitcoin') < 0) ||
            (typeof info.chains[0] === 'object' && info.chains[0].hasOwnProperty('chain') && info.chains[0].chain && info.chains[0].chain.toLowerCase().indexOf('bitcoin') < 0)
          )
          ) {
            this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.COMPLETED } }));
            this.store.dispatch(closeAllDialogs()); // Multiple UI_MESSAGES.GET_NODE_INFO after unlock & UI_MESSAGES.WAIT_SYNC_NODE
            this.store.dispatch(openAlert({
              payload: {
                data: {
                  type: AlertTypeEnum.ERROR,
                  alertTitle: 'Shitcoin Found',
                  titleMessage: 'Sorry Not Sorry, RTL is Bitcoin Only!'
                }
              }
            }));
            return { type: RTLActions.LOGOUT };
          } else if (!info.identity_pubkey) {
            this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.COMPLETED } }));
            this.store.dispatch(closeAllDialogs()); // Multiple UI_MESSAGES.GET_NODE_INFO after unlock & UI_MESSAGES.WAIT_SYNC_NODE
            this.sessionService.removeItem('lndUnlocked');
            this.logger.info('Redirecting to Unlock');
            this.router.navigate(['/lnd/wallet']);
            return {
              type: LNDActions.SET_INFO_LND,
              payload: {}
            };
          } else {
            info.lnImplementation = 'LND';
            this.initializeRemainingData(info, action.payload.loadPage);
            this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.COMPLETED } }));
            this.store.dispatch(closeAllDialogs()); // Multiple UI_MESSAGES.GET_NODE_INFO after unlock & UI_MESSAGES.WAIT_SYNC_NODE
            return {
              type: LNDActions.SET_INFO_LND,
              payload: info ? info : {}
            };
          }
        }),
        catchError((err) => {
          if (
            (typeof err.error.error === 'string' && err.error.error.includes('Not Found')) ||
            (typeof err.error.error === 'string' && err.error.error.includes('wallet locked')) ||
            (err.status === 502 && !err.error.message.includes('Bad or Missing Macaroon'))
          ) {
            this.sessionService.removeItem('lndUnlocked');
            this.logger.info('Redirecting to Unlock');
            this.router.navigate(['/lnd/wallet']);
            this.handleErrorWithoutAlert('FetchInfo', UI_MESSAGES.GET_NODE_INFO, 'Fetching Node Info Failed.', err);
          } else if (typeof err.error.error === 'string' && err.error.error.includes('starting up') && err.status === 500) {
            setTimeout(() => {
              this.store.dispatch(fetchInfoLND({ payload: { loadPage: 'HOME' } }));
            }, 2000);
          } else {
            const code = this.commonService.extractErrorCode(err);
            const msg = (code === 503) ? 'Unable to Connect to LND Server.' : this.commonService.extractErrorMessage(err);
            this.router.navigate(['/error'], { state: { errorCode: code, errorMessage: msg } });
            this.handleErrorWithoutAlert('FetchInfo', UI_MESSAGES.GET_NODE_INFO, 'Fetching Node Info Failed.', { status: code, error: msg });
          }
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  peersFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_PEERS_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPeers', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.PEERS_API).pipe(
        map((peers: any) => {
          this.logger.info(peers);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPeers', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_PEERS_LND,
            payload: peers || []
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchPeers', UI_MESSAGES.NO_SPINNER, 'Fetching Peers Failed.', err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  saveNewPeer = createEffect(() => this.actions.pipe(
    ofType(LNDActions.SAVE_NEW_PEER_LND),
    mergeMap((action: { type: string, payload: SavePeer }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.CONNECT_PEER }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SaveNewPeer', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.PEERS_API, { pubkey: action.payload.pubkey, host: action.payload.host, perm: action.payload.perm }).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SaveNewPeer', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.CONNECT_PEER }));
          this.store.dispatch(setPeers({ payload: (postRes || []) }));
          return {
            type: LNDActions.NEWLY_ADDED_PEER_LND,
            payload: { peer: postRes[0] }
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('SaveNewPeer', UI_MESSAGES.CONNECT_PEER, 'Peer Connection Failed.', err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  detachPeer = createEffect(() => this.actions.pipe(
    ofType(LNDActions.DETACH_PEER_LND),
    mergeMap((action: { type: string, payload: { pubkey: string } }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.DISCONNECT_PEER }));
      return this.httpClient.delete(this.CHILD_API_URL + API_END_POINTS.PEERS_API + '/' + action.payload.pubkey).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.DISCONNECT_PEER }));
          this.store.dispatch(openSnackBar({ payload: 'Peer Disconnected Successfully.' }));
          return {
            type: LNDActions.REMOVE_PEER_LND,
            payload: { pubkey: action.payload.pubkey }
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('DetachPeer', UI_MESSAGES.DISCONNECT_PEER, 'Unable to Detach Peer. Try again later.', this.CHILD_API_URL + API_END_POINTS.PEERS_API + '/' + action.payload.pubkey, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  saveNewInvoice = createEffect(() => this.actions.pipe(
    ofType(LNDActions.SAVE_NEW_INVOICE_LND),
    mergeMap((action: { type: string, payload: SaveInvoice }) => {
      this.store.dispatch(openSpinner({ payload: action.payload.uiMessage }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.INVOICES_API, {
        memo: action.payload.memo, value: action.payload.value, private: action.payload.private, expiry: action.payload.expiry, is_amp: action.payload.is_amp
      }).
        pipe(
          map((postRes: any) => {
            this.logger.info(postRes);
            this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.COMPLETED } }));
            this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: action.payload.pageSize, reversed: true } }));
            if (action.payload.openModal) {
              postRes.memo = action.payload.memo;
              postRes.value = action.payload.value;
              postRes.expiry = action.payload.expiry;
              postRes.private = action.payload.private;
              postRes.is_amp = action.payload.is_amp;
              postRes.cltv_expiry = '144';
              postRes.creation_date = Math.round(new Date().getTime() / 1000).toString();
              setTimeout(() => {
                this.store.dispatch(openAlert({
                  payload: {
                    data: {
                      invoice: postRes,
                      newlyAdded: true,
                      component: InvoiceInformationComponent
                    }
                  }
                }));
              }, 200);
              return {
                type: RTLActions.CLOSE_SPINNER,
                payload: action.payload.uiMessage
              };
            } else {
              return {
                type: LNDActions.NEWLY_SAVED_INVOICE_LND,
                payload: { paymentRequest: postRes.payment_request }
              };
            }
          }),
          catchError((err: any) => {
            this.handleErrorWithoutAlert('SaveNewInvoice', action.payload.uiMessage, 'Add Invoice Failed.', err);
            return of({ type: RTLActions.VOID });
          })
        );
    }))
  );

  openNewChannel = createEffect(() => this.actions.pipe(
    ofType(LNDActions.SAVE_NEW_CHANNEL_LND),
    mergeMap((action: { type: string, payload: SaveChannel }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.OPEN_CHANNEL }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SaveNewChannel', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API, {
        node_pubkey: action.payload.selectedPeerPubkey, local_funding_amount: action.payload.fundingAmount, private: action.payload.private,
        trans_type: action.payload.transType, trans_type_value: action.payload.transTypeValue, spend_unconfirmed: action.payload.spendUnconfirmed,
        commitment_type: action.payload.commitmentType
      }).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SaveNewChannel', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.OPEN_CHANNEL }));
          this.store.dispatch(fetchBalanceBlockchain());
          this.store.dispatch(fetchChannels());
          this.store.dispatch(backupChannels({ payload: { uiMessage: UI_MESSAGES.NO_SPINNER, channelPoint: 'ALL', showMessage: 'Channel Added Successfully!' } }));
          return {
            type: LNDActions.FETCH_PENDING_CHANNELS_LND
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('SaveNewChannel', UI_MESSAGES.OPEN_CHANNEL, 'Opening Channel Failed.', err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  updateChannel = createEffect(() => this.actions.pipe(
    ofType(LNDActions.UPDATE_CHANNEL_LND),
    mergeMap((action: { type: string, payload: any }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.UPDATE_CHAN_POLICY }));
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/chanPolicy',
        { baseFeeMsat: action.payload.baseFeeMsat,
          feeRate: action.payload.feeRate,
          timeLockDelta: action.payload.timeLockDelta,
          max_htlc_msat: action.payload.maxHtlcMsat,
          min_htlc_msat: action.payload.minHtlcMsat,
          chanPoint: action.payload.chanPoint }
      ).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.UPDATE_CHAN_POLICY }));
          if (action.payload.chanPoint === 'all') {
            this.store.dispatch(openSnackBar({ payload: 'All Channels Updated Successfully.' }));
          } else {
            this.store.dispatch(openSnackBar({ payload: 'Channel Updated Successfully!' }));
          }
          return {
            type: LNDActions.FETCH_CHANNELS_LND
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('UpdateChannels', UI_MESSAGES.UPDATE_CHAN_POLICY, 'Update Channel Failed', this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/chanPolicy', err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  closeChannel = createEffect(() => this.actions.pipe(
    ofType(LNDActions.CLOSE_CHANNEL_LND),
    mergeMap((action: { type: string, payload: CloseChannel }) => {
      this.store.dispatch(openSpinner({ payload: (action.payload.forcibly ? UI_MESSAGES.FORCE_CLOSE_CHANNEL : UI_MESSAGES.CLOSE_CHANNEL) }));
      let reqUrl = this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/' + action.payload.channelPoint + '?force=' + action.payload.forcibly;
      if (action.payload.targetConf) {
        reqUrl = reqUrl + '&target_conf=' + action.payload.targetConf;
      }
      if (action.payload.satPerByte) {
        reqUrl = reqUrl + '&sat_per_byte=' + action.payload.satPerByte;
      }
      return this.httpClient.delete(reqUrl).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(closeSpinner({ payload: action.payload.forcibly ? UI_MESSAGES.FORCE_CLOSE_CHANNEL : UI_MESSAGES.CLOSE_CHANNEL }));
          this.store.dispatch(fetchChannels());
          this.store.dispatch(fetchPendingChannels());
          this.store.dispatch(backupChannels({ payload: { uiMessage: UI_MESSAGES.NO_SPINNER, channelPoint: 'ALL', showMessage: postRes.message } }));
          return { type: RTLActions.VOID };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('CloseChannel', (action.payload.forcibly ? UI_MESSAGES.FORCE_CLOSE_CHANNEL : UI_MESSAGES.CLOSE_CHANNEL), 'Unable to Close Channel. Try again later.', this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/' + action.payload.channelPoint + '?force=' + action.payload.forcibly, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  backupChannels = createEffect(() => this.actions.pipe(
    ofType(LNDActions.BACKUP_CHANNELS_LND),
    mergeMap((action: { type: string, payload: any }) => {
      this.store.dispatch(openSpinner({ payload: action.payload.uiMessage }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'BackupChannels', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API + '/' + action.payload.channelPoint).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'BackupChannels', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(closeSpinner({ payload: action.payload.uiMessage }));
          this.store.dispatch(openSnackBar({ payload: action.payload.showMessage + ' ' + postRes.message }));
          return {
            type: LNDActions.BACKUP_CHANNELS_RES_LND,
            payload: postRes.message
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('BackupChannels', action.payload.uiMessage, action.payload.showMessage + ' Unable to Backup Channel. Try again later.', this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API + '/' + action.payload.channelPoint, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  verifyChannel = createEffect(() => this.actions.pipe(
    ofType(LNDActions.VERIFY_CHANNEL_LND),
    mergeMap((action: { type: string, payload: { channelPoint: string } }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.VERIFY_CHANNEL }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'VerifyChannel', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API + '/verify/' + action.payload.channelPoint, {}).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'VerifyChannel', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.VERIFY_CHANNEL }));
          this.store.dispatch(openSnackBar({ payload: postRes.message }));
          return {
            type: LNDActions.VERIFY_CHANNEL_RES_LND,
            payload: postRes.message
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('VerifyChannel', UI_MESSAGES.VERIFY_CHANNEL, 'Unable to Verify Channel. Try again later.', this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API + '/verify/' + action.payload.channelPoint, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  restoreChannels = createEffect(() => this.actions.pipe(
    ofType(LNDActions.RESTORE_CHANNELS_LND),
    mergeMap((action: { type: string, payload: { channelPoint: string } }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.RESTORE_CHANNEL }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'RestoreChannels', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API + '/restore/' + action.payload.channelPoint, {}).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'RestoreChannels', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.RESTORE_CHANNEL }));
          this.store.dispatch(openSnackBar({ payload: postRes.message }));
          this.store.dispatch(setRestoreChannelsList({ payload: postRes.list }));
          return {
            type: LNDActions.RESTORE_CHANNELS_RES_LND,
            payload: postRes.message
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('RestoreChannels', UI_MESSAGES.RESTORE_CHANNEL, 'Unable to Restore Channel. Try again later.', this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API + '/restore/' + action.payload.channelPoint, err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  fetchFees = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_FEES_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchFees', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get<Fees>(this.CHILD_API_URL + API_END_POINTS.FEES_API);
    }),
    map((fees) => {
      this.logger.info(fees);
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchFees', status: APICallStatusEnum.COMPLETED } }));
      if (fees.forwarding_events_history) {
        this.store.dispatch(setForwardingHistory({ payload: fees.forwarding_events_history }));
        delete fees.forwarding_events_history;
      }
      return {
        type: LNDActions.SET_FEES_LND,
        payload: fees ? fees : {}
      };
    }),
    catchError((err: any) => {
      this.handleErrorWithoutAlert('FetchFees', UI_MESSAGES.NO_SPINNER, 'Fetching Fees Failed.', err);
      return of({ type: RTLActions.VOID });
    }))
  );

  balanceBlockchainFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_BLOCKCHAIN_BALANCE_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchBalance', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get<BlockchainBalance>(this.CHILD_API_URL + API_END_POINTS.BALANCE_API);
    }),
    map((res: any) => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchBalance', status: APICallStatusEnum.COMPLETED } }));
      this.logger.info(res);
      return {
        type: LNDActions.SET_BLOCKCHAIN_BALANCE_LND,
        payload: res ? res : { total_balance: '' }
      };
    }),
    catchError((err: any) => {
      this.handleErrorWithoutAlert('FetchBalance', UI_MESSAGES.NO_SPINNER, 'Fetching Blockchain Balance Failed.', err);
      return of({ type: RTLActions.VOID });
    }))
  );

  networkInfoFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_NETWORK_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchNetwork', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get<NetworkInfo>(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/info');
    }),
    map((networkInfo) => {
      this.logger.info(networkInfo);
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchNetwork', status: APICallStatusEnum.COMPLETED } }));
      return {
        type: LNDActions.SET_NETWORK_LND,
        payload: networkInfo ? networkInfo : {}
      };
    }),
    catchError((err: any) => {
      this.handleErrorWithoutAlert('FetchNetwork', UI_MESSAGES.NO_SPINNER, 'Fetching Network Failed.', err);
      return of({ type: RTLActions.VOID });
    }))
  );

  channelsFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_CHANNELS_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchChannels', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API).pipe(
        map((channels: any) => {
          this.logger.info(channels);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchChannels', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_CHANNELS_LND,
            payload: channels.channels || []
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchChannels', UI_MESSAGES.NO_SPINNER, 'Fetching Channels Failed.', err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  channelsPendingFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_PENDING_CHANNELS_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPendingChannels', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/pending').pipe(
        map((pendingChannels: any) => {
          this.logger.info(pendingChannels);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPendingChannels', status: APICallStatusEnum.COMPLETED } }));
          const pendingChannelsSummary: PendingChannelsSummary = { open: { num_channels: 0, limbo_balance: 0 }, closing: { num_channels: 0, limbo_balance: 0 }, force_closing: { num_channels: 0, limbo_balance: 0 }, waiting_close: { num_channels: 0, limbo_balance: 0 }, total_channels: 0, total_limbo_balance: 0 };
          if (pendingChannels) {
            pendingChannelsSummary.total_limbo_balance = pendingChannels.total_limbo_balance;
            if (pendingChannels.pending_closing_channels) {
              pendingChannelsSummary.closing!.num_channels = pendingChannels.pending_closing_channels.length;
              pendingChannelsSummary.total_channels = pendingChannelsSummary.total_channels + pendingChannels.pending_closing_channels.length;
              pendingChannels.pending_closing_channels.forEach((closingChannel) => {
                pendingChannelsSummary.closing!.limbo_balance = +pendingChannelsSummary.closing!.limbo_balance + (closingChannel.channel.local_balance ? +closingChannel.channel.local_balance : 0);
              });
            }
            if (pendingChannels.pending_force_closing_channels) {
              pendingChannelsSummary.force_closing!.num_channels = pendingChannels.pending_force_closing_channels.length;
              pendingChannelsSummary.total_channels = pendingChannelsSummary.total_channels + pendingChannels.pending_force_closing_channels.length;
              pendingChannels.pending_force_closing_channels.forEach((closingChannel) => {
                pendingChannelsSummary.force_closing!.limbo_balance = +pendingChannelsSummary.force_closing!.limbo_balance + (closingChannel.channel.local_balance ? +closingChannel.channel.local_balance : 0);
              });
            }
            if (pendingChannels.pending_open_channels) {
              pendingChannelsSummary.open!.num_channels = pendingChannels.pending_open_channels.length;
              pendingChannelsSummary.total_channels = pendingChannelsSummary.total_channels + pendingChannels.pending_open_channels.length;
              pendingChannels.pending_open_channels.forEach((openingChannel) => {
                pendingChannelsSummary.open!.limbo_balance = +pendingChannelsSummary.open!.limbo_balance + (openingChannel.channel.local_balance ? +openingChannel.channel.local_balance : 0);
              });
            }
            if (pendingChannels.waiting_close_channels) {
              pendingChannelsSummary.waiting_close!.num_channels = pendingChannels.waiting_close_channels.length;
              pendingChannelsSummary.total_channels = pendingChannelsSummary.total_channels + pendingChannels.waiting_close_channels.length;
              pendingChannels.waiting_close_channels.forEach((closingChannel) => {
                pendingChannelsSummary.waiting_close!.limbo_balance = +pendingChannelsSummary.waiting_close!.limbo_balance + (closingChannel.channel.local_balance ? +closingChannel.channel.local_balance : 0);
              });
            }
          }
          return {
            type: LNDActions.SET_PENDING_CHANNELS_LND,
            payload: pendingChannels ? { pendingChannels: pendingChannels, pendingChannelsSummary: pendingChannelsSummary } : { pendingChannels: {}, pendingChannelsSummary: pendingChannelsSummary }
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchPendingChannels', UI_MESSAGES.NO_SPINNER, 'Fetching Pending Channels Failed.', err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  channelsClosedFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_CLOSED_CHANNELS_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchClosedChannels', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/closed').pipe(
        map((channels: any) => {
          this.logger.info(channels);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchClosedChannels', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_CLOSED_CHANNELS_LND,
            payload: channels.channels || []
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchClosedChannels', UI_MESSAGES.NO_SPINNER, 'Fetching Closed Channels Failed.', err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  invoicesFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_INVOICES_LND),
    mergeMap((action: { type: string, payload: FetchInvoices }) => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchInvoices', status: APICallStatusEnum.INITIATED } }));
      const num_max_invoices = (action.payload.num_max_invoices) ? action.payload.num_max_invoices : 100;
      const index_offset = (action.payload.index_offset) ? action.payload.index_offset : 0;
      const reversed = (action.payload.reversed) ? action.payload.reversed : false;
      return this.httpClient.get<ListInvoices>(this.CHILD_API_URL + API_END_POINTS.INVOICES_API + '?num_max_invoices=' + num_max_invoices + '&index_offset=' + index_offset + '&reversed=' + reversed).pipe(
        map((res: ListInvoices) => {
          this.logger.info(res);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchInvoices', status: APICallStatusEnum.COMPLETED } }));
          if (action.payload.reversed && !action.payload.index_offset) {
            res['total_invoices'] = +(res.last_index_offset || 0);
          }
          return {
            type: LNDActions.SET_INVOICES_LND,
            payload: res
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchInvoices', UI_MESSAGES.NO_SPINNER, 'Fetching Invoices Failed.', err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  transactionsFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_TRANSACTIONS_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchTransactions', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get<Transaction[]>(this.CHILD_API_URL + API_END_POINTS.TRANSACTIONS_API);
    }),
    map((transactions) => {
      this.logger.info(transactions);
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchTransactions', status: APICallStatusEnum.COMPLETED } }));
      return {
        type: LNDActions.SET_TRANSACTIONS_LND,
        payload: transactions || []
      };
    }),
    catchError((err: any) => {
      this.handleErrorWithoutAlert('FetchTransactions', UI_MESSAGES.NO_SPINNER, 'Fetching Transactions Failed.', err);
      return of({ type: RTLActions.VOID });
    }))
  );

  utxosFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_UTXOS_LND),
    withLatestFrom(this.store.select(lndNodeInformation)),
    mergeMap(([action, nodeInfo]: [{ type: string, payload: any }, GetInfo]) => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchUTXOs', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get<UTXO[]>(this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/getUTXOs?max_confs=' + (nodeInfo && nodeInfo.block_height ? nodeInfo.block_height : 1000000000));
    }),
    map((utxos) => {
      this.logger.info(utxos);
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchUTXOs', status: APICallStatusEnum.COMPLETED } }));
      return {
        type: LNDActions.SET_UTXOS_LND,
        payload: utxos || []
      };
    }),
    catchError((err: any) => {
      this.handleErrorWithoutAlert('FetchUTXOs', UI_MESSAGES.NO_SPINNER, 'Fetching UTXOs Failed.', err);
      return of({ type: RTLActions.VOID });
    }))
  );

  paymentsFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_PAYMENTS_LND),
    mergeMap((action: { type: string, payload: FetchPayments }) => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPayments', status: APICallStatusEnum.INITIATED } }));
      const max_payments = (action.payload.max_payments) ? action.payload.max_payments : 100;
      const index_offset = (action.payload.index_offset) ? action.payload.index_offset : 0;
      const reversed = (action.payload.reversed) ? action.payload.reversed : false;
      return this.httpClient.get<ListPayments>(this.CHILD_API_URL + API_END_POINTS.PAYMENTS_API + '?max_payments=' + max_payments + '&index_offset=' + index_offset + '&reversed=' + reversed).
        pipe(map((res: ListPayments) => {
          this.logger.info(res);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPayments', status: APICallStatusEnum.COMPLETED } }));
          this.commonService.sortByKey(res.payments || [], this.paymentsPageSettings?.sortBy || 'creation_date', 'number', this.paymentsPageSettings?.sortOrder);
          return {
            type: LNDActions.SET_PAYMENTS_LND,
            payload: res
          };
        }), catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchPayments', UI_MESSAGES.NO_SPINNER, 'Fetching Payments Failed.', err);
          return of({
            type: LNDActions.SET_PAYMENTS_LND,
            payload: { payments: [] }
          });
        }));
    }))
  );

  sendPayment = createEffect(() => this.actions.pipe(
    ofType(LNDActions.SEND_PAYMENT_LND),
    mergeMap((action: { type: string, payload: SendPayment }) => {
      this.store.dispatch(openSpinner({ payload: action.payload.uiMessage }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.INITIATED } }));
      const queryHeaders = {};
      queryHeaders['paymentReq'] = action.payload.paymentReq;
      if (action.payload.paymentAmount) {
        queryHeaders['paymentAmount'] = action.payload.paymentAmount;
      }
      if (action.payload.outgoingChannel) {
        queryHeaders['outgoingChannel'] = action.payload.outgoingChannel.chan_id;
      }
      if (action.payload.allowSelfPayment) {
        queryHeaders['allowSelfPayment'] = action.payload.allowSelfPayment;
      } // Channel Rebalancing
      if (action.payload.lastHopPubkey) {
        queryHeaders['lastHopPubkey'] = action.payload.lastHopPubkey;
      }
      if (action.payload.feeLimitType && action.payload.feeLimitType !== FEE_LIMIT_TYPES[0].id) {
        queryHeaders['feeLimit'] = {};
        queryHeaders['feeLimit'][action.payload.feeLimitType] = action.payload.feeLimit;
      }
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/transactions', queryHeaders).pipe(
        map((sendRes: any) => {
          this.logger.info(sendRes);
          this.store.dispatch(closeSpinner({ payload: action.payload.uiMessage }));
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.COMPLETED } }));
          if (sendRes.payment_error) {
            if (action.payload.allowSelfPayment) {
              this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
              return {
                type: LNDActions.SEND_PAYMENT_STATUS_LND,
                payload: sendRes
              };
            } else {
              if (action.payload.fromDialog) {
                this.handleErrorWithoutAlert('SendPayment', action.payload.uiMessage, 'Send Payment Failed.', sendRes.payment_error);
              } else {
                this.handleErrorWithAlert('SendPayment', action.payload.uiMessage, 'Send Payment Failed', this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/transactions', sendRes.payment_error);
              }
              return { type: RTLActions.VOID };
            }
          } else {
            this.store.dispatch(closeSpinner({ payload: action.payload.uiMessage }));
            this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.COMPLETED } }));
            this.store.dispatch(fetchChannels());
            this.store.dispatch(fetchPayments({ payload: { max_payments: this.paymentsPageSettings?.recordsPerPage, reversed: true } }));
            if (action.payload.allowSelfPayment) {
              this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
            } else {
              let msg = 'Payment Sent Successfully.';
              if (sendRes.payment_route && sendRes.payment_route.total_fees_msat) {
                msg = 'Payment sent successfully with the total fee ' + sendRes.payment_route.total_fees_msat + ' (mSats).';
              }
              this.store.dispatch(openSnackBar({ payload: msg }));
            }
            return {
              type: LNDActions.SEND_PAYMENT_STATUS_LND,
              payload: sendRes
            };
          }
        }),
        catchError((err: any) => {
          this.logger.error('Error: ' + JSON.stringify(err));
          if (action.payload.allowSelfPayment) {
            this.handleErrorWithoutAlert('SendPayment', action.payload.uiMessage, 'Send Payment Failed.', err);
            this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
            return of({
              type: LNDActions.SEND_PAYMENT_STATUS_LND,
              payload: { error: this.commonService.extractErrorMessage(err) }
            });
          } else {
            if (action.payload.fromDialog) {
              this.handleErrorWithoutAlert('SendPayment', action.payload.uiMessage, 'Send Payment Failed.', err);
            } else {
              this.handleErrorWithAlert('SendPayment', action.payload.uiMessage, 'Send Payment Failed', this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/transactions', err);
            }
            return of({ type: RTLActions.VOID });
          }
        }));
    }))
  );

  graphNodeFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_GRAPH_NODE_LND),
    mergeMap((action: { type: string, payload: { pubkey: string } }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_NODE_ADDRESS }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchGraphNode', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get<GraphNode>(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/node/' + action.payload.pubkey).pipe(
        map((graphNode: any) => {
          this.logger.info(graphNode);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_NODE_ADDRESS }));
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchGraphNode', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_GRAPH_NODE_LND,
            payload: graphNode && graphNode.node ? { node: graphNode.node } : { node: null }
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchGraphNode', UI_MESSAGES.GET_NODE_ADDRESS, 'Fetching Graph Node Failed.', err);
          return of({ type: RTLActions.VOID });
        }));
    }))
  );

  setGraphNode = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.SET_GRAPH_NODE_LND),
      map((action: { type: string, payload: { node: LightningNode } }) => {
        this.logger.info(action.payload);
        return action.payload;
      })),
    { dispatch: false }
  );

  getNewAddress = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.GET_NEW_ADDRESS_LND),
      mergeMap((action: { type: string, payload: GetNewAddress }) => {
        this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GENERATE_NEW_ADDRESS }));
        return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.NEW_ADDRESS_API + '?type=' + action.payload.addressId).pipe(
          map((newAddress: any) => {
            this.logger.info(newAddress);
            this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GENERATE_NEW_ADDRESS }));
            return {
              type: LNDActions.SET_NEW_ADDRESS_LND,
              payload: (newAddress && newAddress.address) ? newAddress.address : {}
            };
          }),
          catchError((err: any) => {
            this.handleErrorWithAlert('GetNewAddress', UI_MESSAGES.GENERATE_NEW_ADDRESS, 'Generate New Address Failed', this.CHILD_API_URL + API_END_POINTS.NEW_ADDRESS_API + '?type=' + action.payload.addressId, err);
            return of({ type: RTLActions.VOID });
          }));
      }))
  );

  setNewAddress = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.SET_NEW_ADDRESS_LND),
      map((action: { type: string, payload: string }) => {
        this.logger.info(action.payload);
        return action.payload;
      })),
    { dispatch: false }
  );

  SetChannelTransaction = createEffect(() => this.actions.pipe(
    ofType(LNDActions.SET_CHANNEL_TRANSACTION_LND),
    mergeMap((action: { type: string, payload: ChannelsTransaction }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SEND_FUNDS }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SetChannelTransaction', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.post(
        this.CHILD_API_URL + API_END_POINTS.TRANSACTIONS_API,
        { amount: action.payload.amount, address: action.payload.address, sendAll: action.payload.sendAll, fees: action.payload.fees, blocks: action.payload.blocks }
      ).pipe(
        map((postRes: any) => {
          this.logger.info(postRes);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SetChannelTransaction', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SEND_FUNDS }));
          this.store.dispatch(fetchTransactions());
          this.store.dispatch(fetchBalanceBlockchain());
          this.store.dispatch(fetchChannels());
          return {
            type: LNDActions.SET_CHANNEL_TRANSACTION_RES_LND,
            payload: postRes
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('SetChannelTransaction', UI_MESSAGES.SEND_FUNDS, 'Sending Fund Failed.', err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  fetchForwardingHistory = createEffect(() => this.actions.pipe(
    ofType(LNDActions.GET_FORWARDING_HISTORY_LND),
    mergeMap((action: { type: string, payload: SwitchReq }) => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchForwardingHistory', status: APICallStatusEnum.INITIATED } }));
      const queryHeaders: SwitchReq = {
        num_max_events: action.payload.num_max_events, index_offset: action.payload.index_offset, end_time: action.payload.end_time, start_time: action.payload.start_time
      };
      return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.SWITCH_API, queryHeaders).pipe(
        map((fhRes: any) => {
          this.logger.info(fhRes);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchForwardingHistory', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_FORWARDING_HISTORY_LND,
            payload: fhRes
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('FetchForwardingHistory', UI_MESSAGES.NO_SPINNER, 'Get Forwarding History Failed', this.CHILD_API_URL + API_END_POINTS.SWITCH_API, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  queryRoutesFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.GET_QUERY_ROUTES_LND),
    mergeMap((action: { type: string, payload: GetQueryRoutes }) => {
      let url = this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/routes/' + action.payload.destPubkey + '/' + action.payload.amount;
      if (action.payload.outgoingChanId) {
        url = url + '?outgoing_chan_id=' + action.payload.outgoingChanId;
      }
      return this.httpClient.get(url).pipe(
        map((qrRes: any) => {
          this.logger.info(qrRes);
          return {
            type: LNDActions.SET_QUERY_ROUTES_LND,
            payload: qrRes
          };
        }),
        catchError((err: any) => {
          this.store.dispatch(setQueryRoutes({ payload: { routes: [] } }));
          this.handleErrorWithAlert('GetQueryRoutes', UI_MESSAGES.NO_SPINNER, 'Get Query Routes Failed', this.CHILD_API_URL + API_END_POINTS.NETWORK_API, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  setQueryRoutes = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.SET_QUERY_ROUTES_LND),
      map((action: { type: string, payload: QueryRoutes }) => action.payload)),
    { dispatch: false }
  );

  genSeed = createEffect(() => this.actions.pipe(
    ofType(LNDActions.GEN_SEED_LND),
    mergeMap((action: { type: string, payload: string }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GEN_SEED }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/genseed/' + action.payload).pipe(
        map((postRes: any) => {
          this.logger.info('Generated GenSeed!');
          this.logger.info(postRes);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GEN_SEED }));
          return {
            type: LNDActions.GEN_SEED_RESPONSE_LND,
            payload: postRes.cipher_seed_mnemonic
          };
        }),
        catchError((err) => {
          this.handleErrorWithAlert('GenSeed', UI_MESSAGES.GEN_SEED, 'Genseed Generation Failed', this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/genseed/' + action.payload, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  updateSelNodeOptions = createEffect(() => this.actions.pipe(
    ofType(RTLActions.UPDATE_SELECTED_NODE_OPTIONS),
    mergeMap(() => this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/updateSelNodeOptions').pipe(
      map((postRes: any) => {
        this.logger.info('Update Sel Node Successfull');
        this.logger.info(postRes);
        return {
          type: RTLActions.VOID
        };
      }),
      catchError((err) => {
        this.handleErrorWithAlert('UpdateSelectedNodeOptions', UI_MESSAGES.NO_SPINNER, 'Update macaroon for newly initialized node failed! Please check the macaroon path and restart the server!', 'Update Macaroon', err);
        return of({ type: RTLActions.VOID });
      })
    )))
  );

  genSeedResponse = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.GEN_SEED_RESPONSE_LND),
      map((action: { type: string, payload: Array<string> }) => action.payload)),
    { dispatch: false }
  );

  initWalletRes = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.INIT_WALLET_RESPONSE_LND),
      map((action: { type: string, payload: string }) => action.payload)),
    { dispatch: false }
  );

  initWallet = createEffect(() => this.actions.pipe(
    ofType(LNDActions.INIT_WALLET_LND),
    mergeMap((action: { type: string, payload: InitWallet }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.INITIALIZE_WALLET }));
      return this.httpClient.post(
        this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/wallet/initwallet',
        {
          wallet_password: action.payload.pwd,
          cipher_seed_mnemonic: action.payload.cipher ? action.payload.cipher : '',
          aezeed_passphrase: action.payload.passphrase ? action.payload.passphrase : ''
        }
      ).pipe(
        map((postRes) => {
          this.logger.info(postRes);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.INITIALIZE_WALLET }));
          return {
            type: LNDActions.INIT_WALLET_RESPONSE_LND,
            payload: postRes
          };
        }),
        catchError((err) => {
          this.handleErrorWithAlert('InitWallet', UI_MESSAGES.INITIALIZE_WALLET, 'Wallet Initialization Failed', this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/initwallet', err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  unlockWallet = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.UNLOCK_WALLET_LND),
      mergeMap((action: { type: string, payload: { pwd: string } }) => {
        this.store.dispatch(openSpinner({ payload: UI_MESSAGES.UNLOCK_WALLET }));
        return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/wallet/unlockwallet', { wallet_password: action.payload.pwd }).pipe(
          map((postRes) => {
            this.logger.info(postRes);
            this.logger.info('Successfully Unlocked!');
            this.sessionService.setItem('lndUnlocked', 'true');
            this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.UNLOCK_WALLET }));
            this.store.dispatch(openSpinner({ payload: UI_MESSAGES.WAIT_SYNC_NODE }));
            setTimeout(() => {
              this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.WAIT_SYNC_NODE }));
              this.store.dispatch(fetchInfoLND({ payload: { loadPage: 'HOME' } }));
            }, 5000);
            return { type: RTLActions.VOID };
          }),
          catchError((err) => {
            this.handleErrorWithAlert('UnlockWallet', UI_MESSAGES.UNLOCK_WALLET, 'Unlock Wallet Failed', this.CHILD_API_URL + API_END_POINTS.WALLET_API + '/unlockwallet', err);
            return of({ type: RTLActions.VOID });
          })
        );
      })),
    { dispatch: false }
  );

  peerLookup = createEffect(() => this.actions.pipe(
    ofType(LNDActions.PEER_LOOKUP_LND),
    mergeMap((action: { type: string, payload: string }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SEARCHING_NODE }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/node/' + action.payload).pipe(
        map((resPeer) => {
          this.logger.info(resPeer);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SEARCHING_NODE }));
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_LOOKUP_LND,
            payload: resPeer
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('Lookup', UI_MESSAGES.SEARCHING_NODE, 'Peer Lookup Failed', this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/node/' + action.payload, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  channelLookup = createEffect(() => this.actions.pipe(
    ofType(LNDActions.CHANNEL_LOOKUP_LND),
    mergeMap((action: { type: string, payload: ChannelLookup }) => {
      this.store.dispatch(openSpinner({ payload: action.payload.uiMessage }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/edge/' + action.payload.channelID).pipe(
        map((resChannel) => {
          this.logger.info(resChannel);
          this.store.dispatch(closeSpinner({ payload: action.payload.uiMessage }));
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_LOOKUP_LND,
            payload: resChannel
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('Lookup', action.payload.uiMessage, 'Channel Lookup Failed', this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/edge/' + action.payload.channelID, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  invoiceLookup = createEffect(() => this.actions.pipe(
    ofType(LNDActions.INVOICE_LOOKUP_LND),
    mergeMap((action: { type: string, payload: { openSnackBar: boolean, paymentHash?: string, paymentAddress?: string } }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SEARCHING_INVOICE }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
      let lookupUrl = this.CHILD_API_URL + API_END_POINTS.INVOICES_API + '/lookup';
      if (action.payload.paymentAddress && action.payload.paymentAddress !== '') {
        lookupUrl = lookupUrl + '?payment_addr=' + action.payload.paymentAddress;
      } else {
        lookupUrl = lookupUrl + '?payment_hash=' + action.payload.paymentHash;
      }
      return this.httpClient.get(lookupUrl).pipe(
        map((resInvoice) => {
          this.logger.info(resInvoice);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SEARCHING_INVOICE }));
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(updateInvoice({ payload: resInvoice }));
          return {
            type: LNDActions.SET_LOOKUP_LND,
            payload: resInvoice
          };
        }),
        catchError((err: any) => {
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.ERROR } }));
          this.handleErrorWithoutAlert('Lookup', UI_MESSAGES.SEARCHING_INVOICE, 'Invoice Lookup Failed', err);
          if (action.payload.openSnackBar) {
            this.store.dispatch(openSnackBar({ payload: { message: 'Invoice Refresh Failed.', type: 'ERROR' } }));
          }
          return of({
            type: LNDActions.SET_LOOKUP_LND,
            payload: { error: err }
          });
        })
      );
    }))
  );

  paymentLookup = createEffect(() => this.actions.pipe(
    ofType(LNDActions.PAYMENT_LOOKUP_LND),
    mergeMap((action: { type: string, payload: string }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SEARCHING_PAYMENT }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.PAYMENTS_API + '/lookup/' + action.payload).pipe(
        map((resPayment: any) => {
          this.logger.info(resPayment);
          this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SEARCHING_PAYMENT }));
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
          this.store.dispatch(updatePayment({ payload: resPayment }));
          return {
            type: LNDActions.SET_LOOKUP_LND,
            payload: resPayment
          };
        }),
        catchError((err: any) => {
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.ERROR } }));
          this.handleErrorWithoutAlert('Lookup', UI_MESSAGES.SEARCHING_PAYMENT, 'Payment Lookup Failed', err);
          return of({
            type: LNDActions.SET_LOOKUP_LND,
            payload: { error: err }
          });
        })
      );
    }))
  );

  setLookup = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.SET_LOOKUP_LND),
      map((action: { type: string, payload: any }) => {
        this.logger.info(action.payload);
        return action.payload;
      })),
    { dispatch: false }
  );

  getRestoreChannelList = createEffect(() => this.actions.pipe(
    ofType(LNDActions.RESTORE_CHANNELS_LIST_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'RestoreChannelsList', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API + '/restore/list').pipe(
        map((resRestoreList) => {
          this.logger.info(resRestoreList);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'RestoreChannelsList', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_RESTORE_CHANNELS_LIST_LND,
            payload: (resRestoreList) ? resRestoreList : { all_restore_exists: false, files: [] }
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithAlert('RestoreChannelsList', UI_MESSAGES.NO_SPINNER, 'Restore Channels List Failed', this.CHILD_API_URL + API_END_POINTS.CHANNELS_BACKUP_API, err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  setRestoreChannelList = createEffect(
    () => this.actions.pipe(
      ofType(LNDActions.SET_RESTORE_CHANNELS_LIST_LND),
      map((action: { type: string, payload: SetRestoreChannelsList }) => {
        this.logger.info(action.payload);
        return action.payload;
      })),
    { dispatch: false }
  );

  allLightningTransactionsFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.GET_ALL_LIGHTNING_TRANSATIONS_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchLightningTransactions', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.PAYMENTS_API + '/alltransactions').pipe(
        map((response: any) => {
          this.logger.info(response);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchLightningTransactions', status: APICallStatusEnum.COMPLETED } }));
          return {
            type: LNDActions.SET_ALL_LIGHTNING_TRANSATIONS_LND,
            payload: response
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchLightningTransactions', UI_MESSAGES.NO_SPINNER, 'Fetching All Lightning Transaction Failed.', err);
          return of({ type: RTLActions.VOID });
        })
      );
    }))
  );

  pageSettingsFetch = createEffect(() => this.actions.pipe(
    ofType(LNDActions.FETCH_PAGE_SETTINGS_LND),
    mergeMap(() => {
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPageSettings', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.get(API_END_POINTS.PAGE_SETTINGS_API).pipe(
        map((settings: any) => {
          this.logger.info(settings);
          this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'FetchPageSettings', status: APICallStatusEnum.COMPLETED } }));
          this.invoicesPageSettings = (settings && Object.keys(settings).length > 0 ? (settings.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'invoices')) :
            LND_DEFAULT_PAGE_SETTINGS.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'invoices'));
          this.paymentsPageSettings = (settings && Object.keys(settings).length > 0 ? (settings.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'payments')) :
            LND_DEFAULT_PAGE_SETTINGS.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'payments'));
          return {
            type: LNDActions.SET_PAGE_SETTINGS_LND,
            payload: settings || []
          };
        }),
        catchError((err: any) => {
          this.handleErrorWithoutAlert('FetchPageSettings', UI_MESSAGES.NO_SPINNER, 'Fetching Page Settings Failed.', err);
          return of({ type: RTLActions.VOID });
        })
      );
    })
  ));

  savePageSettings = createEffect(() => this.actions.pipe(
    ofType(LNDActions.SAVE_PAGE_SETTINGS_LND),
    mergeMap((action: { type: string, payload: any }) => {
      this.store.dispatch(openSpinner({ payload: UI_MESSAGES.UPDATE_PAGE_SETTINGS }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SavePageSettings', status: APICallStatusEnum.INITIATED } }));
      return this.httpClient.post(API_END_POINTS.PAGE_SETTINGS_API, action.payload).
        pipe(
          map((postRes: any) => {
            this.logger.info(postRes);
            this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SavePageSettings', status: APICallStatusEnum.COMPLETED } }));
            this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.UPDATE_PAGE_SETTINGS }));
            this.store.dispatch(openSnackBar({ payload: 'Page Layout Updated Successfully!' }));
            const invPgSz = (postRes.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'invoices') || LND_DEFAULT_PAGE_SETTINGS.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'invoices')).recordsPerPage;
            const payPgSz = (postRes.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'payments') || LND_DEFAULT_PAGE_SETTINGS.find((page) => page.pageId === 'transactions')?.tables.find((table) => table.tableId === 'payments')).recordsPerPage;
            if (this.invoicesPageSettings && invPgSz !== this.invoicesPageSettings?.recordsPerPage) {
              this.invoicesPageSettings.recordsPerPage = invPgSz;
              this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
            }
            if (this.paymentsPageSettings && payPgSz !== this.paymentsPageSettings?.recordsPerPage) {
              this.paymentsPageSettings.recordsPerPage = payPgSz;
            }
            return {
              type: LNDActions.SET_PAGE_SETTINGS_LND,
              payload: postRes || []
            };
          }),
          catchError((err: any) => {
            this.handleErrorWithAlert('SavePageSettings', UI_MESSAGES.UPDATE_PAGE_SETTINGS, 'Page Settings Update Failed.', API_END_POINTS.PAGE_SETTINGS_API, err);
            return of({ type: RTLActions.VOID });
          })
        );
    })
  ));

  initializeRemainingData(info: any, landingPage: string) {
    this.sessionService.setItem('lndUnlocked', 'true');
    const node_data = {
      identity_pubkey: info.identity_pubkey,
      alias: info.alias,
      testnet: info.testnet,
      chains: info.chains,
      uris: info.uris,
      version: (!info.version) ? '' : info.version.split(' ')[0]
    };
    this.store.dispatch(openSpinner({ payload: UI_MESSAGES.INITALIZE_NODE_DATA }));
    this.store.dispatch(setNodeData({ payload: node_data }));
    let newRoute = this.location.path();
    if (newRoute.includes('/cln/')) {
      newRoute = newRoute?.replace('/cln/', '/lnd/');
    } else if (newRoute.includes('/ecl/')) {
      newRoute = newRoute?.replace('/ecl/', '/lnd/');
    }
    if (newRoute.includes('/unlock') || newRoute.includes('/login') || newRoute.includes('/error') || newRoute === '' || landingPage === 'HOME' || newRoute.includes('?access-key=')) {
      newRoute = '/lnd/home';
    }
    this.router.navigate([newRoute]);
    this.store.dispatch(fetchBalanceBlockchain());
    this.store.dispatch(fetchChannels());
    this.store.dispatch(fetchPendingChannels());
    this.store.dispatch(fetchClosedChannels());
    this.store.dispatch(fetchPeers());
    this.store.dispatch(fetchNetwork());
    this.store.dispatch(fetchFees()); // Fetches monthly forwarding history as well, to count total number of events
    this.store.dispatch(fetchPayments({ payload: { max_payments: 100000, reversed: true } }));
    this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
  }

  handleErrorWithoutAlert(actionName: string, uiMessage: string, genericErrorMessage: string, err: { status: number, error: any }) {
    this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err));
    if (err.status === 401) {
      this.logger.info('Redirecting to Login');
      this.store.dispatch(closeAllDialogs());
      this.store.dispatch(logout());
      this.store.dispatch(openSnackBar({ payload: 'Authentication Failed. Redirecting to Login.' }));
    } else {
      this.store.dispatch(closeSpinner({ payload: uiMessage }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: actionName, status: APICallStatusEnum.ERROR, statusCode: err.status.toString(), message: this.commonService.extractErrorMessage(err, genericErrorMessage) } }));
    }
  }

  handleErrorWithAlert(actionName: string, uiMessage: string, alertTitle: string, errURL: string, err: { status: number, error: any }) {
    this.logger.error(err);
    if (err.status === 401) {
      this.logger.info('Redirecting to Login');
      this.store.dispatch(closeAllDialogs());
      this.store.dispatch(logout());
      this.store.dispatch(openSnackBar({ payload: 'Authentication Failed. Redirecting to Login.' }));
    } else {
      this.store.dispatch(closeSpinner({ payload: uiMessage }));
      const errMsg = this.commonService.extractErrorMessage(err);
      this.store.dispatch(openAlert({
        payload: {
          data: {
            type: 'ERROR',
            alertTitle: alertTitle,
            message: { code: err.status, message: errMsg, URL: errURL },
            component: ErrorMessageComponent
          }
        }
      }));
      this.store.dispatch(updateLNDAPICallStatus({ payload: { action: actionName, status: APICallStatusEnum.ERROR, statusCode: err.status.toString(), message: errMsg, URL: errURL } }));
    }
  }

  ngOnDestroy() {
    this.unSubs.forEach((completeSub) => {
      completeSub.next(<any>null);
      completeSub.complete();
    });
  }

}
