/* eslint-disable promise/prefer-await-to-then */
'use client';

import { type CustomerInput } from '@repo/graphql/types';
import { Input } from '@repo/ui/components/ui/input';
import { Label } from '@repo/ui/components/ui/label';
import { type ChangeEvent, useEffect, useRef, useState } from 'react';
import { useDebouncedCallback } from 'use-debounce';

type ProfileFieldProps = {
  readonly disabled?: boolean;
  readonly fieldName?: keyof CustomerInput;
  readonly id: string;
  readonly label: string;
  readonly onChange?: (value: CustomerInput) => Promise<void> | void;
  readonly readOnly?: boolean;
  readonly value: string;
};

export function DataField({
  disabled = false,
  fieldName,
  id,
  label,
  onChange,
  readOnly,
  value: initialValue,
}: ProfileFieldProps) {
  const [value, setValue] = useState(initialValue);
  const { debouncedCallback, isPending } = useDebouncedOnChangeCallback(onChange, fieldName);
  const inputRef = useFocus(isPending);

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    const newValue = event.target.value;
    setValue(newValue);
    debouncedCallback(newValue);
  };

  return (
    <div className="space-y-2">
      <Label htmlFor={id}>{label}</Label>
      <Input
        className="bg-secondary outline-none focus:ring-0 focus:ring-offset-0"
        disabled={disabled || isPending}
        id={id}
        onChange={handleChange}
        readOnly={readOnly}
        ref={inputRef}
        value={value}
      />
    </div>
  );
}

function useDebouncedOnChangeCallback(
  callback: ((value: CustomerInput) => Promise<void> | void) | undefined,
  fieldName: string | undefined,
) {
  const [isPending, setIsPending] = useState(false);

  const debouncedCallback = useDebouncedCallback((newValue: string) => {
    if (!callback || !fieldName) return;

    setIsPending(true);
    const result = callback({ [fieldName]: newValue });

    if (result instanceof Promise) {
      result.finally(() => setIsPending(false));
    } else {
      setIsPending(false);
    }
  }, 300);

  return {
    debouncedCallback,
    isPending,
  };
}

function useFocus(isPending: boolean) {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [isInitialRender, setIsInitialRender] = useState(true);

  useEffect(() => {
    if (isInitialRender) {
      setIsInitialRender(false);
      return;
    }

    if (inputRef.current && isPending) {
      inputRef.current.focus();
    }
  }, [isInitialRender, isPending]);

  return inputRef;
}
