import {
	forwardRef,
	type Ref,
	type TextareaHTMLAttributes,
	useEffect,
	useImperativeHandle,
	useRef,
	useState,
} from "react";
import { cn } from "~/lib/utils";

interface UseAutosizeTextAreaProps {
	textAreaRef: HTMLTextAreaElement | null;
	minHeight?: number;
	maxHeight?: number;
	triggerAutoSize: string;
}

const useAutosizeTextArea = ({
	textAreaRef,
	triggerAutoSize,
	maxHeight = Number.MAX_SAFE_INTEGER,
	minHeight = 0,
}: UseAutosizeTextAreaProps) => {
	const [init, setInit] = useState(true);
	useEffect(() => {
		// We need to reset the height momentarily to get the correct scrollHeight for the textarea
		const offsetBorder = 2;
		if (textAreaRef) {
			if (init) {
				textAreaRef.style.minHeight = `${minHeight + offsetBorder}px`;
				if (maxHeight > minHeight) {
					textAreaRef.style.maxHeight = `${maxHeight}px`;
				}
				setInit(false);
			}
			textAreaRef.style.height = `${minHeight + offsetBorder}px`;
			const scrollHeight = textAreaRef.scrollHeight;
			// We then set the height directly, outside of the render loop
			// Trying to set this with state or a ref will product an incorrect value.
			if (scrollHeight > maxHeight) {
				textAreaRef.style.height = `${maxHeight}px`;
			} else {
				textAreaRef.style.height = `${scrollHeight + offsetBorder}px`;
			}
		}
	}, [textAreaRef, triggerAutoSize]);
};

export type AutosizeTextAreaRef = {
	textArea: HTMLTextAreaElement;
	maxHeight: number;
	minHeight: number;
};

type AutosizeTextAreaProps = {
	maxHeight?: number;
	minHeight?: number;
} & TextareaHTMLAttributes<HTMLTextAreaElement>;

export const AutosizeTextarea = forwardRef<
	AutosizeTextAreaRef,
	AutosizeTextAreaProps
>(
	(
		{
			maxHeight = Number.MAX_SAFE_INTEGER,
			minHeight = 56,
			className,
			onChange,
			value,
			...props
		}: AutosizeTextAreaProps,
		ref: Ref<AutosizeTextAreaRef>,
	) => {
		const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
		const [triggerAutoSize, setTriggerAutoSize] = useState("");

		useAutosizeTextArea({
			maxHeight,
			minHeight,
			textAreaRef: textAreaRef.current,
			triggerAutoSize: triggerAutoSize,
		});

		useImperativeHandle(ref, () => ({
			focus: () => textAreaRef.current?.focus(),
			maxHeight,
			minHeight,
			// biome-ignore lint/style/noNonNullAssertion: <it exists>
			textArea: textAreaRef.current!,
		}));

		useEffect(() => {
			if (value || props?.defaultValue) {
				setTriggerAutoSize(value as string);
			}
		}, [value || props?.defaultValue]);

		return (
			<textarea
				{...props}
				className={cn(
					"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
					className,
				)}
				onChange={(e) => {
					setTriggerAutoSize(e.target.value);
					onChange?.(e);
				}}
				ref={textAreaRef}
				value={value}
			/>
		);
	},
);
AutosizeTextarea.displayName = "AutosizeTextarea";
