import {
    useEffect,
    useState,
    type FC
} from "react";
import {
    Text as NativeText,
    TouchableOpacity,
    Linking,
    Image,
    View
} from "react-native";
import type IMarkdownViewerProps from "./type";
import type {
    MarkdownObject,
    TextMarkdownTypes
} from "./type";
import stylesheet, {
    useStyles
} from "./stylesheet";
import {
    parseMarkdown
} from "./util";
import {
    NCoreUIKitTheme
} from "../../core/hooks";
import type ITextProps from "../text/type";
import {
    Quote as QuoteIcon
} from "lucide-react-native";
import Text from "../text";

const MarkdownViewer: FC<IMarkdownViewerProps> = ({
    blockquoteIconSize = "titleLargeSize",
    codeTextColor = "emphasized",
    blockquoteIconColor = "mid",
    blockquoteTextColor = "mid",
    blockquoteContainerProps,
    centerContainerProps,
    blockquoteTextStyle,
    rightContainerProps,
    blockquoteIconStyle,
    centerMarkdownProps,
    leftContainerProps,
    rightMarkdownProps,
    listContainerProps,
    linkContainerProps,
    codeContainerProps,
    leftMarkdownProps,
    linkMarkdownProps,
    customTextStyles,
    customTextProps,
    italicTextProps,
    enterTextProps,
    boldTextProps,
    listIconProps,
    codeTextProps,
    listTextProps,
    italicStyle,
    imageProps,
    boldStyle,
    content,
    style,
    ...props
}) => {
    const {
        typography,
        radiuses,
        colors,
        spaces
    } = NCoreUIKitTheme.useContext();

    const [
        containerWidth,
        setContainerWidth
    ] = useState<null | number>(null);

    const {
        blockquoteContainer: blockquoteContainerDynamicStyle,
        blockquoteIcon: blockquoteIconDynamicStyle,
        codeContainer: codeContainerDynamicStyle
    } = useStyles({
        radiuses,
        colors,
        spaces
    });

    const nodes = parseMarkdown(content);

    const renderBold = ({
        part,
        i
    }: {
        part: string;
        i: number;
    }) => {
        return <NativeText
            {...boldTextProps}
            key={`bold-${i}`}
            style={{
                ...stylesheet.bold,
                ...boldStyle
            }}
        >
            {part.replace(/\*\*/g, "")}
        </NativeText>;
    };

    const renderItalic = ({
        part,
        i
    }: {
        part: string;
        i: number;
    }) => {
        return <NativeText
            {...italicTextProps}
            key={`italic-${i}`}
            style={{
                ...stylesheet.italic,
                ...italicStyle
            }}
        >
            {part.replace(/__/g, "")}
        </NativeText>;
    };

    const renderInlineStyles = (_content: string) => {
        const parts = _content.split(/(\*\*.*?\*\*|__.*?__)/g);

        return parts.map((part, i) => {
            if (part.startsWith("**") && part.endsWith("**")) return renderBold({
                part,
                i
            });

            if (part.startsWith("__") && part.endsWith("__")) return renderItalic({
                part,
                i
            });

            return part;
        });
    };

    const renderText = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        let textProps: ITextProps = {
            variant: node.variant
        };

        if(customTextStyles && customTextStyles[node.nativeType as TextMarkdownTypes]) {
            const currentTextStyle = customTextStyles[node.nativeType as TextMarkdownTypes];

            textProps.style = currentTextStyle;
        }

        if(customTextProps && customTextProps[node.nativeType as TextMarkdownTypes]) {
            const currentTextProps = customTextProps[node.nativeType as TextMarkdownTypes];

            textProps = {
                ...textProps,
                ...currentTextProps
            };
        }

        return <Text
            {...textProps}
            key={node.key}
        >{renderInlineStyles(node.content)}</Text>;
    };

    const renderEnter = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...enterTextProps}
            key={node.key}
            style={[
                stylesheet.enter,
                {
                    height: typography[node.variant as keyof NCoreUIKit.Typography].fontSize
                },
                enterTextProps?.style
            ]}
        />;
    };

    const renderBlockquote = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...blockquoteContainerProps}
            key={node.key}
            style={[
                blockquoteContainerDynamicStyle,
                blockquoteContainerProps?.style
            ]}
        >
            <QuoteIcon
                color={colors.content.icon[blockquoteIconColor]}
                size={typography[blockquoteIconSize].fontSize}
                style={{
                    ...blockquoteIconDynamicStyle,
                    ...blockquoteIconStyle
                }}
            />
            <Text
                color={blockquoteTextColor}
                variant={node.variant}
                style={{
                    ...stylesheet.blockquoteText,
                    ...blockquoteTextStyle
                }}
            >
                {renderInlineStyles(node.content)}
            </Text>
        </View>;
    };

    const renderImage = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        const [
            imgSize,
            setImgSize
        ] = useState<{
            height: number | string;
            width: number | string;
            isSetted: boolean;
        }>({
            isSetted: false,
            width: "100%",
            height: 0
        });

        useEffect(() => {
            if(!imgSize.isSetted && node.size) {
                setImgSize({
                    height: node.size[1] as number | string,
                    width: node.size[0] as number | string,
                    isSetted: true
                });
            }
        }, [imgSize]);

        const currentWidth = typeof imgSize.width === "number" || imgSize.width.indexOf("%") === -1 || !containerWidth
            ? imgSize.width
            : (containerWidth * Number(imgSize.width.split("%")[0])) / 100;

        const currentHeight = typeof imgSize.height === "number" || imgSize.height.indexOf("%") === -1 || !containerWidth
            ? imgSize.height
            : (containerWidth * Number(imgSize.height.split("%")[0])) / 100;

        return <Image
            {...imageProps}
            alt={node.content}
            key={node.key}
            source={{
                uri: node.url
            }}
            resizeMode={node.resizeMode ? node.resizeMode : "contain"}
            onLoad={(event) => {
                if(!node.size) {
                    const {
                        height,
                        width
                    } = event.nativeEvent.source;

                    setImgSize({
                        isSetted: true,
                        height,
                        width
                    });
                }
            }}
            style={{
                ...stylesheet.image,
                height: currentHeight,
                width: currentWidth,
                ...imageProps?.style
            }}
        />;
    };

    const renderList = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...listContainerProps}
            key={node.key}
            style={[
                stylesheet.listContainer,
                listContainerProps?.style
            ]}
        >
            <Text
                {...listIconProps}
                variant={node.variant}
            >
                •{" "}
            </Text>
            <Text
                {...listTextProps}
                variant={node.variant}
            >
                {renderInlineStyles(node.content)}
            </Text>
        </View>;
    };

    const renderCode = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...codeContainerProps}
            key={node.key}
            style={[
                codeContainerDynamicStyle,
                codeContainerProps?.style
            ]}
        >
            <Text
                {...codeTextProps}
                variant={node.variant}
                color={codeTextColor}
            >
                {node.content}
            </Text>
        </View>;
    };

    const renderLink = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <TouchableOpacity
            {...linkContainerProps}
            key={node.key}
            onPress={async () => {
                const isCanOpen = await Linking.canOpenURL(node.url as string);

                if(isCanOpen) {
                    Linking.openURL(node.url as string);
                }
            }}
        >
            <MarkdownViewer
                {...linkMarkdownProps}
                content={node.content}
            />
        </TouchableOpacity>;
    };

    const renderCenter = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...centerContainerProps}
            key={node.key}
            style={[
                stylesheet.centerContainer,
                centerContainerProps?.style
            ]}
        >
            <MarkdownViewer
                {...centerMarkdownProps}
                content={node.content}
            />
        </View>;
    };

    const renderLeft = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...leftContainerProps}
            key={node.key}
            style={[
                stylesheet.leftContainer,
                leftContainerProps?.style
            ]}
        >
            <MarkdownViewer
                {...leftMarkdownProps}
                content={node.content}
            />
        </View>;
    };

    const renderRight = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...rightContainerProps}
            key={node.key}
            style={[
                stylesheet.rightContainer,
                rightContainerProps?.style
            ]}
        >
            <MarkdownViewer
                {...rightMarkdownProps}
                content={node.content}
            />
        </View>;
    };

    return <View
        {...props}
        onLayout={({
            nativeEvent
        }) => {
            setContainerWidth(nativeEvent.layout.width);
        }}
        style={[
            stylesheet.container,
            style
        ]}
    >
        {nodes.map((node) => {
            if (!node.content.trim() && node.nativeType === "<p>") return null;

            switch (node.type) {
                case undefined:
                    return null;
                case "text": {
                    return renderText({
                        node
                    });
                }
                case "space":
                    return renderEnter({
                        node
                    });
                case "blockquote": {
                    return renderBlockquote({
                        node
                    });
                }
                case "right": {
                    return renderRight({
                        node
                    });
                }
                case "left": {
                    return renderLeft({
                        node
                    });
                }
                case "center": {
                    return renderCenter({
                        node
                    });
                }
                case "link": {
                    return renderLink({
                        node
                    });
                }
                case "code": {
                    return renderCode({
                        node
                    });
                }
                case "image": {
                    return renderImage({
                        node
                    });
                }
                case "list":
                    return renderList({
                        node
                    });
                default:
                    return renderText({
                        node
                    });
            }
        })}
    </View>;
};
export default MarkdownViewer;
