import { isEmptyContent } from 'hero-editor';
import React, {
  forwardRef,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import { Animated, Easing } from 'react-native';
import type { ReactElement, Ref } from 'react';
import type { LayoutChangeEvent } from 'react-native';
import { useTheme } from '../../theme';
import {
  StyledAsteriskLabelInsideTextInput,
  StyledBorderBackDrop,
  StyledContainer,
  StyledError,
  StyledErrorAndHelpTextContainer,
  StyledErrorAndMaxLengthContainer,
  StyledErrorContainer,
  StyledHelperText,
  StyledInputContentContainer,
  StyledLabelContainerInsideTextInput,
  StyledLabelInsideTextInput,
  StyledTextInputAndLabelContainer,
  StyledTextInputContainer,
} from '../TextInput/StyledTextInput';
import { LABEL_ANIMATION_DURATION } from '../TextInput';
import Typography from '../Typography';
import RichTextEditorInput from './RichTextEditorInput';
import type {
  RichTextEditorInputProps,
  RichTextEditorRef,
} from './RichTextEditorInput';

type EditorValue = {
  type: string;
  children: any;
}[];

export interface RichTextEditorProps
  extends Omit<RichTextEditorInputProps, 'editorRef'> {
  /**
   * Field label
   */
  label: string;
  /**
   * Field helper text
   */
  helpText?: string;
  /**
   *  Whether the input is required, if true, an asterisk will be appended to the label.
   * */
  required?: boolean;
  /**
   * Testing ID of the component
   */
  testID?: string;
  /**
   * Imperative ref to expose the component method
   */
  forwardedRef?: Ref<RichTextEditorRef>;
}

const defaultValue: EditorValue = [
  {
    type: 'paragraph',
    children: [{ text: '' }],
  },
];

const RichTextEditor = ({
  autoFocus = true,
  name,
  placeholder = '',
  onChange,
  onCursorChange,
  error = '',
  style = {},
  label,
  helpText,
  required,
  testID,
  onFocus,
  onBlur,
  forwardedRef,
  value = defaultValue,
}: RichTextEditorProps): ReactElement => {
  const theme = useTheme();

  const [isFocused, setIsFocused] = useState(false);
  const isEmptyValue = useMemo(() => isEmptyContent(value), [value]);
  const state = useMemo(() => {
    if (error) {
      return 'error';
    }
    if (isEmptyValue) {
      return 'filled';
    }
    return 'default';
  }, [isFocused, error, isEmptyValue]);

  const [containerHeight, setContainerHeight] = React.useState(0);

  const focusAnimation = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.timing(focusAnimation, {
      toValue: isFocused || !isEmptyValue ? 1 : 0,
      duration: LABEL_ANIMATION_DURATION,
      easing: Easing.bezier(0.4, 0, 0.2, 1),
      useNativeDriver: true,
    }).start();
  }, [focusAnimation, isEmptyValue, isFocused]);

  const onLayout = useCallback((event: LayoutChangeEvent) => {
    setContainerHeight(event.nativeEvent.layout.height);
  }, []);

  const backgroundColor = theme.__hd__.textInput.colors.containerBackground;

  const handleEditorFocus = useCallback(() => {
    onFocus?.();
    setIsFocused(true);
  }, [onFocus]);

  const handleEditorBlur = useCallback(() => {
    onBlur?.();
    setIsFocused(false);
  }, [onBlur]);

  return (
    <StyledContainer testID={testID}>
      <StyledTextInputContainer
        onLayout={onLayout}
        themeVariant="text"
        themeState={state}
      >
        <StyledBorderBackDrop
          themeState={state}
          themeFocused={isFocused}
          themeFilled={!isEmptyValue}
          style={{ backgroundColor }}
        />

        <StyledInputContentContainer themeHasLabel={!!label}>
          <StyledLabelContainerInsideTextInput
            themeVariant="text"
            pointerEvents="none"
            testID="input-label-container"
            style={[
              {
                transformOrigin: 'top left',
              },
              {
                transform: [
                  {
                    translateY: focusAnimation.interpolate({
                      inputRange: [0, 1],
                      outputRange: [
                        Math.max(
                          0,
                          (containerHeight -
                            theme.__hd__.textInput.lineHeights.label) /
                            2
                        ),
                        theme.__hd__.textInput.space.labelFocusedTranslateY,
                      ],
                    }),
                  },
                  {
                    scale: focusAnimation.interpolate({
                      inputRange: [0, 1],
                      outputRange: [1, 0.75],
                    }),
                  },
                ],
              },
            ]}
          >
            {!!label && (
              <StyledLabelInsideTextInput
                style={{
                  backgroundColor:
                    theme.__hd__.textInput.colors.labelBackground,
                }}
                testID="input-label"
                themeState={state}
              >
                {required && (
                  <StyledAsteriskLabelInsideTextInput
                    style={{
                      backgroundColor:
                        theme.__hd__.textInput.colors.labelBackground,
                    }}
                    themeState={state}
                  >
                    *
                  </StyledAsteriskLabelInsideTextInput>
                )}
                <Typography.Body intent="muted" numberOfLines={1}>
                  {label}
                </Typography.Body>
              </StyledLabelInsideTextInput>
            )}
          </StyledLabelContainerInsideTextInput>

          <StyledTextInputAndLabelContainer themeHasLabel={!!label}>
            <RichTextEditorInput
              name={name}
              value={value}
              style={[
                style,
                {
                  marginHorizontal:
                    theme.__hd__.textInput.space.inputHorizontalMargin,
                },
              ]}
              testID="webview"
              onChange={onChange}
              autoFocus={autoFocus}
              editorRef={forwardedRef}
              placeholder={placeholder}
              onBlur={handleEditorBlur}
              onFocus={handleEditorFocus}
              onCursorChange={onCursorChange}
            />
          </StyledTextInputAndLabelContainer>
        </StyledInputContentContainer>
      </StyledTextInputContainer>
      <StyledErrorAndHelpTextContainer>
        <StyledErrorAndMaxLengthContainer>
          {error ? (
            <StyledErrorContainer>
              <StyledError testID="input-error-message">{error}</StyledError>
            </StyledErrorContainer>
          ) : (
            !!helpText && <StyledHelperText>{helpText}</StyledHelperText>
          )}
        </StyledErrorAndMaxLengthContainer>
      </StyledErrorAndHelpTextContainer>
    </StyledContainer>
  );
};
const RichTextEditorWithRef = forwardRef<
  RichTextEditorRef,
  Omit<RichTextEditorProps, 'forwardedRef'>
>((props, ref) => <RichTextEditor {...props} forwardedRef={ref} />);

RichTextEditorWithRef.displayName = 'RichTextEditor';

export default RichTextEditorWithRef;
