import React, { useCallback } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { createStructuredSelector } from 'reselect';
import { Nullable, Undefinable } from 'tsdef';
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 { Starred } from '../components';
import reducer, { ACTION } from '../reducer';
import * as epics from '../epics';
import {
  makeRefreshSelector,
  makeActiveTabSelector,
  makeActiveVisibilitySelector,
  makePlaylistsOptimisticSelector,
  makeTracksWithDistributorsSelector,
} from '../selectors';
import { makeLastStreamDateRangeWeekSelector } from '../../common/selectors/stream-latest-date/stream-latest-date-selectors';
import { useNavigationFlush } from '../../common/hooks/use-navigation-flush';
import { withErrorBoundary } from '../../common/hoc/with-error-boundary';
import {
  makeMarketNameShortSelector,
  makeMarketSelector,
} from '../../common/selectors';
import { useOnUserMarketChange } from '../../common/hooks/use-on-user-market-change';
import { ROUTE_PARAMS, ROUTES } from '../../app/constants';
import { useNavKeyMap } from '../../common/hooks/use-nav-key-map';
import { NEntityInfinite, TMarketUndef, TTimerange } from '../../common/types';
import {
  TActiveTab,
  TActiveVisibilityConfig,
  TRootStarredState,
  TRoute,
  TState,
} from '../types';
import { withStreamLatestDate } from '../../common/hoc/with-stream-latest-date';
import { withStarred } from '../../common/submodules/starring-tracks/hoc/with-starred';
import { FETCH_TYPE } from '../../common/constants';
import { ACTION as COMMON_ACTION } from '../../common/reducer';
import { PAGES, updatePageMeta } from '../../analytics';
import { ACTIVE_TAB, SEARCH_VARIANT } from '../../search/constants';
import { TGoToPlaylist, TGoToTrack, TPlaylistData } from '../../search/types';
import { Router } from '../../common/utils/navigation-service';
import { isBrokenIsrc } from '../../common/transducers';
import { STARRED_VARIANT } from '../constants';
import { composeContexts } from '../../common/utils/react-better-context';
import { ContextHeader, ContextTracks, ContextPlaylists } from '../contexts';
import { withStarredPlaylists } from '../../common/submodules/starring-playlists/hoc/with-starred-playlists';
import { withNavigationSync } from '../../common/hoc/with-navigation-sync';
import { TToggleStarred } from '../../common/submodules/starring-tracks/types';
import { useSentryRenderRecursionDetector } from '../../common/utils/debuggers/sentry-logger';
import { EPlaylistSourcePage } from '../../analytics/types';

type TStructured = {
  tracks: TState['tracks'];
  streamRange: Nullable<TTimerange>;
  refreshing: TState['refresh'];
  market: TMarketUndef;
  marketShortName: Undefinable<string>;
  playlists: TState['playlists'];
  activeVisibility: TActiveVisibilityConfig;
  activeTab: TActiveTab;
};

type TConnected = {
  getTracks: NEntityInfinite.TEntityInfiniteGet;
  getPlaylists: NEntityInfinite.TEntityInfiniteGet;
  unstarTrack: () => void;
  refresh: () => void;
  flush: () => void;
  navigation: NavigationProp<ParamListBase>;
  route: TRoute;
  toggleStarred: TToggleStarred;
} & TStructured;

type TPropsExternal = {};

const mapStateToProps = createStructuredSelector<
  TRootStarredState,
  TStructured
>({
  tracks: makeTracksWithDistributorsSelector(),
  playlists: makePlaylistsOptimisticSelector(),
  streamRange: makeLastStreamDateRangeWeekSelector(),
  refreshing: makeRefreshSelector(),
  market: makeMarketSelector(),
  marketShortName: makeMarketNameShortSelector(),
  activeVisibility: makeActiveVisibilitySelector(),
  activeTab: makeActiveTabSelector(),
  //†props
});
const mapDispatchToProps = {
  getTracks: ACTION.getTracks,
  getPlaylists: ACTION.getPlaylists,
  unstarTrack: ACTION.unstarTrack,
  refresh: ACTION.refresh,
  flush: ACTION.flush,
  toggleStarred: COMMON_ACTION.toggleStarred,
  //†action
};

// Uncomment to write componentDidMount hook
const Connected: React.FC<TConnected> = ({
  getTracks,
  getPlaylists,
  tracks,
  playlists,
  flush,
  activeTab,
  streamRange,
  market,
  ...rest
}) => {
  const { navigation, route, activeVisibility } = rest;
  const { variant } = activeVisibility;

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

  useSentryRenderRecursionDetector('starred');

  useNavigationFlush({
    getData: () => {
      navigation.setParams({
        [ROUTE_PARAMS.starredVariant]: ACTIVE_TAB.tracks,
      });
      setTimeout(() => {
        updatePageMeta({
          page: PAGES.Starredtab.page,
          starred_page_tab_opened: _.capitalize(ACTIVE_TAB.tracks),
        });
      });
    },
    flush,
    navigation,
    route,
    // key: 'search-new',
  });

  useOnUserMarketChange({
    page: ROUTES.starred,
    onChange: () => {
      if (variant === STARRED_VARIANT.tracks) {
        flush();
        getTracks(null, FETCH_TYPE.refill);
      }
      if (variant === STARRED_VARIANT.playlists) {
        flush();
        getPlaylists(null, FETCH_TYPE.refill);
      }
    },
  });

  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) => {
      if (!params.name) {
        return Router.goTo(ROUTES.emptytrack, {
          [ROUTE_PARAMS.emptypageVariant]: 'playlist',
        });
      }

      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: EPlaylistSourcePage.starred,
        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;

      Router.goTo(ROUTES.track, params);
      updatePageMeta({
        page: PAGES.Track.page,
        source: 'Starred',
        track_id: params.trackId,
        track_name: params.name,
        track_artist: params.artist,
        is_starred: params.starred,
        isrc: params.isrc,
        distributor: params.distributor,
      });
    },
    [variant]
  );

  return composeContexts(
    [
      [
        ContextHeader,
        {
          activeVisibility,
          entityTracks: tracks,
          entityPlaylists: playlists,
          getTracks,
          getPlaylists,
          activeTab,
        },
      ],
      [
        ContextTracks,
        {
          visibility: activeVisibility.visibility,
          entity: tracks,
          goToTrack,
          streamRange,
          market,
        },
      ],
      [
        ContextPlaylists,
        {
          visibility: activeVisibility.visibility,
          entity: playlists,
          goToPlaylist,
        },
      ],
    ],
    <Starred {...rest} getData={getData} market={market!} />
  );
};

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