// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import clsx from "clsx";
import React, { forwardRef, memo, useImperativeHandle, useRef, useState } from "react";

import "./input.less";

interface InputGroupProps {
    children: React.ReactNode;
    className?: string;
}

const InputGroup = memo(
    forwardRef<HTMLDivElement, InputGroupProps>(({ children, className }: InputGroupProps, ref) => {
        const [isFocused, setIsFocused] = useState(false);

        const manageFocus = (focused: boolean) => {
            setIsFocused(focused);
        };

        return (
            <div
                ref={ref}
                className={clsx("input-group", className, {
                    focused: isFocused,
                })}
            >
                {React.Children.map(children, (child) => {
                    if (React.isValidElement(child)) {
                        return React.cloneElement(child as any, { manageFocus });
                    }
                    return child;
                })}
            </div>
        );
    })
);

interface InputLeftElementProps {
    children: React.ReactNode;
    className?: string;
}

const InputLeftElement = memo(({ children, className }: InputLeftElementProps) => {
    return <div className={clsx("input-left-element", className)}>{children}</div>;
});

interface InputRightElementProps {
    children: React.ReactNode;
    className?: string;
}

const InputRightElement = memo(({ children, className }: InputRightElementProps) => {
    return <div className={clsx("input-right-element", className)}>{children}</div>;
});

interface InputProps {
    value?: string;
    className?: string;
    onChange?: (value: string) => void;
    onKeyDown?: (event: React.KeyboardEvent<any>) => void;
    onFocus?: () => void;
    onBlur?: () => void;
    placeholder?: string;
    defaultValue?: string;
    required?: boolean;
    maxLength?: number;
    autoFocus?: boolean;
    disabled?: boolean;
    isNumber?: boolean;
    inputRef?: React.MutableRefObject<any>;
    manageFocus?: (isFocused: boolean) => void;
}

const Input = memo(
    forwardRef<HTMLInputElement, InputProps>(
        (
            {
                value,
                className,
                onChange,
                onKeyDown,
                onFocus,
                onBlur,
                placeholder,
                defaultValue = "",
                required,
                maxLength,
                autoFocus,
                disabled,
                isNumber,
                manageFocus,
            }: InputProps,
            ref
        ) => {
            const [internalValue, setInternalValue] = useState(defaultValue);
            const inputRef = useRef<HTMLInputElement>(null);

            useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);

            const handleInputChange = (e: React.ChangeEvent<any>) => {
                const inputValue = e.target.value;

                if (isNumber && inputValue !== "" && !/^\d*$/.test(inputValue)) {
                    return;
                }

                if (value === undefined) {
                    setInternalValue(inputValue);
                }

                onChange && onChange(inputValue);
            };

            const handleFocus = () => {
                manageFocus?.(true);
                onFocus?.();
            };

            const handleBlur = () => {
                manageFocus?.(false);
                onBlur?.();
            };

            const inputValue = value ?? internalValue;

            return (
                <input
                    className={clsx("input", className, {
                        disabled: disabled,
                    })}
                    ref={inputRef}
                    value={inputValue}
                    onChange={handleInputChange}
                    onKeyDown={onKeyDown}
                    onFocus={handleFocus}
                    onBlur={handleBlur}
                    placeholder={placeholder}
                    maxLength={maxLength}
                    autoFocus={autoFocus}
                    disabled={disabled}
                />
            );
        }
    )
);

export { Input, InputGroup, InputLeftElement, InputRightElement };
export type { InputGroupProps, InputLeftElementProps, InputProps, InputRightElementProps };