import React, { forwardRef, useState, createRef, useCallback, useRef, useLayoutEffect } from "react";
import { css } from "@emotion/react";
import { cx, mergeRefs } from "../../utils";
import { useOutsideClick, useResizeObserver } from "../../hooks";
import { applyDynamicLabel } from "./mixins";
import { useAutoResize, ROOT_FONT_SIZE } from "./hooks";
import { StyledContent, StyledHelpers, StyledLeftHelper, StyledRightHelper, StyledLabel, StyledPlaceholder, StyledTextArea, StyledTextAreaWrapper, StyledContainer, StyledIndicator, StyledOptionalText, OuterLabelWrapper, StyledIndicatorWrapper, StyledHintWrapper, TitleCaption, StyledOutsideHelpersWrapper, StyledHiddenTextArea, StyledContentWrapper, StyledHeaderSlot } from "./TextArea.styles";
import { classes, privateTokens } from "./TextArea.tokens";
import { base as viewCSS } from "./variations/_view/base";
import { base as sizeCSS } from "./variations/_size/base";
import { base as readOnlyCSS } from "./variations/_read-only/base";
import { base as disabledCSS } from "./variations/_disabled/base";
import { base as hintViewCSS } from "./variations/_hint-view/base";
import { base as hintSizeCSS } from "./variations/_hint-size/base";
import { HintComponent } from "./ui/Hint/Hint";
const { innerPlaceholderUp, focusedOuterPlaceholderColor, hidePlaceHolder, styledContainer, styledTextArea, styledTextAreaWrapper, styledPlaceholder, styledHelpers, hasHeaderSlot, styledContentWrapper } = classes;
const base = /*#__PURE__*/ css(applyDynamicLabel, ";position:relative;box-sizing:border-box;display:flex;flex-direction:column;", "base");
const getDimensionValue = (value)=>{
    if (value === undefined || value === null || value === '') return undefined;
    return Number.isNaN(Number(value)) ? String(value) : `${value}rem`;
};
// TODO: Удалить после отказа от старых библиотек plasma-web / plasma-b2c
const fallbackStatusMap = {
    '': 'primary',
    success: 'positive',
    warning: 'warning',
    error: 'negative'
};
const HINT_DEFAULT_OFFSET = [
    0,
    0
];
// TODO: Перенести этот метод в файл applyDynamicLabel.ts
export const getDynamicLabelClasses = (props, focused)=>{
    const { readOnly, label, labelPlacement, rows, value, size } = props;
    // Добавить класс отвечающий за изменение цвета плейсхолдера при фокусе
    const withFocusedOuterUpPlaceholder = !readOnly && focused && (!label || labelPlacement === 'outer') ? focusedOuterPlaceholderColor : undefined;
    // Добавить класс отвечающий за поднятие и уменьшение плейсхолдера
    const withInnerPlaceholderUp = labelPlacement === 'inner' && label && !rows && size !== 'xs' && (!readOnly && (value || focused) || readOnly && value) ? innerPlaceholderUp : undefined;
    // Добавить класс отвечающий за скрытие плейсхолдера
    const withHidePlaceholder = value && !label || labelPlacement === 'inner' && (focused && !readOnly || value) && label && rows || labelPlacement === 'outer' && value || labelPlacement === 'inner' && size === 'xs' && value ? hidePlaceHolder : undefined;
    return [
        withFocusedOuterUpPlaceholder,
        withInnerPlaceholderUp,
        withHidePlaceholder
    ];
};
export const textAreaRoot = (Root)=>/*#__PURE__*/ forwardRef((props, innerRef)=>{
        const { id, className, style, // layout
        label, labelPlacement = 'inner', titleCaption, placeholder, leftHelper, leftHelperPlacement = 'inner', rightHelperPlacement = 'inner', helperText, rightHelper, contentRight, headerSlot, requiredPlacement = 'right', hasRequiredIndicator = false, // hint
        hintTrigger = 'hover', hintText, hintView = 'default', hintSize = 'm', hintTargetIcon, hintPlacement = 'auto', hintHasArrow, hintOffset = HINT_DEFAULT_OFFSET, hintWidth, hintContentLeft, // variations
        appearance, status, view, size, readOnly, disabled, required = false, clear, optional = false, optionalText = 'optional', hasDivider, // controlled
        value, defaultValue, // textarea-specific
        resize, autoResize = false, minAuto = 0, maxAuto, height, width, rows, cols, // events
        onChange, // additional
        labelAriaHidden = false, ...rest } = props;
        const [isHintVisible, setIsHintVisible] = useState(false);
        const computedHeight = getDimensionValue(height);
        const [helperWidth, setHelperWidth] = useState('100%');
        const innerWidth = getDimensionValue(width) || helperWidth;
        const [focused, setFocused] = useState(false);
        // TODO: перенести в общую переменную для value снаружи и внутри
        const [uncontrolledValue, setUncontrolledValue] = useState();
        const outerRef = /*#__PURE__*/ createRef();
        const hiddenRef = useRef(null);
        const hintRef = useRef(null);
        useOutsideClick(()=>{
            setIsHintVisible(false);
        }, [
            hintRef
        ]);
        const isInnerLeftHelperPlacement = leftHelperPlacement === 'inner';
        const isInnerRightHelperPlacement = rightHelperPlacement === 'inner';
        const leftHelperText = leftHelper || helperText;
        const innerOptional = required ? false : optional;
        const hasLeftHelper = Boolean(leftHelper || helperText);
        const hasRightHelper = Boolean(rightHelper);
        const hasHelper = isInnerLeftHelperPlacement && hasLeftHelper || isInnerRightHelperPlacement && hasRightHelper;
        const hasOuterLabel = Boolean(label && labelPlacement === 'outer');
        const hasInnerLabel = Boolean(label && labelPlacement === 'inner' && size !== 'xs');
        const hasPlaceholderOptional = innerOptional && !hasOuterLabel;
        const hasHeader = Boolean(headerSlot) && !(clear || appearance === 'clear');
        const overriddenView = status !== undefined ? fallbackStatusMap[status] : view;
        const textareaHelperId = id ? `${id}-helper` : undefined;
        const hasExplicitHeight = Boolean(computedHeight);
        const placeholderLabel = hasInnerLabel ? label : placeholder;
        const applyAutoResize = autoResize || Boolean(clear || appearance === 'clear' || appearance === 'viewMode');
        const clearClass = clear || appearance === 'clear' ? classes.clear : undefined;
        const hasHintClass = hintText ? classes.hasHint : undefined;
        const hasRightContentClass = contentRight ? classes.hasRightContent : undefined;
        const hasDividerClass = hasDivider ? classes.hasDivider : undefined;
        const requiredPlacementClass = requiredPlacement === 'right' ? classes.requiredAlignRight : undefined;
        const handleHintShow = ()=>setIsHintVisible(true);
        const handleHintHide = ()=>setIsHintVisible(false);
        const handleHintClick = (event)=>{
            if (!hintText || hintTrigger !== 'click') {
                return;
            }
            event.stopPropagation();
            const targetIsPopover = event.target === hintRef.current;
            const rootHasTarget = hintRef.current?.contains(event.target);
            if (!targetIsPopover && !rootHasTarget) {
                setIsHintVisible(true);
            }
        };
        useLayoutEffect(()=>{
            if (outerRef.current) {
                setUncontrolledValue(outerRef.current.value);
            }
        }, [
            outerRef
        ]);
        useLayoutEffect(()=>{
            if (!hiddenRef.current || !cols) {
                return;
            }
            const { width: elementWidth } = hiddenRef.current.getBoundingClientRect();
            setHelperWidth(`${elementWidth / ROOT_FONT_SIZE}rem`);
        }, [
            cols
        ]);
        useResizeObserver(outerRef, (currentElement)=>{
            const { width: inlineWidth } = currentElement.style;
            if (inlineWidth) {
                const { width: elementWidth } = currentElement.getBoundingClientRect();
                setHelperWidth(`${elementWidth / ROOT_FONT_SIZE}rem`);
            }
        });
        useAutoResize(applyAutoResize, outerRef, value || uncontrolledValue, minAuto, maxAuto, resize, hiddenRef, size);
        const onFocusHandler = useCallback(()=>{
            setFocused(true);
        }, []);
        const onBlurHandler = useCallback(()=>{
            setFocused(false);
        }, []);
        const onChangeHandler = useCallback((event)=>{
            // INFO: Для крайне редких кейсов, когда value снаружи не контролируется
            if (value === undefined) {
                setUncontrolledValue(event?.target.value);
            }
            onChange?.(event);
        }, [
            value,
            onChange
        ]);
        const handleTextAreaFocus = ()=>{
            if (readOnly || disabled || !outerRef?.current) {
                return;
            }
            outerRef.current.focus();
        };
        const dynamicLabelClasses = getDynamicLabelClasses({
            size,
            readOnly,
            label,
            labelPlacement,
            value: value || uncontrolledValue || defaultValue,
            rows,
            labelAriaHidden
        }, focused);
        const optionalTextNode = innerOptional ? /*#__PURE__*/ React.createElement(StyledOptionalText, {
            inheritFont: !hasOuterLabel
        }, Boolean(hasPlaceholderOptional && (placeholderLabel || label)) && '\xa0', optionalText) : null;
        return /*#__PURE__*/ React.createElement(Root, {
            view: overriddenView,
            size: size,
            disabled: disabled,
            readOnly: readOnly,
            style: {
                width: innerWidth,
                height: computedHeight,
                ...hasExplicitHeight && {
                    [privateTokens.inputActualHeight]: '100%'
                },
                ...style
            },
            className: cx(clearClass, hasDividerClass, hasHintClass, className),
            onClick: handleTextAreaFocus,
            "data-root": true,
            ...hintText && {
                hintView,
                hintSize
            }
        }, (hasOuterLabel || titleCaption) && /*#__PURE__*/ React.createElement(OuterLabelWrapper, {
            isInnerLabel: labelPlacement === 'inner'
        }, hasOuterLabel && /*#__PURE__*/ React.createElement(StyledIndicatorWrapper, null, /*#__PURE__*/ React.createElement(StyledLabel, {
            "aria-hidden": labelAriaHidden
        }, label), hintText && /*#__PURE__*/ React.createElement(StyledHintWrapper, null, /*#__PURE__*/ React.createElement(HintComponent, {
            ref: hintRef,
            hintText: hintText,
            hintTrigger: hintTrigger,
            isHintVisible: isHintVisible,
            hintTargetIcon: hintTargetIcon,
            hintPlacement: hintPlacement,
            hintHasArrow: hintHasArrow,
            hintOffset: hintOffset,
            hintWidth: hintWidth,
            hintContentLeft: hintContentLeft,
            handleHintShow: handleHintShow,
            handleHintHide: handleHintHide,
            handleHintClick: handleHintClick
        })), required && hasRequiredIndicator && /*#__PURE__*/ React.createElement(StyledIndicator, {
            className: cx(classes.outerLabelPlacement, requiredPlacementClass, hasHintClass)
        }), optionalTextNode), titleCaption && /*#__PURE__*/ React.createElement(TitleCaption, null, titleCaption)), /*#__PURE__*/ React.createElement(StyledContainer, {
            className: cx(styledContainer, ...dynamicLabelClasses),
            hasExplicitHeight: hasExplicitHeight,
            onFocus: onFocusHandler,
            onBlur: onBlurHandler
        }, !hasOuterLabel && /*#__PURE__*/ React.createElement(React.Fragment, null, required && hasRequiredIndicator && /*#__PURE__*/ React.createElement(StyledIndicator, {
            className: cx(classes.innerLabelPlacement, requiredPlacementClass)
        }), hintText && /*#__PURE__*/ React.createElement(StyledHintWrapper, {
            className: classes.innerLabelPlacement
        }, /*#__PURE__*/ React.createElement(HintComponent, {
            ref: hintRef,
            hintText: hintText,
            hintTrigger: hintTrigger,
            isHintVisible: isHintVisible,
            hintTargetIcon: hintTargetIcon,
            hintPlacement: hintPlacement,
            hintHasArrow: hintHasArrow,
            hintOffset: hintOffset,
            hintWidth: hintWidth,
            hintContentLeft: hintContentLeft,
            size: size,
            handleHintShow: handleHintShow,
            handleHintHide: handleHintHide,
            handleHintClick: handleHintClick,
            isInnerLabel: true
        }))), /*#__PURE__*/ React.createElement(StyledTextAreaWrapper, {
            className: cx(styledTextAreaWrapper, hasHeader && hasHeaderSlot),
            hasHelper: hasHelper,
            hasHeader: hasHeader,
            hasExplicitHeight: hasExplicitHeight,
            isClear: clear || appearance === 'clear'
        }, headerSlot && !clear && /*#__PURE__*/ React.createElement(StyledHeaderSlot, null, headerSlot), contentRight && !hasHeader && /*#__PURE__*/ React.createElement(StyledContent, null, contentRight), hasHeader && /*#__PURE__*/ React.createElement(StyledContentWrapper, {
            className: styledContentWrapper,
            hasHeader: hasHeader
        }, contentRight && /*#__PURE__*/ React.createElement(StyledContent, {
            hasHeader: hasHeader
        }, contentRight), headerSlot && placeholderLabel && /*#__PURE__*/ React.createElement(StyledPlaceholder, {
            hasContentRight: Boolean(contentRight),
            className: styledPlaceholder,
            htmlFor: id
        }, placeholderLabel, !hasOuterLabel && optionalTextNode)), /*#__PURE__*/ React.createElement(StyledTextArea, {
            className: cx(styledTextArea, hasRightContentClass),
            id: id,
            hasContentRight: Boolean(contentRight),
            hasExplicitHeight: hasExplicitHeight,
            ref: mergeRefs(outerRef, innerRef),
            disabled: disabled,
            required: required,
            placeholder: placeholderLabel,
            "aria-describedby": textareaHelperId,
            value: value,
            readOnly: readOnly,
            rows: rows,
            cols: cols,
            resize: resize,
            defaultValue: defaultValue,
            onChange: onChangeHandler,
            ...rest
        }), Boolean(applyAutoResize || cols) && /*#__PURE__*/ React.createElement(StyledHiddenTextArea, {
            "aria-hidden": true,
            ref: hiddenRef,
            hasContentRight: Boolean(contentRight),
            value: value || uncontrolledValue || ' ',
            onChange: ()=>{},
            defaultValue: defaultValue,
            cols: cols
        })), hasHelper && /*#__PURE__*/ React.createElement(StyledHelpers, {
            className: styledHelpers,
            id: textareaHelperId
        }, hasLeftHelper && isInnerLeftHelperPlacement && /*#__PURE__*/ React.createElement(StyledLeftHelper, {
            className: focused ? classes.leftHelperFocus : ''
        }, leftHelperText), hasRightHelper && isInnerRightHelperPlacement && /*#__PURE__*/ React.createElement(StyledRightHelper, {
            "data-root": true
        }, rightHelper)), !hasHeader && placeholderLabel && /*#__PURE__*/ React.createElement(StyledPlaceholder, {
            hasContentRight: Boolean(contentRight),
            className: styledPlaceholder,
            htmlFor: id
        }, placeholderLabel, !hasOuterLabel && optionalTextNode)), !isInnerLeftHelperPlacement && hasLeftHelper || !isInnerRightHelperPlacement && hasRightHelper ? /*#__PURE__*/ React.createElement(StyledOutsideHelpersWrapper, {
            id: textareaHelperId
        }, !isInnerLeftHelperPlacement && hasLeftHelper && /*#__PURE__*/ React.createElement(StyledLeftHelper, null, leftHelperText), !isInnerRightHelperPlacement && hasRightHelper && /*#__PURE__*/ React.createElement(StyledRightHelper, {
            "data-root": true
        }, rightHelper)) : null);
    });
export const textAreaConfig = {
    name: 'TextArea',
    tag: 'div',
    layout: textAreaRoot,
    base,
    variations: {
        size: {
            css: sizeCSS
        },
        view: {
            css: viewCSS
        },
        readOnly: {
            css: readOnlyCSS,
            attrs: true
        },
        disabled: {
            css: disabledCSS,
            attrs: true
        },
        hintView: {
            css: hintViewCSS
        },
        hintSize: {
            css: hintSizeCSS
        }
    },
    defaults: {
        size: 'm',
        view: 'primary'
    }
};
