import React, { useCallback, useEffect, useRef } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { createStructuredSelector } from 'reselect';
import { NavigationProp, ParamListBase } from '@react-navigation/native';
import { withReducer } from '../../app/core/with-reducer';
import { withEpics } from '../../app/core/with-epics';
import { fp as _ } from '../../common/utils/fp';
import { Search } from '../components';
import reducer, { ACTION } from '../reducer';
import * as epics from '../epics';
import { withErrorBoundary } from '../../common/hoc/with-error-boundary';
import { withNavigationSync } from '../../common/hoc/with-navigation-sync';
import {
  makeRecentTracksExtended,
  makeActiveVisibilitySelector,
  makeTracksExtended,
  makeActiveTabSelector,
  makePlaylistsExtendedSelector,
  makeRecentPlaylistsExtendedSelector,
  makeTracksConfigSelector,
} from '../selectors';
import { useNavigationFlush } from '../../common/hooks/use-navigation-flush';
import {
  TActiveTab,
  TActiveVisibilityConfig,
  TGetAnyData,
  TGoToPlaylist,
  TGoToTrack,
  TPlaylistData,
  TRootSearchState,
  TRoute,
  TState,
} from '../types';
import { composeContexts } from '../../common/utils/react-better-context';
import {
  ContextHeader,
  ContextPlaylists,
  ContextRecentPlaylists,
  ContextRecentTracks,
  ContextTracks,
} from '../contexts';
import { withStarred } from '../../common/submodules/starring-tracks/hoc/with-starred';
import { NEntityInfinite, TTrackParams } from '../../common/types';
import { useNavKeyMap } from '../../common/hooks/use-nav-key-map';
import { ROUTE_PARAMS, ROUTES } from '../../app/constants';
import { ACTIVE_TAB, SEARCH_VARIANT } from '../constants';
import { FETCH_TYPE } from '../../common/constants';
import { useOnUserMarketChange } from '../../common/hooks/use-on-user-market-change';
import {
  Router,
  tempParamsCache$,
} from '../../common/utils/navigation-service';
import { isBrokenIsrc } from '../../common/transducers';
import { PAGES, updatePageMeta } from '../../analytics';
import { withStarredPlaylists } from '../../common/submodules/starring-playlists/hoc/with-starred-playlists';
import { useSentryRenderRecursionDetector } from '../../common/utils/debuggers/sentry-logger';
import { EPlaylistSourcePage } from '../../analytics/types';

type TPropsExternal = {};
type TStructured = {
  activeVisibility: TActiveVisibilityConfig;
  recentTracks: TState['recentTracks'];
  tracks: TState['tracks'];
  activeTab: TActiveTab;
  recentPlaylists: TState['recentPlaylists'];
  playlists: TState['playlists'];
  config: TState['tracks']['config'];
};
type TConnected = TPropsExternal &
  TStructured & {
    getRecentTracks: () => void;
    getRecentPlaylists: () => void;
    flush: () => void;
    refresh: () => void;
    getTracks: NEntityInfinite.TEntityInfiniteGet;
    navigation: NavigationProp<ParamListBase>;
    route: TRoute;
    saveToRecentTracks: (params: TTrackParams) => void;
    getPlaylists: NEntityInfinite.TEntityInfiniteGet;
    saveToRecentPlaylists: (params: TPlaylistData) => void;
    getData: TGetAnyData;
  };

const mapStateToProps = createStructuredSelector<TRootSearchState, TStructured>(
  {
    activeVisibility: makeActiveVisibilitySelector(),
    recentTracks: makeRecentTracksExtended(),
    tracks: makeTracksExtended(),
    recentPlaylists: makeRecentPlaylistsExtendedSelector(),
    playlists: makePlaylistsExtendedSelector(),
    activeTab: makeActiveTabSelector(),
    config: makeTracksConfigSelector(),
    //†prop
  }
);
const mapDispatchToProps = {
  getRecentTracks: ACTION.getRecentTracks,
  getRecentPlaylists: ACTION.getRecentPlaylists,
  flush: ACTION.flush,
  refresh: ACTION.refresh,
  getTracks: ACTION.getTracks,
  saveToRecentTracks: ACTION.saveToRecentTracks,
  getPlaylists: ACTION.getPlaylists,
  saveToRecentPlaylists: ACTION.saveToRecentPlaylists,
  getData: ACTION.getData,
  //†action
};

const Connected: React.FC<TConnected> = ({
  recentTracks,
  tracks,
  recentPlaylists,
  playlists,
  getRecentTracks,
  getRecentPlaylists,
  getTracks,
  getPlaylists,
  flush,
  activeTab,
  saveToRecentTracks,
  saveToRecentPlaylists,
  ...rest
}) => {
  const { navigation, route, activeVisibility } = rest;
  const { variant } = activeVisibility;
  const searchVariant = _.path('params.searchVariant', route);
  const searchVariantRef = useRef(searchVariant);

  useNavKeyMap({ route, page: ROUTES.search });

  useEffect(() => {
    searchVariantRef.current = searchVariant;
  }, [searchVariant]);

  useNavigationFlush({
    getData: () => {
      setTimeout(() => {
        const isPlaylistsTabOpened =
          searchVariantRef.current === ACTIVE_TAB.playlists ||
          tempParamsCache$.value[ROUTE_PARAMS.searchVariant] ===
            ACTIVE_TAB.playlists;
        tempParamsCache$.next({});
        const defaultTab = isPlaylistsTabOpened
          ? ACTIVE_TAB.playlists
          : ACTIVE_TAB.tracks;

        if (_.isNil(searchVariantRef.current)) {
          navigation.setParams({
            [ROUTE_PARAMS.searchVariant]: defaultTab,
          });
        }
        updatePageMeta({
          page: PAGES.Searchtab.page,
          search_tab_opened: _.capitalize(defaultTab),
        });
      });
      // getRecentTracks();
    },
    flush: () => {
      flush();
      navigation.setParams({
        [ROUTE_PARAMS.searchVariant]: undefined,
      });
    },
    navigation,
    route,
    // key: 'search-new',
  });

  useSentryRenderRecursionDetector('search');

  useOnUserMarketChange({
    page: ROUTES.search,
    onChange: () => {
      if (variant === SEARCH_VARIANT.tracks) {
        flush();
        getRecentTracks();
      }
      if (variant === SEARCH_VARIANT.playlists) {
        flush();
        getRecentPlaylists();
      }
    },
  });

  const getData = useCallback(() => {
    if (variant === SEARCH_VARIANT.tracks) {
      getTracks(null, FETCH_TYPE.refill);
    }
    if (variant === SEARCH_VARIANT.playlists) {
      getPlaylists(null, FETCH_TYPE.refill);
    }
  }, [variant]);

  const goToPlaylist: TGoToPlaylist = useCallback(
    (params: TPlaylistData) => {
      saveToRecentPlaylists(params);
      Router.goTo(ROUTES.playlist, {
        [ROUTE_PARAMS.playlistrootId]: params.id,
        [ROUTE_PARAMS.playlistrootVendor]: params.vendor,
        [ROUTE_PARAMS.playlistrootMarket]: params.country_code,
        playlistName: params.name,
      });

      updatePageMeta({
        page: PAGES.Playlist.page,
        source_page:
          variant === SEARCH_VARIANT.recentPlaylists
            ? EPlaylistSourcePage.recentSearch
            : EPlaylistSourcePage.search,
        playlist_id: _.path('id', params),
        playlist_vendor: _.capitalizeSafe(_.path('vendor', params)),
        playlist_name: _.path('name', params),
        playlist_personalized: !!_.path('personalized', params),
      });
    },
    [variant]
  );

  const goToTrack: TGoToTrack = useCallback(
    params => {
      if (isBrokenIsrc(params.isrc)) {
        return Router.goTo(ROUTES.emptytrack);
      }

      const { variant } = activeVisibility;

      saveToRecentTracks(params);

      Router.goTo(ROUTES.track, params);
      updatePageMeta({
        page: PAGES.Track.page,
        source:
          variant === SEARCH_VARIANT.recentTracks ? 'Recentsearch' : 'Search',
        track_id: params.trackId,
        track_name: params.name,
        track_artist: params.artist,
        is_starred: params.starred,
        isrc: params.isrc,
      });
    },
    [variant]
  );

  return composeContexts(
    [
      [
        ContextHeader,
        {
          activeVisibility,
          entityTracks: tracks,
          entityPlaylists: playlists,
          getTracks,
          getPlaylists,
          activeTab,
        },
      ],
      [
        ContextRecentTracks,
        {
          visibility: activeVisibility.visibility,
          entity: recentTracks,
          goToTrack,
        },
      ],
      [
        ContextTracks,
        {
          visibility: activeVisibility.visibility,
          entity: tracks,
          goToTrack,
        },
      ],
      [
        ContextRecentPlaylists,
        {
          visibility: activeVisibility.visibility,
          entity: recentPlaylists,
          goToPlaylist,
        },
      ],
      [
        ContextPlaylists,
        {
          visibility: activeVisibility.visibility,
          entity: playlists,
          goToPlaylist,
        },
      ],
    ],
    <Search {...rest} getData={getData} getAnyData={rest.getData} />
  );
};

export default compose(
  _.hoistStatics(Search),
  withReducer('search', reducer),
  withEpics('search', epics),
  withErrorBoundary('search'),
  withNavigationSync(),
  withStarred,
  withStarredPlaylists,
  connect(
    mapStateToProps,
    mapDispatchToProps
  )
)(Connected) as React.FC<TPropsExternal>;
