/* eslint-disable max-lines-per-function */
import React, { forwardRef } from "react";
import { Flex } from "../flex";
import { typographyVariants } from "../text";
import { pxToRem } from "../../utils";
import { useInput, isTextArea } from "./use-input";
import {
  StyledFieldSetContent,
  StyledFieldSet,
  StyledInfoText,
  StyledInput,
  StyledLabel,
  StyledLegend,
  StyledRoot,
  StyledRootContent,
  StyledTextArea,
  InputFieldContainer,
  LEGEND_PADDING,
} from "./input.styled";
import type * as Stitches from "@stitches/react";
import type { WithTestId } from "../../types";
import type { ForwardedRef } from "react";

type SingleLineNumberInputProps = {
  type: "number";
  /**
   * Clears the value on blur if it's invalid and blocks entering `e` notation numbers into.
   * This works only with `number` type fields.
   * For example, by default, user can type i.e.: `1e` or just `+` into the field, and such values will be considered
   * invalid value, and the read value will be an empty string.
   * This can break floating label (which on empty value is rendered inside the input) and data in forms.
   * This prop allows you to work around this issue by sacrificing the `e` notation and clearing some invalid inputs.
   */
  clearScientificNotation?: boolean;
};

type SingleLineGeneralInputProps = {
  type?: Exclude<React.ComponentProps<typeof StyledInput>["type"], "number">;
  clearScientificNotation?: never;
};

type SingleLineInputPropsType = SingleLineNumberInputProps | SingleLineGeneralInputProps;

export type SingleLineInputProps = Omit<
  WithTestId<React.ComponentProps<typeof StyledInput>>,
  "type"
> &
  SingleLineInputPropsType & {
    multiLine?: false;
  };

export interface TextAreaProps extends WithTestId<React.ComponentProps<typeof StyledTextArea>> {
  /**
   * If true, will render a textarea element
   */
  multiLine: true;
  /**
   * If there is a value, will render the textarea in fix height. Only applicable when multiLine set to `true`
   */
  height?: number;
  type?: never;
}

export interface CommonProps extends Stitches.VariantProps<typeof StyledRoot> {
  /**
   * Label Element
   */
  label: string;
  /**
   * Informative text displayed under the input
   */
  info?: string;
  /**
   * If true, will apply ellipsis text overflow to info text
   */
  infoEllipsis?: boolean;
  /**
   * Renders rightmost inside the input
   */
  rightElement?: React.ReactNode;
  /**
   * If true, will keep the label in a raised position
   */
  raisedLabel?: boolean;
}

export type InputProps = (SingleLineInputProps | TextAreaProps) & CommonProps;

// export const isTextArea = (props: InputProps): props is TextAreaProps & CommonProps => {
//   return Boolean(props.multiLine);
// };

/**
 * A component that displays an `input` or `textarea` element.
 */
export const Input = forwardRef<HTMLInputElement | HTMLTextAreaElement, InputProps>(
  (props, ref) => {
    const {
      label,
      inputId,
      type,
      restProps,
      inputRef,
      rightElement,
      rightElementContainer,
      isMaxHeight,
      isTextareaFixHeight,
      isFocused,
      isFocusedOrHasValue,
      info,
      error,
      fieldsetRef,
      onChange,
      handleOnChange,
      onPaste,
      onKeyDown,
      onBlur,
    } = useInput(props, ref);

    const { css, raisedLabel, infoEllipsis, ...rest } = restProps;

    const input = isTextArea(props) ? (
      <StyledTextArea
        ref={inputRef as ForwardedRef<HTMLTextAreaElement>}
        id={inputId}
        css={{
          ...(!isMaxHeight && { overflow: "hidden" }),
          ...(isTextareaFixHeight && {
            height: pxToRem(props.height),
            maxHeight: pxToRem(props.height),
          }),
        }}
        onChange={handleOnChange}
        raisedLabel={raisedLabel}
        {...(error && { "aria-invalid": true })}
        {...(rest as TextAreaProps)}
      />
    ) : (
      <StyledInput
        ref={inputRef as ForwardedRef<HTMLInputElement>}
        type={type}
        id={inputId}
        onChange={onChange as SingleLineInputProps["onChange"]}
        onKeyDown={onKeyDown}
        onPaste={onPaste}
        onBlur={onBlur}
        raisedLabel={raisedLabel}
        {...(error && { "aria-invalid": true })}
        {...(rest as Omit<SingleLineInputProps, "type">)}
      />
    );

    return (
      <StyledRoot error={error} css={css} {...(rest.disabled && { "data-disabled": true })}>
        <StyledRootContent>
          <StyledFieldSetContent>
            <StyledLabel
              htmlFor={inputId}
              css={{
                ...((isFocusedOrHasValue || raisedLabel) && {
                  top: 0,
                  ...typographyVariants.body5,
                }),
                ...(isTextareaFixHeight &&
                  !(isFocusedOrHasValue || raisedLabel) && { top: pxToRem(32) }),
              }}
            >
              {label}
            </StyledLabel>
            <StyledFieldSet
              ref={fieldsetRef}
              focus={isFocused}
              aria-hidden
              css={{
                ...(isTextareaFixHeight &&
                  isTextArea(props) && {
                    height: pxToRem(props.height),
                    maxHeight: pxToRem(props.height),
                  }),
              }}
            >
              <StyledLegend
                {...((isFocusedOrHasValue || raisedLabel) && {
                  css: { maxWidth: "100%", ...typographyVariants.body5, px: LEGEND_PADDING },
                })}
              >
                {label}
              </StyledLegend>
            </StyledFieldSet>
          </StyledFieldSetContent>
          <InputFieldContainer>
            {input}
            <Flex ref={rightElementContainer} {...(rightElement && { css: { pr: "$4" } })}>
              {rightElement}
            </Flex>
          </InputFieldContainer>

          {info && (
            <StyledInfoText variant="body5" ellipsis={infoEllipsis}>
              {info}
            </StyledInfoText>
          )}
        </StyledRootContent>
      </StyledRoot>
    );
  }
);

Input.displayName = "Input";
