'use client';
import React, { useId, useRef } from 'react';
import { BodyText } from '~/components/BodyText';
import { InlineHelpText } from '~/components/InlineHelpText';
import { Label } from '~/components/Label';
import { useTestIdGenerator } from '~/hooks/useTestIdGenerator';
import devWarn from '~/utilities/dev-warn';
import { IS_DEV } from '~/utilities/env';
import { isRequired } from '~/utilities/isRequired';
import { Affix, CharacterCounter, ExpandableTextArea, InputBox, InputWrapper, InputsOnly, InputsWithAffixes, InteractiveAffix, StyledInput, } from './components';
import styles from './TextInput.module.css';
export const TextInput = React.forwardRef(function TextInput({ 'aria-activedescendant': ariaActiveDescendant, 'aria-autocomplete': ariaAutocomplete, 'aria-controls': ariaControls, 'aria-expanded': ariaExpanded, 'aria-owns': ariaOwns, 'aria-label': ariaLabel, autoComplete, autoFocus, corners = 'rounded', 'data-tag': dataTag, disabled, error, helpText, hideLabel, id, inline, label, loading, maxLength, multiline, name, onBlur, 
/* istanbul ignore next */
onChange = () => {
    // Do nothing
}, onFocus, onInput, onKeyDown, placeholder, prefix, prefixOnClick, prefixLabel, readOnly, required, role, characterCount, showCharacterCount, spellCheck, success, suffix, maxHeight, suffixOnClick, suffixLabel, textAlign = 'left', type = 'text', value, variant = 'outlined', disabledVariant = 'default', width = '100%', inputMode, minValue, maxValue, step, containerRef, }, givenRef) {
    /*
     * There's some complicated typing in this component to be aware of.
     * We use a <textarea> element if we're multiline, and an <input
     * type="text"> element if we're not. We also have to pass refs
     * around. Since refs are tied to a particular element type, we have
     * to make sure that TypeScript is able to correlate these properly.
     *
     * Specifically, we need to tell TypeScript that:
     * - `isMultine === true` means that `RefType` is `HTMLTextAreaElement`
     * - `isMultine === false` means that `RefType` is `HTMLInputElement`
     * Implied in the above is that `ref` can _never_ be typed as
     * `HTMLTextAreaElement | HTMLInputElement` except in the component
     * props themselves.
     *
     * This gets tricky though, because the `isMultiline` check is a
     * _runtime_ check, but `RefType` is a _compile time_ check! So how
     * do we correlate these? With a somewhat complicated TypeScript
     * feature called "Conditional Types." In short, anytime you see
     * `typeof isMultiline extends true`, this means we're correlating
     * these types. If you ever get errors about `HTMLTextAreaElement`
     * and `HTMLInputElement` being incompatible with each other, it
     * means you need to add this check.
     *
     * See https://www.typescriptlang.org/docs/handbook/2/conditional-types.html
     */
    const isMultiline = typeof multiline === 'number' ? multiline > 0 : !!multiline;
    const localRef = useRef(null);
    const ref = givenRef ?? localRef;
    // Backward compatibility: map showCharacterCount to characterCount
    const effectiveCharacterCount = characterCount ?? (showCharacterCount ? 'inline' : 'none');
    // Deprecation warning for showCharacterCount
    if (showCharacterCount !== undefined && IS_DEV) {
        devWarn('TextInput: The `showCharacterCount` prop is deprecated. Use `characterCount="inline"` instead. This prop will be removed in a future version.');
    }
    const localId = useId();
    const thisId = id ?? localId ?? '';
    const labelId = `${thisId}-label`;
    const prefixId = `${thisId}-prefix`;
    const suffixId = `${thisId}-suffix`;
    const charCountId = `${thisId}-char-count`;
    const errorId = `${thisId}-error`;
    const successId = `${thisId}-success`;
    const helpTextId = `${thisId}-help`;
    const getTestId = useTestIdGenerator(dataTag);
    const describedBy = [];
    if (error) {
        describedBy.push(errorId);
    }
    if (success) {
        describedBy.push(successId);
    }
    if (helpText) {
        describedBy.push(helpTextId);
    }
    if (effectiveCharacterCount !== 'none') {
        describedBy.push(charCountId);
    }
    const labelledBy = [];
    if (!hideLabel) {
        labelledBy.push(labelId);
    }
    if (prefix) {
        labelledBy.push(prefixId);
    }
    if (suffix) {
        labelledBy.push(suffixId);
    }
    // This is to deal with unicode code points, which can take up to 2 code units.
    // If we directly slice on the string truncation happens at code unit level, but
    // using iterable will ensure we only trucate at code point level.
    // Details in https://stackoverflow.com/a/70303029
    let charCount = 0;
    if (value) {
        charCount = Array.from(String(value)).length;
    }
    const rows = multiline && typeof multiline === 'number' && multiline > 1 ? multiline : 1;
    const additionalProps = isMultiline
        ? { rows }
        : {
            type,
            step,
        };
    let intervalValues = {};
    if (type === 'date' || type === 'number' || type === 'time' || type === 'datetime-local') {
        if (minValue) {
            intervalValues = { min: minValue };
        }
        if (maxValue) {
            intervalValues = {
                ...intervalValues,
                max: maxValue,
            };
        }
        if (step) {
            intervalValues = {
                ...intervalValues,
                step,
            };
        }
    }
    if (!ariaLabel && hideLabel) {
        if (typeof label === 'string') {
            ariaLabel = label;
        }
        else {
            devWarn('You must supply the `aria-label` prop when the label is a `ReactNode`');
        }
    }
    let ariaLabelledBy;
    if (!ariaLabel) {
        const labelledByIds = [];
        if (!hideLabel) {
            labelledByIds.push(labelId);
        }
        if (prefix) {
            labelledByIds.push(prefixId);
        }
        if (suffix) {
            labelledByIds.push(suffixId);
        }
        ariaLabelledBy = labelledByIds.join(' ');
    }
    // TODO (legacied @typescript-eslint/no-shadow)
    // This failure is legacied in and should be updated. DO NOT COPY.
    // eslint-disable-next-line @typescript-eslint/no-shadow
    const isElement = (prefix) => typeof prefix === 'object' && 'props' in prefix;
    // TODO (legacied @typescript-eslint/no-shadow)
    // This failure is legacied in and should be updated. DO NOT COPY.
    // eslint-disable-next-line @typescript-eslint/no-shadow
    const isStringOrElement = (prefix) => typeof prefix === 'string' || isElement(prefix);
    // TODO (legacied @typescript-eslint/no-shadow)
    // This failure is legacied in and should be updated. DO NOT COPY.
    // eslint-disable-next-line @typescript-eslint/no-shadow
    const filterIcon = (prefix) => isStringOrElement(prefix) ? undefined : prefix;
    const Prefix = filterIcon(prefix);
    const Suffix = filterIcon(suffix);
    const Input = (isMultiline ? ExpandableTextArea : StyledInput); // eslint-disable-line @typescript-eslint/no-explicit-any
    // We need to perform these manual casts as Typescript's type narrowing is not powerful enough
    // to automatically realize these guarantees based on the type of TextInputComponentProps
    const onChangeHandler = onChange;
    const onKeyDownHandler = onKeyDown;
    if (prefixOnClick && !prefixLabel && IS_DEV) {
        throw new Error('Please provide the `prefixLabel` prop when using `prefixOnClick` to ensure `TextInput` is accessible to screenreader users.');
    }
    if (suffixOnClick && !suffixLabel && IS_DEV) {
        throw new Error('Please provide the `suffixLabel` prop when using `suffixOnClick` to ensure `TextInput` is accessible to screenreader users.');
    }
    return (<InputWrapper ref={containerRef} inline={inline} widthValue={width} disabled={!!disabled} disabledVariant={disabledVariant} className={styles.root}>
        {label && !hideLabel && (<div className={styles.labelContainer}>
            <Label data-tag={getTestId('label')} id={labelId} error={!!error} htmlFor={thisId}>
              {label}
              {required && required !== 'no-indicator' && ' *'}
            </Label>
            {effectiveCharacterCount === 'label' && (<BodyText aria-label={`${charCount} ${maxLength ? `of ${maxLength}` : ''}`} data-tag={getTestId('charCount')} size="sm" color="var(--global-content-muted-default)">
                {charCount}
                {maxLength && ` / ${maxLength}`}
              </BodyText>)}
          </div>)}
        <InputBox data-tag={getTestId('box')} isDisabled={disabled} isMultiline={isMultiline} onClick={() => {
            /**
             * This seems like a workaround, but we don't have any history on why this is here.
             * We tried to remove this and it resulted in a number of tests failing in PRF.
             * TODO: We should investigate this further and remove this if possible.
             */
            /* istanbul ignore next */
            ref.current?.focus();
        }}>
          <InputsWithAffixes data-tag={getTestId('with-affixes')} corners={corners} variant={variant} isDisabled={disabled} hasError={!!error} isMultiline={isMultiline} characterCount={effectiveCharacterCount === 'inline' ? 'inline' : 'none'} disabledVariant={disabledVariant}>
            {prefix && (<Affix data-tag={getTestId('prefix')} id={prefixId} left multiline={multiline}>
                <InteractiveAffix type={prefixOnClick ? 'button' : undefined} onClick={prefixOnClick} as={prefixOnClick ? 'button' : undefined} aria-label={prefixLabel}>
                  {isStringOrElement(prefix) ? prefix : Prefix && <Prefix size="20px"/>}
                </InteractiveAffix>
              </Affix>)}
            <InputsOnly>
              <Input hasError={!!error} isDisabled={disabled} variant={variant} aria-activedescendant={ariaActiveDescendant} aria-autocomplete={ariaAutocomplete} aria-controls={ariaControls} aria-describedby={describedBy.length ? describedBy.join(' ') : undefined} aria-expanded={ariaExpanded} aria-invalid={Boolean(error)} aria-label={ariaLabel} aria-labelledby={ariaLabelledBy} aria-multiline={isMultiline ? true : undefined} aria-owns={ariaOwns} aria-readonly={readOnly} aria-required={isRequired(required)} autoComplete={autoComplete} 
    // TODO (legacied jsx-a11y/no-autofocus)
    // This failure is legacied in and should be updated. DO NOT COPY.
    // eslint-disable-next-line jsx-a11y/no-autofocus
    autoFocus={autoFocus} data-tag={dataTag} disabled={disabled} id={thisId} maxLength={maxLength} name={name} onBlur={onBlur} onChange={onChangeHandler} onInput={onInput} onFocus={onFocus} onKeyDown={onKeyDownHandler} placeholder={placeholder} readOnly={readOnly} ref={ref} required={required} role={role} spellCheck={spellCheck} textAlign={textAlign} value={value} characterCount={effectiveCharacterCount === 'inline' ? 'inline' : 'none'} maxHeight={maxHeight} inputMode={inputMode} {...additionalProps} {...intervalValues}/>
            </InputsOnly>
            {suffix && (<Affix data-tag={getTestId('suffix')} id={suffixId} right multiline={multiline}>
                <InteractiveAffix type={suffixOnClick ? 'button' : undefined} as={suffixOnClick ? 'button' : undefined} onClick={suffixOnClick} aria-label={suffixLabel}>
                  {isStringOrElement(suffix) ? suffix : Suffix && <Suffix size="20px"/>}
                </InteractiveAffix>
              </Affix>)}

            {effectiveCharacterCount === 'inline' && (<CharacterCounter aria-atomic="true" aria-live="polite" id={charCountId} isMultiline={isMultiline}>
                <BodyText aria-label={`${charCount} ${maxLength ? `of ${maxLength}` : ''}`} data-tag={getTestId('charCount')} size="sm">
                  {charCount}
                  {maxLength && ` / ${maxLength}`}
                </BodyText>
              </CharacterCounter>)}
          </InputsWithAffixes>
        </InputBox>
        <InlineHelpText id={helpTextId} inputId={thisId} helpText={helpText} error={error} success={success} loading={loading} data-tag={dataTag}/>
      </InputWrapper>);
});
//# sourceMappingURL=index.jsx.map