import {
    useState,
    type FC
} from "react";
import {
    type DimensionValue,
    Text as NativeText,
    TouchableOpacity,
    type ImageProps,
    Linking,
    Image,
    View
} from "react-native";
import type IMarkdownViewerProps from "./type";
import type {
    TextMarkdownTypes,
    MarkdownObject
} 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 CodeViewer from "../codeViewer";
import Text from "../text";

const MarkdownImage: FC<{
    containerWidth: number | null;
    imageProps?: ImageProps;
    node: MarkdownObject;
}> = ({
    containerWidth,
    imageProps,
    node
}) => {
    const [
        imgSize,
        setImgSize
    ] = useState<{
        height: number | string;
        width: number | string;
        isSetted: boolean;
    }>({
        isSetted: false,
        width: "100%",
        height: 0
    });

    const targetWidth = node.size ? node.size[0] : imgSize.width;
    const targetHeight = node.size ? node.size[1] : imgSize.height;

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

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

    return <Image
        {...imageProps}
        onLoad={(event) => {
            if (!node.size) {
                const nativeEvent = event.nativeEvent as unknown as {
                    source?: { height: number; width: number };
                    height?: number;
                    width?: number;
                };
                const height = nativeEvent.source?.height || nativeEvent.height || 0;
                const width = nativeEvent.source?.width || nativeEvent.width || 0;
                setImgSize({
                    isSetted: true,
                    height,
                    width
                });
            }
        }}
        resizeMode={node.resizeMode ? node.resizeMode : "contain"}
        style={{
            ...stylesheet.image,
            height: currentHeight as DimensionValue,
            width: currentWidth as DimensionValue,
            ...imageProps?.style
        }}
        alt={node.content}
        source={{
            uri: node.url
        }}
    />;
};

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

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

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

    const nodes = parseMarkdown(content);

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

    const renderItalic = ({
        part,
        i
    }: {
        part: string;
        i: number;
    }) => {
        return <NativeText
            {...italicTextProps}
            style={{
                ...stylesheet.italic,
                ...italicStyle
            }}
            key={`italic-${i}`}
        >
            {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: (variants as Record<string, keyof NCoreUIKit.Typography>)?.[node.nativeType] || 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}
            style={[
                stylesheet.enter,
                {
                    height: typography[((variants as Record<string, keyof NCoreUIKit.Typography>)?.[node.nativeType] || node.variant) as keyof NCoreUIKit.Typography].fontSize
                },
                enterTextProps?.style
            ]}
            key={node.key}
        />;
    };

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

    const renderImageComponent = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <MarkdownImage
            containerWidth={containerWidth}
            imageProps={imageProps}
            key={node.key}
            node={node}
        />;
    };

    const renderList = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...listContainerProps}
            style={[
                stylesheet.listContainer,
                listContainerProps?.style,
                {
                    paddingLeft: (node.indent || 0) * 8
                }
            ]}
            key={node.key}
        >
            <Text
                {...listIconProps}
                variant={(variants as Record<string, keyof NCoreUIKit.Typography>)?.[node.nativeType] || node.variant}
            >
                •{" "}
            </Text>
            <Text
                {...listTextProps}
                variant={(variants as Record<string, keyof NCoreUIKit.Typography>)?.[node.nativeType] || node.variant}
            >
                {renderInlineStyles(node.content)}
            </Text>
        </View>;
    };

    const renderCode = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...codeContainerProps}
            style={codeContainerProps?.style}
            key={node.key}
        >
            <CodeViewer
                language={node.language}
                code={node.content}
            />
        </View>;
    };

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

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

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

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

    const renderRight = ({
        node
    }: {
        node: MarkdownObject
    }) => {
        return <View
            {...rightContainerProps}
            style={[
                stylesheet.rightContainer,
                rightContainerProps?.style
            ]}
            key={node.key}
        >
            <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 renderImageComponent({
                        node
                    });
                }
                case "list":
                    return renderList({
                        node
                    });
                default:
                    return renderText({
                        node
                    });
            }
        })}
    </View>;
};
export default MarkdownViewer;
