import {
    useImperativeHandle,
    isValidElement,
    cloneElement,
    forwardRef,
    useEffect,
    Children,
    useState,
    useRef
} from "react";
import {
    TouchableWithoutFeedback,
    Animated,
    Platform,
    Easing,
    View
} from "react-native";
import type IModalProps from "./type";
import {
    type IModalRef
} from "./type";
import stylesheet from "./stylesheet";
import {
    NCoreUIKitTheme
} from "../../core/hooks";
import {
    type RefForwardingComponent
} from "../../types";
import {
    Portal
} from "../../helpers/portalize";
import {
    uuid
} from "../../utils";

/**
 * A generic modal
 * @param props {@link IModalProps}
 * @returns Element
 */
const Modal: RefForwardingComponent<IModalRef, IModalProps> = ({
    isScaleAnimatedOnlyOpen = true,
    closeAnimationDelay = 100,
    isDisabledOverlay = false,
    isContentRequired = true,
    openAnimationDelay = 100,
    alignContent = "center",
    isOverlayVisible = true,
    isScaleAnimated = false,
    isWorkWithPortal = true,
    isActive: isActiveProp,
    isAnimated = true,
    onContainerLayout,
    onOverlayPress,
    contentStyle,
    overlayProps,
    customTheme,
    portalName,
    id: outID,
    children,
    onClosed,
    style
}, ref) => {
    const {
        colors
    } = NCoreUIKitTheme.useContext(customTheme);

    const id = useRef(outID ? outID : uuid());

    const scaleAnim = useRef(new Animated.Value(isAnimated && isScaleAnimated ? 0 : 1)).current;
    const opacityAnim = useRef(new Animated.Value(isAnimated ? 0 : 1)).current;

    const [
        isActive,
        setIsActive
    ] = useState(isActiveProp === undefined ? true : isActiveProp);

    useEffect(() => {
        if(isAnimated) {
            if(isScaleAnimated) {
                Animated.timing(scaleAnim, {
                    duration: openAnimationDelay,
                    easing: Easing.linear,
                    useNativeDriver: true,
                    toValue: 1
                }).start();
            }

            Animated.timing(opacityAnim, {
                duration: openAnimationDelay,
                easing: Easing.linear,
                useNativeDriver: true,
                toValue: 1
            }).start();
        }
    }, [
        isActive
    ]);

    useEffect(() => {
        if(isActiveProp !== undefined) {
            setIsActive(isActiveProp);
        }
    }, [
        isActiveProp
    ]);

    useImperativeHandle(
        ref,
        () => ({
            closeAnimation,
            setIsActive
        }),
        []
    );

    const closeAnimation = (_onClosed?: (props: {
        id: string;
    }) => void) => {
        if(isAnimated) {
            if(isScaleAnimated && !isScaleAnimatedOnlyOpen) {
                Animated.timing(scaleAnim, {
                    duration: closeAnimationDelay,
                    easing: Easing.linear,
                    useNativeDriver: true,
                    toValue: 0
                }).start();
            }

            Animated.timing(opacityAnim, {
                duration: closeAnimationDelay,
                easing: Easing.linear,
                useNativeDriver: true,
                toValue: 0
            }).start(({
                finished
            }) => {
                if(finished) {
                    if(_onClosed) _onClosed({
                        id: id.current
                    });

                    if(onClosed) onClosed({
                        id: id.current
                    });
                }
            });
        }
    };

    const renderOverlay = () => {
        if(!isOverlayVisible) {
            return null;
        }

        const {
            onFocus,
            onBlur,
            ...restOverlayProps
        } = overlayProps || {};

        return <TouchableWithoutFeedback
            {...restOverlayProps}
            onFocus={onFocus ? onFocus : undefined}
            onPress={() => {
                if(isDisabledOverlay) {
                    return;
                }

                if(onOverlayPress) onOverlayPress({
                    closeAnimation
                });
            }}
            onBlur={onBlur ? onBlur : undefined}
            style={[
                stylesheet.overlay
            ]}
        >
            <View
                style={[
                    stylesheet.overlayContent,
                    {
                        backgroundColor: colors.system.scrim
                    }
                ]}
                {...Platform.select({
                    web: {
                        dataSet: {
                            disablePressableHoverEffect: "true",
                            disablePressableDownEffect: "true"
                        }
                    },
                    default: {}
                })}
            />
        </TouchableWithoutFeedback>;
    };

    const renderContainer = () => {
        return <Animated.View
            style={[
                style,
                stylesheet.container,
                {
                    justifyContent: alignContent === "center" ? "center" : undefined,
                    alignItems: alignContent === "center" ? "center" : "baseline",
                    transform: [
                        {
                            scale: scaleAnim
                        }
                    ],
                    opacity: opacityAnim
                }
            ]}
            onLayout={(e) => {
                if (onContainerLayout) onContainerLayout(e);
            }}
        >
            {renderOverlay()}
            {isContentRequired ? renderContent() : children}
        </Animated.View>;
    };

    const renderContent = () => {
        if(!children) {
            return null;
        }

        let contentToRender = children;

        if(Children.count(children) === 1) {
            const singleChild = Children.toArray(children)[0];

            if(isValidElement<{ style?: unknown }>(singleChild)) {
                contentToRender = cloneElement(singleChild, {
                    style: [
                        singleChild.props.style,
                        {
                            zIndex: stylesheet.content.zIndex
                        }
                    ]
                });
            }
        }

        return <View
            style={[
                contentStyle,
                stylesheet.content
            ]}
        >
            {contentToRender}
        </View>;
    };

    const renderWithPortal = () => {
        return <Portal
            name={portalName ? portalName : "modal-system"}
        >
            {renderContainer()}
        </Portal>;
    };

    if(!isActive) {
        return null;
    }

    return isWorkWithPortal ? renderWithPortal() : renderContainer();
};
export default forwardRef(Modal);
