import * as React from "react";
import {
    StyleProp,
    StyleSheet,
    Animated,
    SafeAreaView,
    TouchableWithoutFeedback,
    View,
    ViewStyle,
} from "react-native";
import color from "color";
import FAB from "./FAB";
import Text from "../Text";
import Card from "../Card/Card";
import { DefaultTheme } from "styled-components";

type Props = {
    /**
     * Action items to display in the form of a speed dial.
     * An action item should contain the following properties:
     * - `icon`: icon to display (required)
     * - `label`: optional label text
     * - `accessibilityLabel`: accessibility label for the action, uses label by default if specified
     * - `color`: custom icon color of the action item
     * - `style`: pass additional styles for the fab item, for example, `backgroundColor`
     * - `small`: boolean describing whether small or normal sized FAB is rendered. Defaults to `true`
     * - `onPress`: callback that is called when `FAB` is pressed (required)
     */
    actions: Array<{
        icon: React.ReactElement;
        label?: string;
        color?: string;
        accessibilityLabel?: string;
        style?: StyleProp<ViewStyle>;
        small?: boolean;
        onPress: () => void;
        testID?: string;
    }>;
    /**
     * Icon to display for the `FAB`.
     * You can toggle it based on whether the speed dial is open to display a different icon.
     */
    icon: React.ReactElement;
    /**
     * Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB.
     */
    accessibilityLabel?: string;
    /**
     * Custom color for the `FAB`.
     */
    color?: string;
    /**
     * Function to execute on pressing the `FAB`.
     */
    onPress?: () => void;
    /**
     * Whether the speed dial is open.
     */
    open: boolean;
    /**
     * Callback which is called on opening and closing the speed dial.
     * The open state needs to be updated when it's called, otherwise the change is dropped.
     */
    onStateChange: (state: { open: boolean }) => void;
    /**
     * Whether `FAB` is currently visible.
     */
    visible: boolean;
    /**
     * Style for the group. You can use it to pass additional styles if you need.
     * For example, you can set an additional padding if you have a tab bar at the bottom.
     */
    style?: StyleProp<ViewStyle>;
    /**
     * Style for the FAB. It allows to pass the FAB button styles, such as backgroundColor.
     */
    fabStyle?: StyleProp<ViewStyle>;
    /**
     * @optional
     */
    theme?: DefaultTheme;
    /**
     * Pass down testID from Group props to FAB.
     */
    testID?: string;
};

/**
 * A component to display a stack of FABs with related actions in a speed dial.
 * To render the group above other components, you'll need to wrap it with the [`Portal`](portal.html) component.
 *
 * <div class="screenshots">
 *   <img src="screenshots/fab-group.png" />
 * </div>
 *
 * ## Usage
 * ```js
 * import * as React from 'react';
 * import FAB from 'react-native-simple-elements/components/FAB';
 * import Portal from "react-native-simple-elements/components/Portal";
 * import Provider from "react-native-simple-elements/components/theme/Provider";
 *
 * const MyComponent = () => {
 *   const [state, setState] = React.useState({ open: false });
 *
 *   const onStateChange = ({ open }) => setState({ open });
 *
 *   const { open } = state;
 *
 *   return (
 *     <Provider>
 *       <Portal>
 *         <FAB.Group
 *           open={open}
 *           icon={open ? 'calendar-today' : 'plus'}
 *           actions={[
 *             { icon: 'plus', onPress: () => console.log('Pressed add') },
 *             {
 *               icon: 'star',
 *               label: 'Star',
 *               onPress: () => console.log('Pressed star'),
 *             },
 *             {
 *               icon: 'email',
 *               label: 'Email',
 *               onPress: () => console.log('Pressed email'),
 *             },
 *             {
 *               icon: 'bell',
 *               label: 'Remind',
 *               onPress: () => console.log('Pressed notifications'),
 *               small: false,
 *             },
 *           ]}
 *           onStateChange={onStateChange}
 *           onPress={() => {
 *             if (open) {
 *               // do something if the speed dial is open
 *             }
 *           }}
 *         />
 *       </Portal>
 *     </Provider>
 *   );
 * };
 *
 * export default MyComponent;
 * ```
 */
const FABGroup = ({
    actions,
    icon,
    open,
    onPress,
    accessibilityLabel,
    theme,
    style,
    fabStyle,
    visible,
    testID,
    onStateChange,
    color: colorProp,
}: Props) => {
    const { current: backdrop } = React.useRef<Animated.Value>(
        new Animated.Value(0)
    );
    const animations = React.useRef<Animated.Value[]>(
        actions.map(() => new Animated.Value(open ? 1 : 0))
    );

    const [prevActions, setPrevActions] = React.useState<
        | {
            icon: React.ReactElement;
            label?: string;
            color?: string;
            accessibilityLabel?: string;
            style?: StyleProp<ViewStyle>;
            onPress: () => void;
            testID?: string;
                }[]
                | null
                >(null);

    const { scale } = theme.animation;

    React.useEffect(() => {
        if (open) {
            Animated.parallel([
                Animated.timing(backdrop, {
                    toValue: 1,
                    duration: 250 * scale,
                    useNativeDriver: true,
                }),
                Animated.stagger(
                    50 * scale,
                    animations.current
                        .map((animation) =>
                            Animated.timing(animation, {
                                toValue: 1,
                                duration: 150 * scale,
                                useNativeDriver: true,
                            })
                        )
                        .reverse()
                ),
            ]).start();
        } else {
            Animated.parallel([
                Animated.timing(backdrop, {
                    toValue: 0,
                    duration: 200 * scale,
                    useNativeDriver: true,
                }),
                ...animations.current.map((animation) =>
                    Animated.timing(animation, {
                        toValue: 0,
                        duration: 150 * scale,
                        useNativeDriver: true,
                    })
                ),
            ]).start();
        }
    }, [open, actions, backdrop, scale]);

    const close = () => onStateChange({ open: false });

    const toggle = () => onStateChange({ open: !open });

    const { colors } = theme;

    const labelColor = theme.dark
        ? colors.text
        : color(colors.text).fade(0.54).rgb().string();
    const backdropOpacity = open
        ? backdrop.interpolate({
            inputRange: [0, 0.5, 1],
            outputRange: [0, 1, 1],
        })
        : backdrop;

    const opacities = animations.current;
    const scales = opacities.map((opacity) =>
        open
            ? opacity.interpolate({
                inputRange: [0, 1],
                outputRange: [0.8, 1],
            })
            : 1
    );

    if (actions.length !== prevActions?.length) {
        animations.current = actions.map(
            (_, i) => animations.current[i] || new Animated.Value(open ? 1 : 0)
        );
        setPrevActions(actions);
    }

    return (
        <View pointerEvents="box-none" style={[styles.container, style]}>
            <TouchableWithoutFeedback onPress={close}>
                <Animated.View
                    pointerEvents={open ? "auto" : "none"}
                    style={[
                        styles.backdrop,
                        {
                            opacity: backdropOpacity,
                            backgroundColor: colors.backdrop,
                        },
                    ]}
                />
            </TouchableWithoutFeedback>
            <SafeAreaView pointerEvents="box-none" style={styles.safeArea}>
                <View pointerEvents={open ? "box-none" : "none"}>
                    {actions.map((it, i) => (
                        <View
                            key={i} // eslint-disable-line react/no-array-index-key
                            style={[
                                styles.item,
                                {
                                    marginHorizontal:
                                        typeof it.small === "undefined" || it.small ? 24 : 16,
                                },
                            ]}
                            pointerEvents={open ? "box-none" : "none"}
                        >
                            {it.label && (
                                <View>
                                    <Card
                                        style={
                                            [
                                                styles.label,
                                                {
                                                    transform: [{ scale: scales[i] }],
                                                    opacity: opacities[i],
                                                },
                                            ] as StyleProp<ViewStyle>
                                        }
                                        onPress={() => {
                                            it.onPress();
                                            close();
                                        }}
                                        accessibilityLabel={
                                            it.accessibilityLabel !== "undefined"
                                                ? it.accessibilityLabel
                                                : it.label
                                        }
                                        // accessibilityTraits="button"
                                        // accessibilityComponentType="button"
                                        accessibilityRole="button"
                                    >
                                        <Text style={{ color: labelColor }}>{it.label}</Text>
                                    </Card>
                                </View>
                            )}
                            <FAB
                                small={typeof it.small !== "undefined" ? it.small : true}
                                icon={it.icon}
                                color={it.color}
                                style={
                                    [
                                        {
                                            transform: [{ scale: scales[i] }],
                                            opacity: opacities[i],
                                            backgroundColor: theme.colors.surface,
                                        },
                                        it.style,
                                    ] as StyleProp<ViewStyle>
                                }
                                onPress={() => {
                                    it.onPress();
                                    close();
                                }}
                                accessibilityLabel={
                                    typeof it.accessibilityLabel !== "undefined"
                                        ? it.accessibilityLabel
                                        : it.label
                                }
                                // accessibilityTraits="button"
                                // accessibilityComponentType="button"
                                accessibilityRole="button"
                                testID={it.testID}
                                visible={open}
                            />
                        </View>
                    ))}
                </View>
                <FAB
                    onPress={() => {
                        onPress?.();
                        toggle();
                    }}
                    icon={icon}
                    color={colorProp}
                    accessibilityLabel={accessibilityLabel}
                    // accessibilityTraits="button"
                    // accessibilityComponentType="button"
                    accessibilityRole="button"
                    accessibilityState={{ expanded: open }}
                    style={[styles.fab, fabStyle]}
                    visible={visible}
                    testID={testID}
                />
            </SafeAreaView>
        </View>
    );
};

FABGroup.displayName = "FAB.Group";

export default FABGroup;

// @component-docs ignore-next-line
const FABGroupWithTheme = FABGroup;
// @component-docs ignore-next-line
export { FABGroupWithTheme as FABGroup };

const styles = StyleSheet.create({
    safeArea: {
        alignItems: "flex-end",
    },
    container: {
        ...StyleSheet.absoluteFillObject,
        justifyContent: "flex-end",
    },
    fab: {
        marginHorizontal: 16,
        marginBottom: 16,
        marginTop: 0,
    },
    backdrop: {
        ...StyleSheet.absoluteFillObject,
    },
    label: {
        borderRadius: 5,
        paddingHorizontal: 12,
        paddingVertical: 6,
        marginVertical: 8,
        marginHorizontal: 16,
        elevation: 2,
    },
    item: {
        marginBottom: 16,
        flexDirection: "row",
        justifyContent: "flex-end",
        alignItems: "center",
    },
});
