import React, { ComponentProps, CSSProperties, useEffect, useRef, useState } from 'react';
import classNames from 'clsx';
import type { TypographyVariant } from '@mui/material';

import { getInputStyleCssVariables, useInputStyles } from '../helpers';

import TextField from '../../../TextField/TextField';
import Typography from '../../../Typography/Typography';

import styles from './TextField.module.scss';

type Props = Omit<ComponentProps<typeof TextField>, 'variant'> & {
  value: string;
  fontWeight: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; // The font weight of the text
  variant?: TypographyVariant; // The variant of the text style, e.g., 'body1', 'body2'
  error: boolean; // Error flag, indicates the error state of the text field
  minRows: number; // The minimum number of rows in the text field
  rows: number; // The number of rows in the text field
  editable: boolean; // Flag indicating if the text field can be edited
  disabled: boolean; // Flag indicating if the text field is disabled
  readOnly: boolean; // Flag indicating if the text field is read-only
  onCancel: (event: React.KeyboardEvent<HTMLDivElement>) => void; // Function called when editing is canceled
  onApply: (
    event: React.FocusEvent<HTMLTextAreaElement | HTMLInputElement, Element> | React.KeyboardEvent<HTMLDivElement>,
    trimmedValue: string,
  ) => void; // Function called when changes are applied
  placeholder: string; // Text displayed in the text field when it is empty
  helperText: string; // Helper text displayed below the text field
  maxLength: number; // Maximum length of the input text
  textPlaceholder: string; // Text displayed when there is no value in the text field
};

function InlineEditTextField({
  variant = 'body2',
  rows,
  minRows,
  maxLength,
  disabled,
  readOnly,
  value,
  editable,
  fontWeight,
  placeholder,
  error,
  helperText,
  textPlaceholder,
  onApply,
  onCancel,
  ...props
}: Props) {
  const multiline = rows !== 1;
  const canBeEdit = !disabled && !readOnly;
  const inputRef = useRef<HTMLInputElement | null>(null);
  const focusPosition = React.useRef<number>(0);
  const [stateValue, setValue] = useState(value);
  const [stateEditable, setEditState] = useState(editable && canBeEdit);

  const { fontSize, fontWeight: stateFontWeight, lineHeight, letterSpacing } = useInputStyles(variant, fontWeight);

  useEffect(() => setValue(value), [value]);

  useEffect(() => setEditState(editable && canBeEdit), [disabled, editable, readOnly]);

  useEffect(() => {
    if (stateEditable && inputRef.current) {
      inputRef.current.focus();
      inputRef.current.setSelectionRange(focusPosition.current, focusPosition.current);
    }
  }, [stateEditable]);

  const style = {
    ...getInputStyleCssVariables(fontSize, stateFontWeight, lineHeight, letterSpacing),
    '--internal-min-rows': `${(minRows || 0) * (fontSize * lineHeight)}px`,
  } as CSSProperties;

  const stateOnApply = (
    event: React.FocusEvent<HTMLTextAreaElement | HTMLInputElement, Element> | React.KeyboardEvent<HTMLDivElement>,
  ) => {
    const trimmedValue = stateValue.trim();
    onApply(event, trimmedValue);
    setEditState(false);
    setValue(trimmedValue);
    focusPosition.current = 0;
  };

  let textColor = 'text.primary';

  if (disabled) {
    textColor = 'text.disabled';
  } else if (!stateValue) {
    textColor = 'text.secondary';
  }

  return error || stateEditable ? (
    <TextField
      {...props}
      inputRef={inputRef}
      size="medium"
      variant="filled"
      autoComplete="new-field"
      placeholder={placeholder}
      error={error}
      helperText={helperText}
      minRows={minRows}
      maxRows={rows}
      multiline={multiline}
      value={stateValue}
      onChange={({ target }) => setValue(target.value)}
      onBlur={stateOnApply}
      onKeyDown={(event) => {
        if (event.code === 'Escape') {
          if (onCancel) {
            onCancel(event);
          }
          setEditState(false);
          setValue(value);
          focusPosition.current = 0;
        } else if (event.key === 'Enter') {
          if (!multiline || (multiline && !event.shiftKey)) {
            event.preventDefault();
            stateOnApply(event);
          }
        }
      }}
      classes={{
        root: styles.TextFieldWrapper,
      }}
      InputProps={{
        classes: {
          root: styles.InputRoot,
          input: styles.Input,
        },
        style,
      }}
      FormHelperTextProps={{
        classes: {
          root: styles.HelperText,
        },
      }}
      inputProps={{
        maxLength,
      }}
    />
  ) : (
    <Typography
      variant={variant}
      component="div"
      className={classNames(styles.Typography, {
        [styles.Typography___edit]: canBeEdit,
        [styles.Typography___multiline]: multiline,
      })}
      color={textColor}
      onClick={
        !readOnly
          ? () => {
              const selection = window.getSelection();

              if (selection?.type !== 'Range') {
                focusPosition.current = selection?.focusOffset ?? 0;
                setEditState(true);
              }
            }
          : undefined
      }
      style={style}
    >
      <Typography
        variant="inherit"
        title={value}
        component="div"
        rows={rows > 1 ? rows : undefined}
        noWrap={rows === 1}
        className={rows <= 0 ? styles.Scrollable : undefined}
      >
        {stateValue || textPlaceholder || '\u00A0'}
      </Typography>
    </Typography>
  );
}

export default InlineEditTextField;
