import React, { useContext, useState } from 'react'
import {
  View,
  ActivityIndicator,
  Text,
  TouchableOpacity,
  Image,
  useWindowDimensions,
  Platform,
} from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'

import { UserContext } from '../contexts/UserContext'
import { Globe } from '../globe/globe'
import { timeRangeDates } from '../util'
import { GeoCoordinate } from '../types/components'
import { CountryCard } from '../types/CountryCard'
import { Artist } from '../types/Artist'
import { colors } from '../Colors'
import { CountriesComponent } from '../components/CountriesComponent'
import { HeaderNav } from '../components/HeaderNav'
import { NetworkError, useNetwork } from '../components/useNetwork'
import { ErrorBoundary } from '../components/ErrorBoundary'
import { ScreenNames } from '../constants/ScreenNames'
import { ReactNavigationProps, NavigationProp } from '../Navigation'
import { TextStyles } from '../Styles'
import { StreamsPerParticleIndicator } from '../components/StreamsPerParticleIndicator'
import { EventType, sendAnalyticsEvent } from '../hooks/useAnalytics'
import { ArtistContext } from '../contexts/ArtistContext'
import { Strings } from '../i18n'
import { defaultTimeInterval, TimeInterval } from '../types/TimeInterval'

interface GeoCoordinatesWithStreamsPerParticle {
  coordinates: GeoCoordinate[]
  streamsPerParticle: number
}

interface GlobeData {
  artist: Artist
  countries: CountryCard[]
  streamCoordinates: GeoCoordinatesWithStreamsPerParticle | GeoCoordinate[]
}

function hasStreamsPerParticle(
  streamCoordinates: GeoCoordinate[] | GeoCoordinatesWithStreamsPerParticle
): streamCoordinates is GeoCoordinatesWithStreamsPerParticle {
  return (
    (streamCoordinates as GeoCoordinatesWithStreamsPerParticle)?.coordinates !==
    undefined
  )
}

type GlobeScreenProps = ReactNavigationProps<ScreenNames.Globe>
export const GlobeScreen: React.FC<GlobeScreenProps> = ({ navigation }) => {
  const user = useContext(UserContext)
  const artist = useContext(ArtistContext)

  const [timeInterval, setTimeInterval] =
    useState<TimeInterval>(defaultTimeInterval)
  const { startDate, endDate } = timeRangeDates(timeInterval)
  const { data, error, refreshControl, showToast, retry } =
    useNetwork<GlobeData>(
      `screens/artist/${artist.sonyArtistId}/dashboard?startDate=${startDate}&endDate=${endDate}`,
      user,
      () =>
        sendAnalyticsEvent(EventType.PULL_TO_REFRESH, {
          artist_selected: artist.name,
          timeframe_selected: timeInterval,
          page: 'Globe Screen',
          subject: 'user',
          verb: 'refreshed',
          object: 'globe',
        })
    )

  return (
    <View style={{ flex: 1 }}>
      <Header
        navigation={navigation}
        showToast={showToast}
        timeInterval={timeInterval}
        setTimeInterval={setTimeInterval}
      />
      <ErrorBoundary error={error} refreshControl={refreshControl}>
        <Content
          navigation={navigation}
          artistId={artist.sonyArtistId}
          error={error}
          data={data}
          retry={retry}
          timeInterval={timeInterval}
        />
      </ErrorBoundary>
    </View>
  )
}

const Header: React.FC<{
  navigation: NavigationProp<ScreenNames>
  showToast: boolean
  timeInterval: TimeInterval
  setTimeInterval: (timeInterval: TimeInterval) => void
}> = ({ navigation, showToast, timeInterval, setTimeInterval }) => (
  <SafeAreaView
    style={{
      position: 'absolute',
      width: '100%',
      zIndex: 2,
    }}
  >
    <HeaderNav
      navigation={navigation}
      showToast={showToast}
      style={{
        backgroundColor: 'transparent',
      }}
      timeInterval={timeInterval}
      setTimeInterval={setTimeInterval}
    />
  </SafeAreaView>
)

const Content: React.FC<
  {
    navigation: GlobeScreenProps['navigation']
    artistId: string
  } & {
    data: GlobeData
    error: NetworkError
    retry: () => void
    timeInterval: TimeInterval
  }
> = ({ navigation, data, error, retry, artistId, timeInterval }) => {
  const [targetCountry, setTargetCountry] = useState<CountryCard | null>(null)
  const setTargetCountryWithAnalytics = (
    country: CountryCard,
    rank: number
  ) => {
    sendAnalyticsEvent(EventType.COUNTRY_CARD_SWIPED, {
      rank,
      countryCode: country.countryCode,
      name: country.name,
      streams: country.streams.currentPeriod,
      subject: 'user',
      verb: 'swiped',
      object: 'countrycard',
    })
    // TODO: fix swipe re-initialization on android
    if (Platform.OS === 'android') return
    setTargetCountry(country)
  }

  const streamCoordinates = hasStreamsPerParticle(data?.streamCoordinates)
    ? data?.streamCoordinates?.coordinates
    : data?.streamCoordinates
  const streamsPerParticle = hasStreamsPerParticle(data?.streamCoordinates)
    ? Math.round(data?.streamCoordinates?.streamsPerParticle)
    : null

  if (!data) return <ActivityIndicator style={{ flex: 1 }} size="large" />
  return (
    <View style={{ flex: 1 }}>
      {error && <ErrorImageBackground />}

      <View style={{ flexGrow: 3 }}>
        <Globe
          streamCoordinates={streamCoordinates}
          targetCountry={targetCountry}
          allCountries={data?.countries}
        />
      </View>

      <View style={{ flexBasis: 165 }}>
        {error ? (
          <GlobeError retry={retry} />
        ) : (
          <>
            <StreamsPerParticleIndicator
              streamsPerParticle={streamsPerParticle}
            />
            <CountriesComponent
              countries={data.countries}
              onSwipe={setTargetCountryWithAnalytics}
              onPressCountry={(country) =>
                navigation.navigate(ScreenNames.Market, {
                  artistId,
                  countryCode: country.countryCode,
                })
              }
              onPressViewAll={() => {
                sendAnalyticsEvent(EventType.VIEW_ALL_COUNTRIES_PRESSED, {
                  subject: 'user',
                  verb: 'pressed',
                  object: 'view_all_countries',
                })
                navigation.navigate(ScreenNames.Countries, {
                  artistId,
                  timeInterval,
                })
              }}
              timeInterval={timeInterval}
            />
          </>
        )}
      </View>
    </View>
  )
}

const ErrorImageBackground = () => {
  const { width, height } = useWindowDimensions()
  return (
    <View
      style={{
        justifyContent: 'center',
        alignItems: 'center',
        padding: 20,
        width: width,
        height: height / 1.333,
      }}
    >
      <Image
        resizeMode="contain"
        style={{ width: '100%', height: '100%' }}
        source={require('../../assets/login-globe.png')}
      />
    </View>
  )
}

function GlobeError({ retry }: { retry: () => void }) {
  return (
    <View
      style={{
        flexGrow: 1,
        alignItems: 'center',
        justifyContent: 'center',
        paddingVertical: 48,
        paddingHorizontal: 20,
        borderTopRightRadius: 20,
        borderTopLeftRadius: 20,
        backgroundColor: colors.darkGray,
      }}
    >
      <Text
        style={{
          ...TextStyles.body,
          color: colors.lightGray,
          textAlign: 'center',
          marginBottom: 8,
        }}
      >
        {Strings.Shared.SomethingWentWrong}
      </Text>
      <Text
        style={{
          ...TextStyles.tiny,
          textAlign: 'center',
          marginBottom: 28,
        }}
      >
        {Strings.Shared.CheckYourNetworkConnectionAndTryAgain}
      </Text>
      <TouchableOpacity onPress={retry}>
        <Text style={[TextStyles.body, { color: colors.white }]}>
          {Strings.Shared.Retry}
        </Text>
      </TouchableOpacity>
    </View>
  )
}
