import { useNavigation } from '@react-navigation/native'
import React, {
  Dispatch,
  useCallback,
  useEffect,
  useRef,
  useState
} from 'react'
import {
  Alert,
  Animated,
  Dimensions,
  Image,
  KeyboardAvoidingView,
  ScrollView,
  StyleSheet,
  Text,
  TextInput,
  View
} from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import Svg, { Circle, G, Path } from 'react-native-svg'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'

import { HeaderLeft, HeaderRight } from 'src/components/Header'
import { StyleConstants } from 'src/utils/styles/constants'
import { useTheme } from 'src/utils/styles/ThemeManager'
import { PanGestureHandler } from 'react-native-gesture-handler'
import { PostAction } from '../Compose'
import client from 'src/api/client'
import AttachmentVideo from 'src/components/Timelines/Timeline/Shared/Attachment/AttachmentVideo'

const Stack = createNativeStackNavigator()

export interface Props {
  route: {
    params: {
      attachment: Mastodon.Attachment & { local_url: string }
      composeDispatch: Dispatch<PostAction>
    }
  }
}

const ComposeEditAttachment: React.FC<Props> = ({
  route: {
    params: { attachment, composeDispatch }
  }
}) => {
  const navigation = useNavigation()

  const { theme } = useTheme()

  const [altText, setAltText] = useState<string | undefined>(
    attachment.description
  )
  const focus = useRef({ x: 0, y: 0 })

  useEffect(() => {
    const unsubscribe = navigation.addListener('beforeRemove', () => {
      let needUpdate = false
      if (altText) {
        attachment.description = altText
        needUpdate = true
      }
      if (attachment.type === 'image') {
        if (focus.current.x !== 0 || focus.current.y !== 0) {
          attachment.meta!.focus = {
            x: focus.current.x > 1 ? 1 : focus.current.x,
            y: focus.current.y > 1 ? 1 : focus.current.y
          }
          needUpdate = true
        }
      }
      if (needUpdate) {
        composeDispatch({ type: 'attachmentEdit', payload: attachment })
      }
    })

    return unsubscribe
  }, [navigation, altText])

  const videoPlayback = useCallback(() => {
    return (
      <AttachmentVideo
        media_attachments={[attachment as Mastodon.AttachmentVideo]}
        width={Dimensions.get('screen').width}
      />
    )
  }, [])

  const imageFocus = useCallback(() => {
    const imageDimensionis = {
      width: Dimensions.get('screen').width,
      height:
        Dimensions.get('screen').width /
        (attachment as Mastodon.AttachmentImage).meta?.original?.aspect!
    }

    const panFocus = useRef(
      new Animated.ValueXY(
        (attachment as Mastodon.AttachmentImage).meta?.focus?.x &&
        (attachment as Mastodon.AttachmentImage).meta?.focus?.y
          ? {
              x:
                ((attachment as Mastodon.AttachmentImage).meta!.focus!.x *
                  imageDimensionis.width) /
                2,
              y:
                (-(attachment as Mastodon.AttachmentImage).meta!.focus!.y *
                  imageDimensionis.height) /
                2
            }
          : { x: 0, y: 0 }
      )
    ).current
    const panX = panFocus.x.interpolate({
      inputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
      outputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
      extrapolate: 'clamp'
    })
    const panY = panFocus.y.interpolate({
      inputRange: [-imageDimensionis.height / 2, imageDimensionis.height / 2],
      outputRange: [-imageDimensionis.height / 2, imageDimensionis.height / 2],
      extrapolate: 'clamp'
    })
    panFocus.addListener(e => {
      focus.current = {
        x: e.x / (imageDimensionis.width / 2),
        y: -e.y / (imageDimensionis.height / 2)
      }
    })
    const handleGesture = Animated.event(
      [{ nativeEvent: { translationX: panFocus.x, translationY: panFocus.y } }],
      { useNativeDriver: true }
    )
    const onHandlerStateChange = useCallback(() => {
      panFocus.extractOffset()
    }, [])

    return (
      <>
        <View style={{ overflow: 'hidden' }}>
          <Image
            style={{
              width: imageDimensionis.width,
              height: imageDimensionis.height
            }}
            source={{
              uri: attachment.local_url || attachment.preview_url
            }}
          />
          <PanGestureHandler
            onGestureEvent={handleGesture}
            onHandlerStateChange={onHandlerStateChange}
          >
            <Animated.View
              style={[
                {
                  position: 'absolute',
                  top: -1000 + imageDimensionis.height / 2,
                  left: -1000 + imageDimensionis.width / 2,
                  transform: [{ translateX: panX }, { translateY: panY }]
                }
              ]}
            >
              <Svg width='2000' height='2000' viewBox='0 0 2000 2000'>
                <G>
                  <G id='Mask'>
                    <Path
                      d={
                        'M2000,0 L2000,2000 L0,2000 L0,0 L2000,0 Z M1000,967 C981.774603,967 967,981.774603 967,1000 C967,1018.2254 981.774603,1033 1000,1033 C1018.2254,1033 1033,1018.2254 1033,1000 C1033,981.774603 1018.2254,967 1000,967 Z'
                      }
                      fill={theme.backgroundOverlay}
                    />
                    <G transform='translate(967, 967)'>
                      <Circle
                        stroke={theme.background}
                        strokeWidth='2'
                        cx='33'
                        cy='33'
                        r='33'
                      />
                      <Circle fill={theme.background} cx='33' cy='33' r='2' />
                    </G>
                  </G>
                </G>
              </Svg>
            </Animated.View>
          </PanGestureHandler>
        </View>
        <Text style={[styles.imageFocusText, { color: theme.primary }]}>
          在预览图上拖动圆圈，以选择缩略图的焦点。
        </Text>
      </>
    )
  }, [])

  const altTextInput = useCallback(() => {
    return (
      <View style={styles.altTextContainer}>
        <Text style={[styles.altTextInputHeading, { color: theme.primary }]}>
          为附件添加文字说明
        </Text>
        <TextInput
          style={[styles.altTextInput, { borderColor: theme.border }]}
          autoCapitalize='none'
          autoCorrect={false}
          maxLength={1500}
          multiline
          onChangeText={e => setAltText(e)}
          placeholder={
            '你可以为附件添加文字说明，以便更多人可以查看他们（包括视力障碍或视力受损人士）。\n\n优质的描述应该简洁明了，但要准确地描述照片中的内容，以便用户理解其含义。'
          }
          placeholderTextColor={theme.secondary}
          scrollEnabled
          value={altText}
        />
        <Text style={[styles.altTextLength, { color: theme.secondary }]}>
          {altText?.length || 0} / 1500
        </Text>
      </View>
    )
  }, [altText])

  return (
    <KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
      <SafeAreaView style={{ flex: 1 }} edges={['right', 'bottom', 'left']}>
        <Stack.Navigator>
          <Stack.Screen
            name='Screen-Shared-Compose-EditAttachment-Root'
            options={{
              title: '编辑附件',
              headerLeft: () => (
                <HeaderLeft text='取消' onPress={() => navigation.goBack()} />
              ),
              headerRight: () => (
                <HeaderRight
                  text='应用'
                  onPress={() => {
                    const formData = new FormData()

                    if (altText) {
                      formData.append('description', altText)
                    }

                    if (focus.current.x !== 0 || focus.current.y !== 0) {
                      formData.append(
                        'focus',
                        `${focus.current.x},${focus.current.y}`
                      )
                    }

                    client({
                      method: 'put',
                      instance: 'local',
                      url: `media/${attachment.id}`,
                      ...(formData && { body: formData })
                    })
                      .then(
                        res => {
                          if (res.body.id === attachment.id) {
                            Alert.alert('修改成功', '', [
                              {
                                text: '好的',
                                onPress: () => {
                                  navigation.goBack()
                                }
                              }
                            ])
                          } else {
                            Alert.alert('修改失败', '', [
                              {
                                text: '返回重试'
                              }
                            ])
                          }
                        },
                        error => {
                          Alert.alert('修改失败', error.body, [
                            {
                              text: '返回重试'
                            }
                          ])
                        }
                      )
                      .catch(() => {
                        Alert.alert('修改失败', '', [
                          {
                            text: '返回重试'
                          }
                        ])
                      })
                  }}
                />
              )
            }}
          >
            {() => {
              switch (attachment.type) {
                case 'image':
                  return (
                    <ScrollView style={{ flex: 1 }}>
                      {imageFocus()}
                      {altTextInput()}
                    </ScrollView>
                  )
                case 'video':
                case 'gifv':
                  return (
                    <ScrollView style={{ flex: 1 }}>
                      {videoPlayback()}
                      {altTextInput()}
                    </ScrollView>
                  )
              }
              return null
            }}
          </Stack.Screen>
        </Stack.Navigator>
      </SafeAreaView>
    </KeyboardAvoidingView>
  )
}

const styles = StyleSheet.create({
  imageFocusText: {
    fontSize: StyleConstants.Font.Size.M,
    padding: StyleConstants.Spacing.Global.PagePadding
  },
  altTextContainer: { padding: StyleConstants.Spacing.Global.PagePadding },
  altTextInputHeading: {
    fontSize: StyleConstants.Font.Size.M,
    fontWeight: StyleConstants.Font.Weight.Bold
  },
  altTextInput: {
    height: 200,
    fontSize: StyleConstants.Font.Size.M,
    marginTop: StyleConstants.Spacing.M,
    marginBottom: StyleConstants.Spacing.S,
    padding: StyleConstants.Spacing.Global.PagePadding,
    paddingTop: StyleConstants.Spacing.S * 1.5,
    borderWidth: StyleSheet.hairlineWidth
  },
  altTextLength: {
    textAlign: 'right',
    marginRight: StyleConstants.Spacing.S,
    fontSize: StyleConstants.Font.Size.S,
    marginBottom: StyleConstants.Spacing.M
  }
})

export default ComposeEditAttachment
