import React, { Component, JSX } from 'react';
import {
  Animated,
  BackHandler,
  LayoutChangeEvent,
  NativeEventSubscription,
  StyleSheet,
  Text,
  TextStyle,
  TouchableOpacity,
  View,
} from 'react-native';

// 等待 popup 库完成之后，将会被替换掉

const POPUP_ANIMATION_DURATION = 300;
const POPUP_BACKGROUND_COLOR = '#000000';
const POPUP_BACKGROUND_OPACITY = 0.2;
const POPUP_VERTICAL_OFFSET = 0;

type PopupOptions = {
  duration?: number;
  backgroundColor?: string;
  backgroundOpacity?: number;
  verticalOffset?: number;
};
type PopupType = 'top' | 'bottom' | 'alert';

interface PopupProps {
  content: JSX.Element;
  type: PopupType;
  options?: PopupOptions;
  onShown?: () => void;
  onPressBackground?: () => void;
}
interface PopupState {
  backgroundOpacity: Animated.Value;
  top: Animated.Value;
  bottom: Animated.Value;
  contentOpacity: Animated.Value;
}

class Popup extends Component<PopupProps, PopupState> {
  private contentHeight = 0;

  constructor(props: PopupProps) {
    super(props);
    this.state = {
      backgroundOpacity: new Animated.Value(0),
      top: new Animated.Value(0),
      bottom: new Animated.Value(0),
      contentOpacity: new Animated.Value(0),
    };
  }

  backHandler?: NativeEventSubscription;

  backAction = () => {
    return true;
  };

  componentDidMount() {
    this.backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      this.backAction,
    );
  }

  componentWillUnmount() {
    this.backHandler?.remove();
  }

  show(): void {
    Animated.parallel([
      Animated.timing(this.state.backgroundOpacity, {
        toValue:
          this.props.options?.backgroundOpacity ?? POPUP_BACKGROUND_OPACITY,
        duration: this.props.options?.duration ?? POPUP_ANIMATION_DURATION,
        useNativeDriver: false,
      }),
      this.props.type === 'top'
        ? Animated.timing(this.state.top, {
            toValue:
              this.props.options?.verticalOffset ?? POPUP_VERTICAL_OFFSET,
            duration: this.props.options?.duration ?? POPUP_ANIMATION_DURATION,
            useNativeDriver: false,
          })
        : this.props.type === 'bottom'
        ? Animated.timing(this.state.bottom, {
            toValue: -(
              this.props.options?.verticalOffset ?? POPUP_VERTICAL_OFFSET
            ),
            duration: this.props.options?.duration ?? POPUP_ANIMATION_DURATION,
            useNativeDriver: false,
          })
        : Animated.timing(this.state.contentOpacity, {
            toValue: 1,
            duration: this.props.options?.duration ?? POPUP_ANIMATION_DURATION,
            useNativeDriver: false,
          }),
    ]).start(finished => {
      if (finished) {
        this.props.onShown && this.props.onShown();
      }
    });
  }

  hide(callback?: () => void): void {
    Animated.parallel([
      Animated.timing(this.state.backgroundOpacity, {
        toValue: 0,
        duration: this.props.options?.duration ?? POPUP_ANIMATION_DURATION,
        useNativeDriver: false,
      }),
      this.props.type === 'top'
        ? Animated.timing(this.state.top, {
            toValue: -this.contentHeight,
            duration: POPUP_ANIMATION_DURATION,
            useNativeDriver: false,
          })
        : this.props.type === 'bottom'
        ? Animated.timing(this.state.bottom, {
            toValue: -this.contentHeight,
            duration: POPUP_ANIMATION_DURATION,
            useNativeDriver: false,
          })
        : Animated.timing(this.state.contentOpacity, {
            toValue: 0,
            duration: POPUP_ANIMATION_DURATION,
            useNativeDriver: false,
          }),
    ]).start(finished => {
      if (finished) {
        callback && callback();
      }
    });
  }

  onLayout(event: LayoutChangeEvent): void {
    const height = event.nativeEvent.layout.height;
    if (height > 0 && this.contentHeight === 0) {
      this.contentHeight = height;
      this.state.top.setValue(-height);
      this.state.bottom.setValue(-height);
      this.show();
    }
  }

  render(): JSX.Element {
    return (
      <View
        style={
          this.props.type === 'alert'
            ? popupStyles.containerAlert
            : popupStyles.container
        }>
        <Animated.View
          style={[
            popupStyles.background,
            {
              opacity: this.state.backgroundOpacity,
              backgroundColor:
                this.props.options?.backgroundColor ?? POPUP_BACKGROUND_COLOR,
            },
          ]}>
          <TouchableOpacity
            style={popupStyles.backgroundTouch}
            onPress={() => {
              this.props.onPressBackground && this.props.onPressBackground();
            }}
          />
        </Animated.View>
        <Animated.View
          style={
            this.props.type === 'top'
              ? [popupStyles.content, {top: this.state.top}]
              : this.props.type === 'bottom'
              ? [popupStyles.content, {bottom: this.state.bottom}]
              : [
                  popupStyles.contentAlert,
                  {
                    opacity: this.state.contentOpacity,
                    top:
                      this.props.options?.verticalOffset ??
                      POPUP_VERTICAL_OFFSET,
                  },
                ]
          }
          onLayout={this.onLayout.bind(this)}>
          {this.props.content}
        </Animated.View>
      </View>
    );
  }
}

const popupStyles = StyleSheet.create({
  container: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
  },
  containerAlert: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
  },
  background: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
  },
  backgroundTouch: {
    flex: 1,
  },
  content: {
    position: 'absolute',
    left: 0,
    right: 0,
  },
  contentAlert: {
    justifyContent: 'center',
    alignItems: 'center',
  },
});

interface State {
  title?: string;
  content?: JSX.Element;
  confirm?: {
    title?: string;
    noHide?: boolean;
    titleStyle?: TextStyle;
    action?: () => void;
  };
  cancel?: {
    title?: string;
    titleStyle?: TextStyle;
    action?: () => void;
  };
}

class Alert extends Component<object, State> {
  private static _ref: Alert | null = null;

  public static setRef(ref: Alert | null): void {
    Alert._ref = ref;
  }

  public static show(
    title: string,
    content?: JSX.Element,
    confirm?: {
      title?: string;
      noHide?: boolean;
      titleStyle?: TextStyle;
      action?: () => void;
    },
    cancel?: {
      title?: string;
      titleStyle?: TextStyle;
      action?: () => void;
    },
  ): void {
    if (Alert._ref) {
      Alert._ref.show(title, content, confirm, cancel);
    }
  }

  public static hide(callback?: () => void): void {
    if (Alert._ref) {
      Alert._ref.hide(callback);
    }
  }

  private readonly popupRef: React.RefObject<Popup | null>;

  constructor(props: object) {
    super(props);
    this.popupRef = React.createRef();
    this.state = {
      title: undefined,
      content: undefined,
      confirm: undefined,
      cancel: undefined,
    };
  }

  public show(
    title: string,
    content?: JSX.Element,
    confirm?: {
      title?: string;
      noHide?: boolean;
      titleStyle?: TextStyle;
      action?: () => void;
    },
    cancel?: {
      title?: string;
      titleStyle?: TextStyle;
      action?: () => void;
    },
  ): void {
    if (this.state.title) {
      this.hide(() => {
        this.show(title, content, confirm, cancel);
      });
      return;
    }
    this.setState({
      title: title,
      content: content,
      confirm: confirm,
      cancel: cancel,
    });
  }

  public hide(callback?: () => void): void {
    if (!this.state.title) {
      return;
    }
    this.popupRef.current?.hide(() => {
      this.setState(
        {
          title: undefined,
          content: undefined,
        },
        () => {
          callback && callback();
        },
      );
    });
  }

  render(): JSX.Element {
    return (
      <>
        {this.state.title && (
          <Popup
            ref={this.popupRef}
            content={
              <View style={styles.container}>
                <View style={styles.title}>
                  <Text style={styles.titleText} numberOfLines={8}>
                    {this.state.title}
                  </Text>
                </View>
                {this.state.content && (
                  <View style={styles.content}>{this.state.content}</View>
                )}
                <View style={styles.bottom}>
                  {this.state.cancel && (
                    <TouchableOpacity
                      style={styles.cancel}
                      onPress={() => {
                        this.hide(() => {
                          this.state.cancel?.action &&
                            this.state.cancel.action();
                        });
                      }}>
                      <Text
                        style={
                          this.state.cancel.titleStyle ?? styles.cancelText
                        }>
                        {this.state.cancel.title ?? '取消'}
                      </Text>
                    </TouchableOpacity>
                  )}
                  <TouchableOpacity
                    style={styles.confirm}
                    onPress={() => {
                      if (this.state.confirm?.noHide) {
                        this.state.confirm?.action &&
                          this.state.confirm.action();
                      } else {
                        this.hide(() => {
                          this.state.confirm?.action &&
                            this.state.confirm.action();
                        });
                      }
                    }}>
                    <Text
                      style={
                        this.state.confirm?.titleStyle ?? styles.confirmText
                      }>
                      {this.state.confirm?.title ?? '确定'}
                    </Text>
                  </TouchableOpacity>
                </View>
              </View>
            }
            type={'alert'}
            options={{}}
          />
        )}
      </>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#FFFFFF',
    borderRadius: 10,
    width: 250,
  },
  title: {
    borderTopLeftRadius: 10,
    borderTopRightRadius: 10,
    backgroundColor: '#FFFFFF',
    alignItems: 'center',
    justifyContent: 'center',
    padding: 20,
  },
  titleText: {
    fontSize: 16,
    color: '#222222',
    fontWeight: '500',
    textAlign: 'justify',
    lineHeight: 22,
  },
  content: {
    marginHorizontal: 16,
    marginBottom: 20,
  },
  bottom: {
    borderBottomLeftRadius: 10,
    borderBottomRightRadius: 10,
    backgroundColor: '#FFFFFF',
    height: 50,
    borderTopWidth: 0.5,
    borderTopColor: '#E4E4E4',
    flexDirection: 'row',
    justifyContent: 'space-evenly',
  },
  cancel: {
    flex: 1,
    borderBottomLeftRadius: 10,
    backgroundColor: '#FFFFFF',
    alignItems: 'center',
    justifyContent: 'center',
    borderRightWidth: 0.5,
    borderRightColor: '#E4E4E4',
  },
  cancelText: {
    fontSize: 16,
    color: '#B2B2B2',
    fontWeight: '400',
  },
  confirm: {
    flex: 1,
    borderBottomLeftRadius: 10,
    borderBottomRightRadius: 10,
    backgroundColor: '#FFFFFF',
    alignItems: 'center',
    justifyContent: 'center',
  },
  confirmText: {
    fontSize: 16,
    color: '#4C81F7',
    fontWeight: '500',
  },
});

export default Alert;
