import React, { useState, useEffect, useMemo, useRef, useLayoutEffect } from 'react';
import cls from 'classnames';
import uniqueId from 'lodash-es/uniqueId';
import { faPen } from '@fortawesome/free-solid-svg-icons/faPen';
import { faCircleXmark } from '@fortawesome/free-solid-svg-icons/faCircleXmark';
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons/faCircleNotch';
import { FontAwesomeIcon } from '../font-awesome-icon';
import { Input } from '../input';
import { TextArea } from '../textarea';
import { useNonNativeButtonAttrs } from '../hooks/use-non-native-button-attrs';
import { useFocusWithin } from '../hooks/use-focus-within';
import { VALIDATION_RULE_TYPES } from '../form-field';
import { INLINE_EDIT_TAGS } from './types';
import { useTranslation } from '../hooks/use-translation';
import { TruncateWithTooltip } from '../truncate-with-tooltip';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const {
  H1,
  H2,
  H3,
  H4,
  H5,
  P
} = INLINE_EDIT_TAGS;
const classNameToTagMap = {
  'heading-01': H1,
  'heading-02': H2,
  'heading-03': H3,
  'heading-04': H4,
  'heading-05': H5
};
const defaultTag = P;
const openState = new Map();
const isSomeOneOpen = () => {
  const values = Array.from(openState.values());
  return values.some(val => val);
};

/**
 * @midnight-gray-supported
 * @uxr-only-supported
 * @alpine-light-supported
 * */
export default function InlineEdit({
  onSubmit,
  placeholder,
  textTestId,
  textWrapperTestId,
  inputTestId,
  value = '',
  className,
  overrideTag,
  onChange = () => {},
  isMultiline = false,
  typoClassName = 'body',
  inputProps = {},
  textareaProps = {},
  autoFocus = false,
  rows = 3,
  readOnly = false,
  submitAriaLabel,
  cancelAriaLabel,
  onCancel,
  isLoading,
  loadingAriaLabel,
  editableTextAriaLabel
}) {
  const {
    t
  } = useTranslation();
  const [touched, setTouched] = useState(false);
  const [inputValue, setInputValue] = useState(value);
  const [componentId] = useState(() => uniqueId());
  const [isEditMode, setIsEditMode] = useState(false);
  const container = useRef(null);
  const handleCancel = () => {
    setInputValue(value);
    setIsEditMode(false);
    onCancel?.();
  };
  const hasError = useMemo(() => [inputProps?.validity?.getType?.(inputValue), inputProps?.validity?.type, textareaProps?.validity?.getType?.(inputValue), textareaProps?.validity?.type].includes(VALIDATION_RULE_TYPES.ERROR), [textareaProps?.validity, inputProps?.validity]);
  useEffect(() => {
    if (hasError) {
      setIsEditMode(true);
    }
  }, [hasError]);
  const onFormSubmit = () => {
    setTouched(true);
    if (!hasError) {
      onSubmit(inputValue);
      setIsEditMode(false);
    }
  };
  useEffect(() => {
    onChange(inputValue);
  }, [inputValue]);
  useEffect(() => {
    openState.set(componentId, isEditMode);
  }, [isEditMode]);
  useEffect(() => () => {
    openState.delete(componentId);
  }, []);
  useLayoutEffect(() => {
    // If the component is mounted, autoFocus is true, and there are no other inline edits in edit mode -
    // set the open state and edit mode to true
    // previously it was set in state initialization function but since react 18 that initialization function
    // can be called twice without unmount if 2+ components rendering inline edit are wrapped in Suspense and lazy loaded
    // it results in inline edits being locked up because first id is set to true, however latest render would use different id
    // it turns out to be common react thing: https://stackoverflow.com/questions/73325723/suspense-as-a-root-element-causes-two-instances-of-component-in-production-and
    if (autoFocus && !(isSomeOneOpen() || readOnly || isLoading)) {
      openState.set(componentId, true);
      setIsEditMode(true);
    }
  }, []);
  const onTextClick = e => {
    e.preventDefault();
    if (!isSomeOneOpen() && !readOnly && !isLoading) {
      setIsEditMode(true);
    }
  };
  const buttonAttrs = useNonNativeButtonAttrs({
    onClick: onTextClick,
    onKeyDown: onTextClick,
    isDisabled: readOnly
  });
  const focusWithinProps = useFocusWithin({
    onBlur: onFormSubmit
  });
  const onFieldChange = e => {
    const {
      value: newValue
    } = e.target;
    setInputValue(newValue);
  };
  const fieldProps = {
    placeholder,
    className: 'inline-edit-input controllable-field',
    value: inputValue.trim() ? inputValue : '',
    onChange: onFieldChange,
    fieldClass: typoClassName,
    touched,
    autoFocus: true,
    fieldTestId: inputTestId,
    useFont: false,
    disableApplyButton: hasError,
    shouldDisplayControls: true,
    onSubmit: onFormSubmit,
    onCancel: handleCancel,
    rightIcon: hasError ? faCircleXmark : null,
    iconTooltipText: isMultiline ? textareaProps.validity?.value?.toString() : inputProps.validity?.value?.toString(),
    submitAriaLabel,
    cancelAriaLabel
  };
  const onFormKeyDown = e => {
    if (e.key === 'Esc' || e.key === 'Escape') {
      e.stopPropagation();
      handleCancel();
    }
    if (isMultiline) {
      if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
        e.preventDefault();
        onFormSubmit();
      }
    }
  };
  useEffect(() => {
    setTouched(false);
  }, [isEditMode]);
  useEffect(() => {
    setInputValue(value);
  }, [value]);
  const TagName = useMemo(() => overrideTag || classNameToTagMap[typoClassName] || defaultTag, [typoClassName, overrideTag]);
  return /*#__PURE__*/_jsx("div", {
    ref: container,
    className: cls('inline-edit-container', className),
    children: isEditMode ?
    /*#__PURE__*/
    // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
    _jsx("form", {
      onKeyDown: onFormKeyDown,
      onBlur: focusWithinProps.onBlur,
      onSubmit: e => {
        e.preventDefault();
        onFormSubmit();
      },
      children: isMultiline ? /*#__PURE__*/_jsx(TextArea, {
        ...fieldProps,
        ...textareaProps,
        validity: {
          type: textareaProps.validity?.type,
          value: hasError
        },
        rows: rows,
        autoFocus: true,
        className: cls(fieldProps.className, 'resized')
        // eslint-disable-next-line react/forbid-component-props
        ,
        style: {
          width: container?.current?.offsetWidth
        }
      }) : /*#__PURE__*/_jsx(Input, {
        ...fieldProps,
        ...inputProps,
        validity: {
          type: inputProps.validity?.type,
          value: hasError
        },
        shouldDisplayControls: true,
        autoFocus: true
      })
    }) : /*#__PURE__*/_jsxs("div", {
      className: cls('inline-edit-text-container', (readOnly || isLoading) && 'read-only'),
      "test-id": textWrapperTestId,
      ...buttonAttrs,
      "aria-label": editableTextAriaLabel ?? t('Click to edit text'),
      children: [/*#__PURE__*/_jsxs("div", {
        className: "inline-edit-text-wrapper",
        children: [/*#__PURE__*/_jsx(TruncateWithTooltip, {
          className: cls(typoClassName, 'inline-text-wrapper', isMultiline && `multiline rows-${rows}`, !inputValue.trim() && 'empty'),
          tooltipTestId: textTestId ? `${textTestId}-tooltip` : 'inline-text-tooltip',
          children: /*#__PURE__*/_jsx(TagName, {
            "test-id": textTestId,
            children: inputValue.trim() ? inputValue : placeholder
          })
        }), isLoading && /*#__PURE__*/_jsx("span", {
          role: "status",
          "aria-label": loadingAriaLabel ?? t('Updating text'),
          "test-id": "is-loading-icon",
          className: "is-loading-icon",
          "aria-busy": true,
          children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
            icon: faCircleNotch,
            spin: true
          })
        })]
      }), !readOnly && !isLoading && /*#__PURE__*/_jsx(FontAwesomeIcon, {
        className: "inline-edit-pencil-button",
        icon: faPen,
        "test-id": "edit-icon"
      })]
    })
  });
}