import React, { useState, useEffect, SetStateAction, Dispatch } from 'react'
import {
  TouchableOpacity,
  Text,
  Animated,
  Easing,
  View,
  StyleSheet,
} from 'react-native'

import { TextStyles } from '../Styles'
import { colors } from '../Colors'
import { Strings } from '../i18n'
import { streamingDsps } from '../Consts'
import { DspSlug } from '../types/types'

const height = 32
const styles = StyleSheet.create({
  container: {
    position: 'relative',
    flexDirection: 'row',
    justifyContent: 'space-between',
    backgroundColor: colors.midGray,
    height,
    borderRadius: height / 2,
    marginBottom: height / 2,
  },
  highlight: {
    position: 'absolute',
    height,
    backgroundColor: colors.gray,
    borderRadius: height / 2,
  },
  button: {
    padding: height / 4,
  },
  text: {
    ...TextStyles.micro,
    color: colors.gray,
    textAlign: 'center',
    textTransform: 'uppercase',
  },
  selected: {
    color: colors.white,
  },
})

type FiltersProps<T extends string> = {
  filters: readonly { key: T; label: string; disabled?: boolean }[]
  onLoad: (dsp: T) => void
  selectedFilter?: T
  children: (dsp: T) => React.ReactNode
  onSelected?: (dsp: T) => SetStateAction<T>
  showSelector?: boolean
}
export function Filters<T extends string>({
  filters,
  onLoad,
  children,
  selectedFilter = filters[0].key,
  onSelected,
  showSelector,
}: FiltersProps<T>): React.ReactElement {
  const [width, setWidth] = useState<number | null>(null)
  const [selected, setSelected] = useState(selectedFilter)

  useEffect(() => {
    onSelected?.(selected)
  }, [selected])
  useEffect(() => {
    setSelected(selectedFilter)
  }, [selectedFilter])
  onLoad(selected)

  return (
    <View
      style={{ flex: 1 }}
      onLayout={(e) => setWidth(e.nativeEvent.layout.width)}
    >
      {width != null ? (
        <>
          {showSelector !== false && (
            <FiltersView
              filters={filters}
              selected={selected}
              onPress={setSelected}
              width={width}
            />
          )}
          {children(selected)}
        </>
      ) : null}
    </View>
  )
}

interface FiltersViewProps<T extends string> {
  filters: readonly { key: T; label: string; disabled?: boolean }[]
  selected: T
  onPress: Dispatch<SetStateAction<T>>
  width: number
}

function FiltersView<T extends string>({
  filters,
  selected,
  onPress,
  width,
}: FiltersViewProps<T>) {
  const flex = 1 / filters.length
  const selectedIndex = filters.map((filter) => filter.key).indexOf(selected)

  const [animation] = useState(new Animated.Value(selectedIndex))

  useEffect(() => {
    const filterAnimation = Animated.timing(animation, {
      toValue: selectedIndex * flex * width,
      duration: 125,
      easing: Easing.quad,
      useNativeDriver: false,
    })
    filterAnimation.start()
    return () => filterAnimation.stop()
  })
  const highlight = [
    styles.highlight,
    { width: flex * width, transform: [{ translateX: animation }] },
  ]

  return (
    <View style={styles.container}>
      <Animated.View style={highlight} />
      {filters.map((filter) => (
        <FilterButton
          key={filter.key}
          width={flex}
          label={filter.label}
          selected={filter.key === selected}
          onPress={() => onPress(filter.key)}
          disabled={filter.disabled}
        />
      ))}
    </View>
  )
}

interface FilterButtonProps {
  label: string
  width: number
  selected: boolean
  onPress: () => void
  disabled?: boolean
}

export function FilterButton(props: FilterButtonProps) {
  return (
    <TouchableOpacity
      style={{ flex: props.width }}
      onPress={props.onPress}
      disabled={props.disabled}
    >
      <View style={styles.button}>
        <Text style={[styles.text, props.selected && styles.selected]}>
          {props.label}
        </Text>
      </View>
    </TouchableOpacity>
  )
}

export function toLabels<T extends string>(
  dsps: readonly T[]
): { label: string; key: T }[] {
  return dsps.map((key) => ({ label: key, key }))
}

export const streamingDspFilters = [
  { key: 'all' as DspSlug, label: Strings.Shared.AllDSPs },
  ...toLabels(streamingDsps),
]
