import type { FC } from 'react';
import React, { useState, useCallback } from 'react';
import { GlyphIcon } from '@theorchard/suite-icons';
import cx from 'classnames';
import debounce from 'lodash.debounce';
import { GlyphButton } from '../glyphButton';
import { t } from './i18n';
import type { ComponentBaseProps } from '../../types';

export const CLASS_NAME = 'SearchInput';

type SIZES = 'small' | 'medium' | 'large';

export interface Props extends ComponentBaseProps {
    style?: React.CSSProperties;

    /**
     * The default text to display in the input field
     */
    defaultValue?: string;

    /**
     * The value of the input field
     */
    value?: string;

    /**
     * The default (and smallest) expand state of the input field
     * @deprecated Use `expanded` prop to control minimum size
     */
    defaultSize?: SIZES;

    /**
     * Placeholder text for the input field
     */
    placeholder?: string;

    /**
     * Callback function that is called when the input value changes
     */
    onChange: (value: string) => void;

    /**
     * Width of the field when text is entered.
     * @deprecated Use `maxWidth`
     */
    filledWidth?: number;

    /**
     * Sets a fixed width when the field is expanded.
     */
    width?: number;

    /**
     * Maximum width of the field
     * @default 200px
     */
    maxWidth?: number;

    /**
     * Width of the field when the placeholder is shown.
     * Defaults to the width of the placeholder text.
     * @deprecated Use `width` to control field size.
     */
    placeholderWidth?: number;

    /**
     * Controls whether the field is always expanded
     * @default false
     */
    expanded?: boolean;

    /**
     * Disables the input
     */
    disabled?: boolean;
}

/**
 * SearchInput is a single-line text box with a search icon that allows users to input search terms in order to filter through a list of objects.
 *
 * @type molecule
 * @status live
 * @tags form-elements
 */
export const SearchInput: FC<Props> = ({
    className,
    style,
    testId = CLASS_NAME,
    value,
    defaultValue,
    onChange,
    placeholder = t('placeholder'),
    defaultSize = 'small',
    filledWidth,
    width,
    maxWidth = 200,
    placeholderWidth,
    expanded = defaultSize !== 'small',
    disabled = false,
}) => {
    const inputRef = React.useRef<HTMLInputElement>(null);
    const [inputValue, setInputValue] = useState(defaultValue);
    const [measuredPlaceholderWidth, setMeasuredPlaceholderWidth] = useState(100);

    const hasValue = Boolean(value !== undefined ? value : inputValue);
    const calculateWidth = () => {
        if (width) return width;
        const max = filledWidth ?? maxWidth;
        if (hasValue) return max;
        return Math.min(placeholderWidth ?? measuredPlaceholderWidth, max);
    };

    // Measure hidden dummy element to figure out how much space the placeholder text requires.
    const measurePlaceholder = useCallback(
        (node: HTMLDivElement) => {
            setMeasuredPlaceholderWidth(46 + node?.clientWidth);
        },
        // Necessary to pick up changes to the placeholder prop
        // eslint-disable-next-line react-hooks/exhaustive-deps
        [placeholder]
    );

    const debouncedOnChange = debounce(onChange, 100);
    const onChangeHandler = value !== undefined ? onChange : debouncedOnChange;

    return (
        <label
            className={cx(CLASS_NAME, className, {
                ['applied']: hasValue,
                ['fixed-size']: expanded,
                disabled,
            })}
            data-testid={testId}
            style={
                {
                    '--segmented-input-active-width': `${calculateWidth()}px`,
                    ...style,
                } as React.CSSProperties
            }
        >
            <GlyphIcon name="search" size={16} />

            <input
                ref={inputRef}
                className={`${CLASS_NAME}-input`}
                data-testid={`${testId}-input`}
                defaultValue={defaultValue}
                value={value}
                placeholder={placeholder}
                onChange={(e) => {
                    setInputValue(e.currentTarget.value);
                    onChangeHandler(e.target.value);
                }}
                disabled={disabled}
            />
            <div className="hidden-placeholder" ref={measurePlaceholder} aria-hidden>
                {placeholder}
            </div>

            <GlyphButton
                variant="control"
                name="clear"
                className={`${CLASS_NAME}-clear`}
                onClick={() => {
                    if (inputRef.current) {
                        inputRef.current.value = '';
                        onChange('');
                        setInputValue('');
                    }
                }}
            />
        </label>
    );
};
