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

// 等待 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',
  },
});

type BottomSheetItem = {
  text: string;
};

interface State {
  items?: BottomSheetItem[];
  title?: string;
  onSelected?: (item: BottomSheetItem) => void;
}

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

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

  public static show<T extends BottomSheetItem>(
    items: T[],
    title?: string,
    onSelected?: (item: T) => void,
  ): void {
    if (BottomSheet._ref) {
      BottomSheet._ref.show(
        items,
        title,
        onSelected as (item: BottomSheetItem) => void,
      );
    }
  }

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

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

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

  private show(
    items: BottomSheetItem[],
    title?: string,
    onSelected?: (item: BottomSheetItem) => void,
  ): void {
    if (this.state.title) {
      this.hide(() => {
        this.show(items, title, onSelected);
      });
      return;
    }
    this.setState({
      items: items,
      title: title,
      onSelected: onSelected,
    });
  }

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

  render(): JSX.Element {
    return (
      <>
        {this.state.items && (
          <Popup
            ref={this.popupRef}
            content={
              <SafeAreaView style={styles.content} edges={['bottom']}>
                {this.state.title && (
                  <View style={styles.title}>
                    <Text style={styles.titleText}>{this.state.title}</Text>
                  </View>
                )}
                {this.state.items.map((item, index) => {
                  return (
                    <TouchableOpacity
                      key={index}
                      style={styles.item}
                      onPress={() => {
                        this.hide(() => {
                          this.state.onSelected && this.state.onSelected(item);
                        });
                      }}>
                      <Text style={styles.itemText}>{item.text}</Text>
                    </TouchableOpacity>
                  );
                })}
                <TouchableOpacity
                  style={styles.cancel}
                  onPress={() => {
                    this.hide();
                  }}>
                  <Text style={styles.cancelText}>取消</Text>
                </TouchableOpacity>
              </SafeAreaView>
            }
            type={'bottom'}
            options={{}}
            onPressBackground={() => {
              this.hide();
            }}
          />
        )}
      </>
    );
  }
}

const styles = StyleSheet.create({
  content: {
    backgroundColor: '#F2F2F2',
  },
  title: {
    backgroundColor: '#FFFFFF',
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
  },
  titleText: {
    fontSize: 16,
    color: '#222222',
    fontWeight: '500',
  },
  item: {
    backgroundColor: '#FFFFFF',
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
    marginTop: 0.5,
  },
  itemText: {
    fontSize: 14,
    color: '#333333',
    fontWeight: '400',
  },
  cancel: {
    backgroundColor: '#FFFFFF',
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
    marginTop: 6,
  },
  cancelText: {
    fontSize: 14,
    color: '#666666',
    fontWeight: '400',
  },
});

export default BottomSheet;
