import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
// (C) 2007-2026 GoodData Corporation
import { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
import cx from "classnames";
import { defaultImport } from "default-import";
import ReactTextareaAutosize from "react-textarea-autosize";
import { v4 as uuid } from "uuid";
import { Overlay } from "../Overlay/Overlay.js";
import { ENUM_KEY_CODE } from "../typings/utilities.js";
// There are known compatibility issues between CommonJS (CJS) and ECMAScript modules (ESM).
// In ESM, default exports of CJS modules are wrapped in default properties instead of being exposed directly.
// https://github.com/microsoft/TypeScript/issues/52086#issuecomment-1385978414
const TextareaAutosize = defaultImport(ReactTextareaAutosize);
/**
 * @internal
 */
export const EditableLabel = forwardRef((props, ref) => {
    const { children = false, className = "", id, maxLength = 100000, maxRows = 1, onSubmit, onCancel = (v) => v, onEditingStart = (v) => v, onChange = (v) => v, placeholder = "", scrollToEndOnEditingStart = true, textareaInOverlay = false, autofocus = false, isEditableLabelWidthBasedOnText = false, isEditableLabelWidthDynamic = false, ariaLabel, autocomplete, } = props;
    const wrapperRef = useRef(null);
    const rootRef = useRef(null);
    const textareaRef = useRef(null);
    const measureSpanRef = useRef(null);
    const focusTimeoutRef = useRef(null);
    const resizeObserverRef = useRef(null);
    const [value, setValue] = useState(props.value);
    const [isEditing, setIsEditing] = useState(false);
    const [doFocus, setDoFocus] = useState(false);
    const [shouldRestoreFocus, setShouldRestoreFocus] = useState(false);
    const [rootWidth, setRootWidth] = useState(0);
    const [dynamicWidth, setDynamicWidth] = useState(undefined);
    const [textareaWidth, setTextareaWidth] = useState(100);
    const [textareaFontSize, setTextareaFontSize] = useState(undefined);
    const setWrapperRefs = useCallback((node) => {
        wrapperRef.current = node;
        if (typeof ref === "function") {
            ref(node);
        }
        else if (ref) {
            ref.current = node;
        }
    }, [ref]);
    useEffect(() => {
        setValue(props.value);
    }, [props.value]);
    const onDocumentClick = useCallback((e) => {
        if (isClickOutsideTextarea(e.target)) {
            const textAreaNode = textareaRef.current;
            if (textAreaNode) {
                textAreaNode.blur();
            }
        }
    }, []);
    const removeListeners = useCallback(() => {
        document.removeEventListener("click", onDocumentClick);
    }, [onDocumentClick]);
    const isMultiLine = useMemo(() => {
        return maxRows > 1;
    }, [maxRows]);
    const measureRootDimensions = useCallback(() => {
        const rootElement = rootRef.current;
        if (!rootElement) {
            return;
        }
        const rootElementFontSize = getComputedStyle(rootElement).fontSize;
        setTextareaWidth(rootElement.offsetWidth);
        setTextareaFontSize(Math.floor(parseInt(rootElementFontSize, 10)));
    }, [rootRef]);
    const selectAndFocus = useCallback(() => {
        const componentElement = textareaRef.current;
        if (componentElement) {
            if (focusTimeoutRef.current !== null) {
                window.clearTimeout(focusTimeoutRef.current);
            }
            // without the timeout the focus sometimes got stolen by the previously active item for some reason
            focusTimeoutRef.current = window.setTimeout(() => {
                componentElement.focus();
                if (scrollToEndOnEditingStart && isMultiLine) {
                    componentElement.scrollTop = componentElement.scrollHeight;
                }
                componentElement.select();
                if (textareaInOverlay) {
                    measureRootDimensions();
                }
            }, 1);
        }
    }, [scrollToEndOnEditingStart, isMultiLine, textareaInOverlay, measureRootDimensions]);
    const edit = useCallback((_e) => {
        if (!isEditing) {
            setIsEditing(true);
            document.addEventListener("mousedown", onDocumentClick);
            setDoFocus(true);
            onEditingStart();
        }
    }, [isEditing, onEditingStart, onDocumentClick]);
    const onWrapperKeyDown = useCallback((event) => {
        const isEnter = event.key === "Enter";
        const isSpace = event.key === " ";
        if (isEnter || isSpace) {
            event.preventDefault();
            event.stopPropagation();
            edit();
        }
    }, [edit]);
    useEffect(() => {
        const rootNode = rootRef.current;
        const focusTimeoutId = focusTimeoutRef.current;
        if (rootNode) {
            resizeObserverRef.current = new ResizeObserver(() => {
                setRootWidth(rootNode.offsetWidth);
            });
            resizeObserverRef.current.observe(rootNode);
        }
        document.addEventListener("click", onDocumentClick);
        if (autofocus) {
            edit();
        }
        return () => {
            if (rootNode) {
                if (resizeObserverRef.current) {
                    resizeObserverRef.current.unobserve(rootNode);
                }
            }
            removeListeners();
            if (focusTimeoutId) {
                clearTimeout(focusTimeoutId);
            }
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);
    useEffect(() => {
        if (doFocus && isEditing) {
            selectAndFocus();
            setDoFocus(false);
        }
    }, [doFocus, isEditing, selectAndFocus]);
    useEffect(() => {
        if (shouldRestoreFocus && !isEditing) {
            wrapperRef.current?.focus();
            setShouldRestoreFocus(false);
        }
    }, [shouldRestoreFocus, isEditing]);
    // Measure the hidden span to compute dynamic textarea width before paint
    useLayoutEffect(() => {
        if (isEditableLabelWidthDynamic && isEditing && measureSpanRef.current) {
            const width = Math.ceil(measureSpanRef.current.getBoundingClientRect().width) + 2;
            setDynamicWidth(width);
        }
    }, [isEditableLabelWidthDynamic, isEditing, value]);
    // Reset dynamic width when editing ends
    useEffect(() => {
        if (!isEditing) {
            setDynamicWidth(undefined);
        }
    }, [isEditing]);
    const onKeyDown = (e) => {
        const isSubmit = e.keyCode === ENUM_KEY_CODE.KEY_CODE_ENTER;
        const isCancel = e.keyCode === ENUM_KEY_CODE.KEY_CODE_ESCAPE;
        if (isSubmit || isCancel) {
            e.preventDefault();
            e.stopPropagation();
        }
        if (isSubmit) {
            onSubmitHandler();
        }
        if (isCancel) {
            onCancelHandler();
        }
    };
    const onSubmitHandler = () => {
        const oldValue = props.value;
        const newTrimmedValue = value.trim();
        if (newTrimmedValue === "") {
            setValue("");
        }
        if (oldValue === newTrimmedValue) {
            onCancel(oldValue);
        }
        else {
            onSubmit(newTrimmedValue);
        }
        setValue(newTrimmedValue);
        setIsEditing(false);
        removeListeners();
    };
    const onCancelHandler = () => {
        onCancel(props.value);
        setValue(props.value);
        setIsEditing(false);
        setShouldRestoreFocus(true);
        removeListeners();
    };
    const onChangeHandler = (e) => {
        const { value } = e.target;
        setValue(value);
        onChange(value);
    };
    const isClickOutsideTextarea = (target) => {
        const textAreaNode = textareaRef.current;
        return !!textAreaNode && !textAreaNode.contains(target);
    };
    const renderTextarea = (style = {}) => {
        return (_jsx(TextareaAutosize, { style: style, rows: 1, maxRows: maxRows, maxLength: maxLength, onKeyDown: onKeyDown, onBlur: onSubmitHandler, onChange: onChangeHandler, defaultValue: props.value, placeholder: placeholder, ref: textareaRef, id: id, "aria-label": ariaLabel, autoComplete: autocomplete }));
    };
    const renderEditableLabelEdit = () => {
        if (textareaInOverlay) {
            return renderTextAreaInOverlay();
        }
        let style = {};
        if (isEditableLabelWidthDynamic && dynamicWidth !== undefined) {
            style = { width: dynamicWidth };
        }
        else if (rootRef.current && isEditableLabelWidthBasedOnText) {
            style = { width: rootWidth };
        }
        return (_jsxs(_Fragment, { children: [isEditableLabelWidthDynamic ? (_jsx("span", { ref: measureSpanRef, className: "gd-editable-label-measure", "aria-hidden": "true", children: value || placeholder })) : null, renderTextarea(style)] }));
    };
    const renderTextAreaInOverlay = () => {
        const alignId = `gd-editable-label-${uuid()}`;
        const style = {
            width: textareaWidth,
            fontSize: `${textareaFontSize ?? 14}px`,
            // http://stackoverflow.com/a/6295222
            lineHeight: `${(textareaFontSize ?? 14) * 1.25}px`,
        };
        return (_jsx("div", { "data-testid": "textarea-wrapper", className: `${alignId} gd-editable-label-textarea-wrapper`, children: _jsx(Overlay, { alignTo: `.${alignId}`, alignPoints: [
                    {
                        align: "cr cr",
                    },
                ], children: _jsx("div", { className: "gd-editable-label-overlay", children: renderTextarea(style) }) }) }));
    };
    const editableLabelClasses = cx({
        "gd-editable-label": true,
        "s-editable-label": true,
        "is-editing": isEditing,
        placeholder: value === "",
    }, className);
    const displayValue = children || value || placeholder;
    return (_jsx("div", { "data-testid": "editable-label", ref: setWrapperRefs, className: editableLabelClasses, onClick: edit, tabIndex: isEditing ? -1 : 0, role: isEditing ? undefined : "button", "aria-label": isEditing ? undefined : ariaLabel, onKeyDown: isEditing ? undefined : onWrapperKeyDown, onMouseDown: (e) => e.stopPropagation(), children: _jsx("div", { className: "gd-editable-label-inner", ref: rootRef, children: isEditing ? renderEditableLabelEdit() : displayValue }) }));
});
EditableLabel.displayName = "EditableLabel";
