import * as d3Array from 'd3-array'
import * as d3Scale from 'd3-scale'
import React, { useEffect, useState } from 'react'
import {
  LayoutChangeEvent,
  StyleProp,
  Text,
  View,
  ViewStyle,
} from 'react-native'
import { ScrollView, TouchableOpacity } from 'react-native-gesture-handler'

import { colors } from '../Colors'
import { Dsp, streamingDsps } from '../Consts'
import { TextStyles } from '../Styles'
import { useComponentLayout } from '../hooks/useComponentLayout'
import { Strings } from '../i18n'
import { periodToPeriodChangePercentage } from '../streams'
import {
  CurrentAndPreviousByDate,
  TrackWithStreamsByDspAndDate,
} from '../types/types'
import { formatCountShort } from '../util'
import { DiffNumber } from './DiffNumber'
import { DspStreams } from './DspStreams'

function trackDspData(
  track: TrackWithStreamsByDspAndDate,
  dsp: Dsp | 'all'
): CurrentAndPreviousByDate | undefined {
  return track.data.find((x) => x.dsp === dsp)
}

export function sortByNumberOfStreams(tracks: TrackWithStreamsByDspAndDate[]) {
  return tracks.sort(
    (a, b) =>
      (trackDspData(b, 'all')?.currentPeriod ?? 0) -
      (trackDspData(a, 'all')?.currentPeriod ?? 0)
  )
}

/**
 * A single track in the comparison
 */
const TrackComparisonTrack: React.FC<{
  track: TrackWithStreamsByDspAndDate
  showDspBreakdown?: boolean
  onBarLayout?: (e: LayoutChangeEvent) => void
  onPress?: () => void
  selectedTrackISRC?: string | null
  scale?: d3Scale.ScaleLinear<number | undefined, number | undefined>
  style?: StyleProp<ViewStyle>
}> = ({
  track,
  showDspBreakdown,
  onBarLayout,
  onPress,
  selectedTrackISRC,
  scale,
  style,
}) => {
  const streams = trackDspData(track, 'all')?.currentPeriod ?? 0
  const width = scale != null ? scale(streams) : null

  return (
    <TouchableOpacity
      style={[
        {
          flexDirection: 'row',
          alignItems: 'center',
          flex: 1,
        },
        style,
      ]}
      onPress={onPress}
    >
      {showDspBreakdown ? (
        <View style={{ flexDirection: 'row' }}>
          {streamingDsps.map((dsp, i) => {
            const streamsPerDsp = trackDspData(track, dsp)?.currentPeriod ?? 0
            const widthPerDsp = scale != null ? scale(streamsPerDsp) : null
            const backgroundColor = selectedTrackISRC
              ? selectedTrackISRC === track.item.isrc
                ? colors.platform[dsp]
                : colors.metals.metal1
              : colors.offWhite
            const isLast = i === streamingDsps.length - 1

            return (
              <View
                key={track.item.isrc + dsp}
                style={[
                  {
                    height: 10,
                    backgroundColor,
                    marginVertical: 7,
                    marginRight: isLast ? 8 : 0,
                    borderTopRightRadius: isLast ? 1 : 0,
                    borderBottomRightRadius: isLast ? 1 : 0,
                  },
                  widthPerDsp != null ? { width: widthPerDsp } : { flex: 1 },
                ]}
              />
            )
          })}
        </View>
      ) : (
        <View
          style={[
            {
              height: 7,
              marginVertical: 7,
              marginRight: 8,
            },
            width != null ? { width } : { flex: 1 },
          ]}
          onLayout={onBarLayout}
        />
      )}
      <View style={{ flexShrink: 1, flexGrow: 0 }}>
        <Text
          style={[TextStyles.small, { color: colors.white }]}
          numberOfLines={1}
          ellipsizeMode="tail"
        >
          {track.item.name}
        </Text>
      </View>
    </TouchableOpacity>
  )
}

/**
 * Summary for a track
 */
const TrackPerformanceSummary: React.FC<{
  track: TrackWithStreamsByDspAndDate | null
}> = ({ track }) => {
  const all = track != null ? trackDspData(track, 'all') : null
  return (
    <View
      style={{
        height: 64,
        justifyContent: 'flex-end',
        marginBottom: 24,
      }}
    >
      {track != null ? (
        <>
          <View
            style={{ flexDirection: 'row', justifyContent: 'space-between' }}
          >
            <View>
              <Text
                style={{
                  ...TextStyles.tiny,
                  ...{ color: colors.metals.metal0 },
                }}
              >
                {Strings.Shared.Total}
              </Text>
              <Text style={[TextStyles.bodyBold, { color: colors.white }]}>
                {formatCountShort(all?.currentPeriod ?? 0)}
              </Text>
            </View>
            <View>
              <Text
                style={{
                  ...TextStyles.tiny,
                  ...{ color: colors.metals.metal0 },
                }}
              >
                {Strings.Shared.Change}
              </Text>
              <DiffNumber
                value={Math.round(
                  periodToPeriodChangePercentage(
                    all ?? {
                      currentPeriodByDay: undefined,
                      previousPeriodByDay: undefined,
                    }
                  ) ?? 0
                )}
                size="large"
                type="percentage"
              />
            </View>
          </View>
          <View
            style={{ flexDirection: 'row', justifyContent: 'space-between' }}
          >
            {streamingDsps.map((dsp) => (
              <DspStreams
                key={dsp}
                dsp={dsp}
                streams={trackDspData(track, dsp)?.currentPeriod ?? 0}
                gap={8}
              />
            ))}
          </View>
        </>
      ) : (
        <Text
          style={{
            ...TextStyles.bodyBold,
            ...{ color: colors.white },
          }}
        >
          {Strings.TrackComparison.TapBarChartToSeeTheTrackDetails}
        </Text>
      )}
    </View>
  )
}

/**
 * A set of bars representing tracks
 *
 * The width of the bar representing the top track is first measured, then all other bars are sized relative to that
 */
export const TrackComparison: React.FC<{
  tracks: TrackWithStreamsByDspAndDate[]
  onTrackSelect?: (track: TrackWithStreamsByDspAndDate | null) => void
}> = ({ tracks, onTrackSelect }) => {
  // Number of streams for the track with most streams
  const maxTrackStreams = Math.max(
    ...tracks.map((track) => trackDspData(track, 'all')?.currentPeriod ?? 0)
  )

  const sortedTracks = sortByNumberOfStreams(tracks)

  const [onMaxBarLayout, maxBarLayout] = useComponentLayout()
  const [onContainerLayout, containerLayout] = useComponentLayout()
  const [selectedTrackISRC, selectTrackISRC] = useState<string | null>(null)

  const toggleSelectedTrackISRC = (isrc: string) =>
    selectTrackISRC(selectedTrackISRC === isrc ? null : isrc)
  const selectedTrack =
    tracks.find((track) => track.item.isrc === selectedTrackISRC) ?? null

  useEffect(
    () => onTrackSelect?.(selectedTrack),
    [onTrackSelect, selectedTrack]
  )

  const numTicks = 7
  const range = [0, maxBarLayout?.width]
  const domain = [0, maxTrackStreams]
  const scale =
    maxBarLayout != null
      ? d3Scale.scaleLinear(domain, range).nice(numTicks)
      : null

  const margin = 20
  // Number of streams (`margin` pixels away) from the right edge of the view
  const maxViewStreams =
    containerLayout?.width != null && scale != null
      ? scale.invert(containerLayout.width - margin)
      : null

  const ticks =
    maxViewStreams != null
      ? d3Array.ticks(domain[0], maxViewStreams, numTicks)
      : null

  return (
    <View style={{ flex: 1 }} onLayout={onContainerLayout}>
      <TrackPerformanceSummary track={selectedTrack} />
      <View style={{ flex: 1 }}>
        <View
          style={{
            position: 'absolute',
            left: 0,
            right: 0,
            top: 0,
            bottom: 0,
          }}
        >
          {ticks != null &&
            scale != null &&
            ticks
              .slice(1) //Don't draw a line at 0
              .map((tick, i) => (
                <View
                  key={i}
                  style={{
                    position: 'absolute',
                    width: 1,
                    height: '100%',
                    backgroundColor: colors.metals.metal1,
                    left: scale(tick),
                  }}
                />
              ))}
        </View>
        {sortedTracks.length > 0 && (
          <ScrollView>
            <View style={maxBarLayout == null ? { opacity: 0 } : null}>
              {/* Invisible view, used for layout width measurement only */}
              <TrackComparisonTrack
                key={sortedTracks[0].item.isrc}
                track={sortedTracks[0]}
                onBarLayout={onMaxBarLayout}
                style={{ position: 'absolute', opacity: 0 }}
              />
              {sortedTracks.map((track) => (
                <TrackComparisonTrack
                  key={track.item.isrc}
                  track={track}
                  showDspBreakdown
                  onPress={() => toggleSelectedTrackISRC(track.item.isrc)}
                  selectedTrackISRC={selectedTrackISRC}
                  scale={scale ?? undefined}
                />
              ))}
            </View>
          </ScrollView>
        )}
      </View>
      <View
        style={{
          height: 16,
        }}
      >
        {ticks != null &&
          scale != null &&
          ticks.map((tick, i) => (
            <Text
              key={i}
              style={[
                TextStyles.small,
                {
                  position: 'absolute',
                  color: colors.offWhite,
                },
                { left: scale(tick) },
              ]}
            >
              {formatCountShort(tick)}
            </Text>
          ))}
      </View>
    </View>
  )
}
