import { useEffect, useState, useRef } from 'react';

/**
 * Hook that debounces a value, delaying updates until after a specified delay.
 *
 * Useful for delaying API calls, search queries, or other expensive operations
 * until the user has stopped typing or interacting.
 *
 * @param value - The value to debounce
 * @param delayMs - Delay in milliseconds (default: 500)
 * @returns The debounced value
 *
 * @example
 * ```tsx
 * function SearchInput() {
 *   const [query, setQuery] = useState('');
 *   const debouncedQuery = useDebounce(query, 300);
 *
 *   // This effect will only run when debouncedQuery changes
 *   useEffect(() => {
 *     if (debouncedQuery) {
 *       performSearch(debouncedQuery);
 *     }
 *   }, [debouncedQuery]);
 *
 *   return (
 *     <input
 *       value={query}
 *       onChange={(e) => setQuery(e.target.value)}
 *       placeholder="Search..."
 *     />
 *   );
 * }
 * ```
 */
export function useDebounce<T>(value: T, delayMs: number = 500): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    // Clear existing timeout
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }

    // Set new timeout
    timeoutRef.current = setTimeout(() => {
      setDebouncedValue(value);
    }, delayMs);

    // Cleanup function
    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, [value, delayMs]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, []);

  return debouncedValue;
}

/**
 * Alternative debounce hook that also provides immediate control.
 *
 * Returns both the debounced value and a function to immediately update it,
 * useful when you need to bypass the debounce in certain cases.
 *
 * @param value - The value to debounce
 * @param delayMs - Delay in milliseconds (default: 500)
 * @returns Object with debouncedValue and setImmediate function
 *
 * @example
 * ```tsx
 * function AutoSaveForm() {
 *   const [formData, setFormData] = useState({ title: '', content: '' });
 *   const { debouncedValue: debouncedFormData, setImmediate } = useDebounceWithImmediate(formData, 1000);
 *
 *   // Auto-save after 1 second of no changes
 *   useEffect(() => {
 *     saveFormData(debouncedFormData);
 *   }, [debouncedFormData]);
 *
 *   const handleSubmit = () => {
 *     // Immediately save without waiting for debounce
 *     setImmediate(formData);
 *     submitForm(formData);
 *   };
 *
 *   return (
 *     <form onSubmit={handleSubmit}>
 *       <input
 *         value={formData.title}
 *         onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
 *       />
 *       <button type="submit">Submit</button>
 *     </form>
 *   );
 * }
 * ```
 */
export function useDebounceWithImmediate<T>(value: T, delayMs: number = 500) {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    // Clear existing timeout
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }

    // Set new timeout
    timeoutRef.current = setTimeout(() => {
      setDebouncedValue(value);
    }, delayMs);

    // Cleanup function
    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, [value, delayMs]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, []);

  const setImmediate = (newValue: T) => {
    // Clear pending timeout
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
      timeoutRef.current = null;
    }

    // Immediately update debounced value
    setDebouncedValue(newValue);
  };

  return {
    debouncedValue,
    setImmediate,
  };
}