import {
    useCallback,
    useEffect,
    useState,
    useMemo,
    useRef
} from "react";
import {
    type TextInputKeyPressEvent,
    Text as NativeText,
    ScrollView,
    TextInput,
    Platform,
    View
} from "react-native";
import type ICodeEditorProps from "./type";
import stylesheet, {
    useStyles
} from "./stylesheet";
import {
    NCoreUIKitTheme
} from "../../core/hooks";
import type {
    TokenType
} from "../codeViewer/type";
import {
    splitTokensIntoLines,
    parseCode
} from "../codeViewer/parser";
import SystemToolbar from "../systemToolbar";

const CodeEditor = ({
    isLineNumberVisible = true,
    isToolbarVisible = false,
    toolbarActions = [],
    containerStyle,
    onChangeText,
    language,
    style,
    value,
    ...props
}: ICodeEditorProps) => {
    const {
        typography,
        radiuses,
        borders,
        colors,
        spaces
    } = NCoreUIKitTheme.useContext();

    const inputRef = useRef<TextInput>(null);

    const [
        selection,
        setSelection
    ] = useState<{ start: number; end: number; }>({
        start: 0,
        end: 0
    });

    const tokens = useMemo(() => parseCode(value, language), [
        language,
        value
    ]);

    const lines = useMemo(() => {
        return splitTokensIntoLines(tokens);
    }, [tokens]);

    const {
        toolbarContainer: toolbarContainerDynamicStyle,
        editorContainer: editorContainerDynamicStyle,
        languageText: languageTextDynamicStyle,
        editorInput: editorInputDynamicStyle,
        container: containerDynamicStyle,
        lineRow: lineRowDynamicStyle,
        gutter: gutterDynamicStyle,
        text: textDynamicStyle
    } = useStyles({
        hasLanguage: !!language,
        isLineNumberVisible,
        isToolbarVisible,
        typography,
        radiuses,
        borders,
        colors,
        spaces
    });

    const getTokenColor = (type: TokenType) => {
        return colors.code[type as keyof typeof colors.code] || colors.code.default;
    };

    const handleKeyPress = (e: TextInputKeyPressEvent) => {
        if (e.nativeEvent.key === "Tab") {
            e.preventDefault();

            const before = value.substring(0, selection.start);
            const after = value.substring(selection.end);

            const newText = before + "    " + after;
            onChangeText?.(newText);
        }
    };

    useEffect(() => {
        if (Platform.OS === "web" && inputRef.current) {
            const node = inputRef.current as unknown as HTMLTextAreaElement;
            const handleKeyDown = (e: KeyboardEvent) => {
                const target = e.target as HTMLTextAreaElement;
                const start = target.selectionStart;
                const end = target.selectionEnd;

                const before = value.substring(0, start);
                const after = value.substring(end);

                const updateText = (newText: string, newCursorPos: number) => {
                    e.preventDefault();
                    onChangeText?.(newText);
                    setTimeout(() => {
                        if (inputRef.current) {
                            const currentTarget = inputRef.current as unknown as HTMLTextAreaElement;
                            currentTarget.selectionStart = newCursorPos;
                            currentTarget.selectionEnd = newCursorPos;
                            setSelection({
                                start: newCursorPos,
                                end: newCursorPos
                            });
                        }
                    }, 0);
                };

                if (e.key === "Tab") {
                    updateText(before + "    " + after, start + 4);
                    return;
                }

                const pairs: Record<string, string> = {
                    "\"": "\"",
                    "'": "'",
                    "(": ")",
                    "[": "]",
                    "{": "}",
                    "`": "`"
                };

                const prevChar = before.charAt(before.length - 1);
                const nextChar = after.charAt(0);

                if (pairs[e.key]) {
                    if (start !== end) {
                        const selectedText = value.substring(start, end);
                        updateText(before + e.key + selectedText + pairs[e.key] + after, end + 1);
                        return;
                    }

                    if ([
                        "\"",
                        "'",
                        "`"
                    ].includes(e.key) && nextChar === e.key) {
                        updateText(value, start + 1);
                        return;
                    }

                    if ([
                        "\"",
                        "'",
                        "`"
                    ].includes(e.key) && /[a-zA-Z0-9]/.test(prevChar)) {
                        return;
                    }

                    if (nextChar === "" || /[\s\]})]/ .test(nextChar)) {
                        updateText(before + e.key + pairs[e.key] + after, start + 1);
                        return;
                    }
                }

                if ([
                    ")",
                    "}",
                    "]"
                ].includes(e.key) && start === end && nextChar === e.key) {
                    updateText(value, start + 1);
                    return;
                }

                if (e.key === "Backspace" && start === end && start > 0) {
                    if (
                        (prevChar === "{" && nextChar === "}") ||
                        (prevChar === "[" && nextChar === "]") ||
                        (prevChar === "(" && nextChar === ")") ||
                        (prevChar === "\"" && nextChar === "\"") ||
                        (prevChar === "'" && nextChar === "'") ||
                        (prevChar === "`" && nextChar === "`")
                    ) {
                        updateText(before.slice(0, -1) + after.slice(1), start - 1);
                        return;
                    }
                }

                if (e.key === "Enter" && start === end) {
                    const currentLineText = before.split("\n").pop() || "";
                    const match = currentLineText.match(/^(\s*)/);
                    const currentIndentation = match?.[1] || "";

                    if (prevChar === "{" && nextChar === "}") {
                        updateText(
                            before + "\n" + currentIndentation + "    \n" + currentIndentation + after,
                            start + 1 + currentIndentation.length + 4
                        );
                        return;
                    } else if (currentIndentation.length > 0) {
                        updateText(
                            before + "\n" + currentIndentation + after,
                            start + 1 + currentIndentation.length
                        );
                        return;
                    }
                }
            };

            node.addEventListener?.("keydown", handleKeyDown);
            return () => {
                node.removeEventListener?.("keydown", handleKeyDown);
            };
        }

        return undefined;
    }, [
        value,
        onChangeText
    ]);

    const renderBackground = useCallback(() => {
        return <View>
            {
                language ?
                    <NativeText
                        style={[
                            stylesheet.content,
                            textDynamicStyle,
                            languageTextDynamicStyle
                        ]}
                        selectable={false}
                    >
                        // {language}
                    </NativeText>
                :
                    null
            }
            {lines.map((lineTokens, index) => {
                return <View
                    style={[
                        stylesheet.lineRow,
                        lineRowDynamicStyle
                    ]}
                    key={`line-${index}`}
                >
                    {
                        isLineNumberVisible ?
                            <View
                                style={[
                                    stylesheet.gutter,
                                    gutterDynamicStyle,
                                    {
                                        borderRightColor: colors.content.border.subtle
                                    }
                                ]}
                            >
                                <NativeText
                                    style={[
                                        stylesheet.lineNumber,
                                        {
                                            color: colors.content.text.low
                                        }
                                    ]}
                                    selectable={false}
                                >
                                    {index + 1}
                                </NativeText>
                            </View>
                        :
                            null
                    }
                    <NativeText
                        style={[
                            stylesheet.content,
                            textDynamicStyle,
                            {
                                color: colors.content.text.high
                            }
                        ]}
                    >
                        {lineTokens.map((token, tIndex) => {
                            return <NativeText
                                style={{
                                    color: getTokenColor(token.type)
                                }}
                                key={`token-${index}-${tIndex}`}
                            >
                                {token.value}
                            </NativeText>;
                        })}
                    </NativeText>
                </View>;
            })}
            {
                lines.length === 0 ?
                    <View
                        style={[
                            stylesheet.lineRow,
                            lineRowDynamicStyle
                        ]}
                    >
                        {
                            isLineNumberVisible ?
                                <View
                                    style={[
                                        stylesheet.gutter,
                                        gutterDynamicStyle,
                                        {
                                            borderRightColor: colors.content.border.subtle
                                        }
                                    ]}
                                >
                                    <NativeText
                                        style={[
                                            stylesheet.lineNumber,
                                            {
                                                color: colors.content.text.low
                                            }
                                        ]}
                                        selectable={false}
                                    >
                                        1
                                    </NativeText>
                                </View>
                            :
                                null
                        }
                        <NativeText
                            style={[
                                stylesheet.content,
                                textDynamicStyle,
                                {
                                    color: colors.content.text.high
                                }
                            ]}
                        >
                        </NativeText>
                    </View>
                :
                    null
            }
        </View>;
    }, [
        languageTextDynamicStyle,
        isLineNumberVisible,
        lineRowDynamicStyle,
        gutterDynamicStyle,
        textDynamicStyle,
        language,
        colors,
        lines
    ]);

    return <View
        style={[
            stylesheet.container,
            containerDynamicStyle,
            containerStyle
        ]}
    >
        {
            isToolbarVisible ?
                <View
                    style={[
                        stylesheet.toolbarContainer,
                        toolbarContainerDynamicStyle
                    ]}
                >
                    {toolbarActions.map((action, index) => <SystemToolbar
                        key={`toolbar-action-${index}`}
                        {...action}
                    />)}
                </View>
            :
                null
        }
        <View
            style={[
                editorContainerDynamicStyle
            ]}
        >
            <ScrollView
                showsHorizontalScrollIndicator={true}
                contentContainerStyle={[
                    stylesheet.scrollContent
                ]}
                style={[
                    stylesheet.scrollView
                ]}
                horizontal={true}
            >
                <View
                    style={[
                        stylesheet.scrollContent
                    ]}
                >
                    {renderBackground()}
                    <TextInput
                        {...props}
                        onSelectionChange={(e) => {
                            setSelection(e.nativeEvent.selection);
                            props.onSelectionChange?.(e);
                        }}
                        style={[
                            stylesheet.editorInput,
                            editorInputDynamicStyle,
                            style
                        ]}
                        onChangeText={onChangeText}
                        onKeyPress={handleKeyPress}
                        autoCapitalize="none"
                        autoCorrect={false}
                        spellCheck={false}
                        multiline={true}
                        ref={inputRef}
                        value={value}
                    />
                </View>
            </ScrollView>
        </View>
    </View>;
};
export default CodeEditor;
