import { curveStepAfter } from 'd3-shape'
import * as df from 'date-fns'
import { createURL } from 'expo-linking'
import React, { useCallback, useContext, useState } from 'react'
import { Linking, Text, View, TouchableOpacity } from 'react-native'

import { colors } from '../Colors'
import { dspNames } from '../Consts'
import { dateDataNumberRange } from '../DateData'
import { ReactNavigationProps } from '../Navigation'
import { TextStyles } from '../Styles'
import { AutoLayout } from '../components/AutoLayout'
import { CenteredActivityIndicator } from '../components/CenteredActivityIndicator'
import { PlaylistImage } from '../components/PlaylistImage'
import { PositionChangeAndArrow } from '../components/PositionChangeAndArrow'
import { ProductImage } from '../components/ProductImage'
import {
  SummaryLegendAbsoluteValueItem,
  SummaryLegendChangePercentageItem,
  SummaryLegendItem,
  SummaryLegendPositionItem,
} from '../components/SummaryLegendItem'
import { TrackDateDataFigure } from '../components/TrackDateDataFigure'
import { PlotConfig } from '../components/figures/BarAndLineFigure'
import { ScreenNames } from '../constants/ScreenNames'
import { ArtistContext } from '../contexts/ArtistContext'
import { EventType, sendAnalyticsEvent } from '../hooks/useAnalytics'
import { usePlaylist } from '../hooks/usePlaylist'
import { useTrack } from '../hooks/useTrack'
import { useTrackPlaylistMetrics } from '../hooks/useTrackPlaylistMetrics'
import { useTrackTopCountryCodes } from '../hooks/useTrackTopCountries'
import { Strings } from '../i18n'
import { ScreenPaths } from '../linking'
import { shareContextMenuItem } from '../share'
import { streamsSummary, summaryForDate } from '../streams'
import { PlaylistType } from '../types/Playlist'
import { defaultTimeInterval, TimeInterval } from '../types/TimeInterval'
import { notEmpty, playlistUrl } from '../util'
import { formatDateString } from '../util/date'
import { GraphScreenContent } from './GraphScreenContent'

const PositionChange: React.FC<{
  position: {
    current: number | null
    previous: number | null
  }
}> = ({ position }) => {
  return position.current != null && position.previous != null ? (
    position.current !== position.previous ? (
      <PositionChangeAndArrow
        currentValue={position.current}
        previousValue={position.previous}
        invertDirection
      />
    ) : (
      <Text style={[TextStyles.bodyBold, { color: colors.white }]}>
        {Strings.Shared.NoChange}
      </Text>
    )
  ) : (
    <Text style={[TextStyles.bodyBold, { color: colors.white }]}>
      {Strings.Shared.NotApplicable}
    </Text>
  )
}

/**
 *
 * Display information for the currently selected day
 */
export const TrackPlaylistDaySummary: React.FC<{
  playlistType: PlaylistType
  streams: {
    current: number | null
    changePercentage: number | null
  }
  position?: {
    current: number | null
    previous: number | null
  }
}> = ({ streams, position, playlistType }) => {
  return (
    <View>
      <View
        style={{
          flexDirection: 'row',
          justifyContent: 'space-between',
          marginBottom: 12,
        }}
      >
        <SummaryLegendAbsoluteValueItem
          label={Strings.Shared.TotalStreams}
          value={streams.current ?? undefined}
          style={{ flex: 1 }}
        />
        <SummaryLegendChangePercentageItem
          label={Strings.Shared.Change}
          style={{ flex: 1 }}
          changePercentage={streams?.changePercentage ?? undefined}
        />
      </View>

      {playlistType !== 'algorithmic' && (
        <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
          <SummaryLegendPositionItem
            label={Strings.Shared.PositionInPlaylist}
            labelColor={colors.platform.apple}
            style={{ flex: 1 }}
            position={position}
          />
          <SummaryLegendItem
            label={Strings.Shared.PositionChange}
            labelColor={colors.platform.apple}
            style={{ flex: 1 }}
          >
            {position != null ? <PositionChange position={position} /> : null}
          </SummaryLegendItem>
        </View>
      )}

      {playlistType !== 'algorithmic' && position == null ? (
        <Text
          style={[
            TextStyles.bodySmall,
            {
              color: colors.white,
              marginTop: 12,
            },
          ]}
        >
          {Strings.TrackPlaylistScreen.PressTheChartToSeePositionInPlaylist}
        </Text>
      ) : null}
    </View>
  )
}

function useTrackPlaylistScreenMetrics(
  isrc: string,
  playlistId: string,
  market: string,
  selectedInterval: Interval
) {
  const metrics = useTrackPlaylistMetrics(
    isrc,
    playlistId,
    market,
    selectedInterval
  ).data?.artist?.track?.playlist?.metrics

  if (metrics == null)
    return {
      currentIntervalStreams: undefined,
      previousIntervalStreams: undefined,
      currentIntervalPlaylistPositions: undefined,
      previousIntervalPlaylistPositions: undefined,
    }

  const [currentIntervalStreams, previousIntervalStreams] = metrics?.map(
    (metricsForInterval) =>
      metricsForInterval.map(({ date, streams }) => ({ date, data: streams }))
  )

  const [currentIntervalPlaylistPositions, previousIntervalPlaylistPositions] =
    metrics?.map((metricsForInterval) =>
      metricsForInterval.map(({ date, currentPosition }) => ({
        date,
        data: currentPosition,
      }))
    )

  return {
    currentIntervalStreams,
    previousIntervalStreams,
    currentIntervalPlaylistPositions,
    previousIntervalPlaylistPositions,
  }
}

const SelectedDateSummary: React.FC<{
  isrc: string
  playlistId: string
  playlistType: PlaylistType
  countryCode: string
  selectedInterval: Interval
  selectedTimeInterval: TimeInterval
  selectedBar: number | null
}> = ({
  isrc,
  playlistId,
  playlistType,
  countryCode,
  selectedInterval,
  selectedBar,
}) => {
  const selectedDate = selectedBar
    ? formatDateString(df.eachDayOfInterval(selectedInterval)[selectedBar])
    : null

  const { currentIntervalStreams, currentIntervalPlaylistPositions } =
    useTrackPlaylistScreenMetrics(
      isrc,
      playlistId,
      countryCode,
      selectedInterval
    )

  const streamsSummary =
    currentIntervalStreams != null && selectedDate != null
      ? summaryForDate(currentIntervalStreams, selectedDate)
      : { current: null, previous: null, changePercentage: null }

  const playlistPositionSummary =
    currentIntervalPlaylistPositions != null && selectedDate != null
      ? summaryForDate(currentIntervalPlaylistPositions, selectedDate)
      : { current: null, previous: null, changePercentage: null }

  return (
    <TrackPlaylistDaySummary
      streams={streamsSummary}
      position={playlistPositionSummary}
      playlistType={playlistType}
    />
  )
}

export const TrackPlaylistIntervalSummary: React.FC<{
  isrc: string
  playlistId: string
  countryCode: string
  selectedInterval: Interval
  playlistType: PlaylistType
}> = ({ isrc, playlistId, countryCode, selectedInterval, playlistType }) => {
  const { currentIntervalStreams, previousIntervalStreams } =
    useTrackPlaylistScreenMetrics(
      isrc,
      playlistId,
      countryCode,
      selectedInterval
    )

  const selectedIntervalSummary =
    currentIntervalStreams != null && previousIntervalStreams != null
      ? streamsSummary(currentIntervalStreams, previousIntervalStreams)
      : {
          currentIntervalStreamsSum: null,
          previous: null,
          changePercentage: null,
        }

  const selectedDateStreams = selectedIntervalSummary?.currentIntervalStreamsSum
  const selectedDateStreamsChangePercentage =
    selectedIntervalSummary?.changePercentage

  return selectedDateStreams != null ? (
    <TrackPlaylistDaySummary
      playlistType={playlistType}
      streams={{
        current: selectedDateStreams,
        changePercentage:
          selectedDateStreamsChangePercentage != null
            ? Math.round(selectedDateStreamsChangePercentage)
            : null,
      }}
    />
  ) : null
}

export const TrackPlaylistScreen: React.FC<
  ReactNavigationProps<ScreenNames.TrackPlaylist>
> = ({ navigation, route }) => {
  const screenName = 'Track Playlist Screen'
  const { isrc, playlist_id: playlistId } = route.params
  const artist = useContext(ArtistContext)
  const countryCode = route.params.market ?? 'worldwide'

  const maxInterval = defaultTimeInterval //Longest interval represented in the graph
  const [selectedTimeInterval, selectTimeInterval] =
    useState<TimeInterval>(maxInterval)

  const { data, retry, refreshing, showToast, error } = useTrack(isrc)

  const onPullToRefresh = () => {
    sendAnalyticsEvent(EventType.PULL_TO_REFRESH, {
      artist_selected: artist.name,
      page: screenName,
      subject: 'user',
      verb: 'refreshed',
      object: 'playlist',
    })
    retry()
  }

  const track = data?.artist?.track

  const { data: playlistData } = usePlaylist(playlistId)
  const playlist = playlistData?.playlist
  const playlistName = playlist?.name ?? ''
  const playlistType = playlist?.playlistType
  const dsp = playlist?.dsp
  const trackTopMarketCountryCodes = useTrackTopCountryCodes(
    isrc,
    defaultTimeInterval
  )

  const handleOnSelectTimeInterval = useCallback((selectedTimeInterval) => {
    sendAnalyticsEvent(EventType.GRAPH_TIMEFRAME_CHANGED, {
      artist_selected: artist.name,
      timeframe_selected: selectedTimeInterval,
      entity_id: isrc,
      screen_name: screenName,
      subject: 'user',
      verb: 'selected',
      object: 'graph_timeframe',
    })
  }, [])

  return (
    <GraphScreenContent
      screenName={screenName}
      screenTitle={track?.name}
      title={playlistName}
      headerImage={
        track != null && playlist != null ? (
          <AutoLayout style={{ flexDirection: 'row' }} gap={8}>
            <ProductImage
              product={track.product}
              isExplicit={track.isExplicit}
              size={48}
            />

            <TouchableOpacity
              onPress={() => {
                const url = playlistUrl(playlist)
                if (url) {
                  sendAnalyticsEvent(EventType.PLAYLIST_OPENED, {
                    isrc: track.isrc,
                    playlist_name: playlist.name,
                    dsp: playlist.dsp,
                    url,
                    subject: 'user',
                    verb: 'opened',
                    object: 'playlist',
                  })

                  Linking.openURL(url)
                }
              }}
            >
              <PlaylistImage
                playlist={{
                  id: playlist.sonyPlaylistId,
                  image: playlist.image,
                }}
                imageSize={48}
              />
            </TouchableOpacity>
          </AutoLayout>
        ) : null
      }
      dsp={dsp ?? 'all'}
      countryCode={countryCode}
      selectedTimeInterval={selectedTimeInterval}
      selectTimeInterval={selectTimeInterval}
      onSelectTimeInterval={handleOnSelectTimeInterval}
      error={error}
      showToast={showToast}
      navigation={navigation}
      retry={onPullToRefresh}
      refreshing={refreshing}
      onPressMarketSelectorButton={() => {
        navigation.navigate('MarketSelector', {
          previousScreen: 'TrackPlaylist',
          topMarketCountryCodes: trackTopMarketCountryCodes,
        })
      }}
      renderSelectedDateSummary={(
        selectedInterval: Interval,
        selectedTimeInterval: TimeInterval,
        selectedBar: number | null
      ) =>
        playlistType != null && selectedBar != null ? (
          <SelectedDateSummary
            isrc={isrc}
            playlistId={playlistId}
            countryCode={countryCode}
            selectedInterval={selectedInterval}
            selectedTimeInterval={selectedTimeInterval}
            selectedBar={selectedBar}
            playlistType={playlistType}
          />
        ) : null
      }
      renderSelectedIntervalSummary={(selectedInterval: Interval) =>
        playlistType != null ? (
          <TrackPlaylistIntervalSummary
            isrc={isrc}
            playlistId={playlistId}
            countryCode={countryCode}
            selectedInterval={selectedInterval}
            playlistType={playlistType}
          />
        ) : null
      }
      renderGraph={(
        selectedBar: number | null,
        selectBar: (index: number | null) => void,
        selectedInterval: Interval,
        selectedTimeInterval: TimeInterval,
        selectTimeInterval: (timeInterval: TimeInterval) => void,
        setScrollEnabled: (enabled: boolean) => void
      ) => {
        const { currentIntervalStreams, currentIntervalPlaylistPositions } =
          useTrackPlaylistScreenMetrics(
            isrc,
            playlistId,
            countryCode,
            selectedInterval
          )

        // maxRange can be null if interval is full of nulls
        const maxRange =
          currentIntervalPlaylistPositions != null
            ? dateDataNumberRange(currentIntervalPlaylistPositions)?.max ?? 0
            : 0

        if (
          currentIntervalStreams == null ||
          currentIntervalPlaylistPositions == null
        ) {
          return <CenteredActivityIndicator />
        } else {
          const streamsPlot: PlotConfig<number | null> = {
            type: 'line',
            data: currentIntervalStreams.map((x) => x.data),
            color: colors.offWhite,
            lineStrokeWidth: 2.5,
            axisSide: 'right',
          }

          const playlistPlot: PlotConfig<number | null> = {
            type: 'line',
            data: currentIntervalPlaylistPositions.map((x) => x.data),
            color: colors.platform.apple,
            curve: curveStepAfter,
            lineStrokeWidth: 2.5,
            axisSide: 'left',
          }

          return (
            <View style={{ flex: 1 }}>
              <TrackDateDataFigure
                plots={[
                  streamsPlot,
                  playlistType === 'algorithmic' ? null : playlistPlot,
                ].filter(notEmpty)}
                axisConfig={{
                  right: {
                    legendLabel: Strings.Shared.Streams,
                    color: colors.offWhite,
                  },
                  left: {
                    bounds: {
                      min: 1,
                      // Add +2 to domain as it is used to calculate the axes of
                      // the plots, otherwise when max === min, we could not
                      // align the plot into the figure as there would be no
                      // scale on that axis. Use +2 instead of +1 in order to
                      // have at least 3 ticks (e.g. 1 + 2 => ticks: 1, 2, 3) to
                      // make figures more visually pleasing
                      max: maxRange + 2,
                    },
                    fixedLabels: { min: 1 },
                    legendLabel: Strings.Shared.Position,
                    color: colors.platform.apple,
                    reverse: true,
                  },
                }}
                selectBar={selectBar}
                selectedBar={selectedBar}
                selectedInterval={selectedInterval}
                onHoverBar={() => setScrollEnabled(false)}
                onHoverBarEnd={() => setScrollEnabled(true)}
              />
            </View>
          )
        }
      }}
      contextMenuItems={[
        shareContextMenuItem(
          createURL(
            ScreenPaths['TrackPlaylist'](artist.sonyArtistId, isrc, playlistId),
            {
              queryParams: {
                market: countryCode,
              },
            }
          ),
          {
            message: Strings.ShareContent.TrackPlaylistScreen.message
              .replace('${artistName}', artist.name)
              .replace('${trackName}', track?.name ?? '')
              .replace(
                '${dsp}',
                dsp != null && dsp !== 'all' ? dspNames[dsp] : ''
              )
              .replace('${playlistName}', playlist?.name ?? ''),
          }
        ),
      ]}
    />
  )
}
