/**
 * useToast Hook
 *
 * A composable hook for showing toast notifications.
 * Supports success, error, warning, and info variants with auto-dismiss.
 *
 * This hook requires the ToastProvider to be mounted in your app.
 *
 * @example
 * ```tsx
 * // In your app root:
 * import { ToastProvider } from '@/prism/headless';
 *
 * function App() {
 *   return (
 *     <ToastProvider>
 *       <YourApp />
 *     </ToastProvider>
 *   );
 * }
 *
 * // In your component:
 * import { useToast } from '@/prism/headless';
 *
 * function SaveButton() {
 *   const toast = useToast();
 *
 *   const handleSave = async () => {
 *     try {
 *       await saveData();
 *       toast.success('Changes saved successfully');
 *     } catch (error) {
 *       toast.error({
 *         title: 'Failed to save',
 *         description: error.message,
 *         action: {
 *           label: 'Retry',
 *           onClick: handleSave,
 *         },
 *       });
 *     }
 *   };
 *
 *   return <button onClick={handleSave}>Save</button>;
 * }
 * ```
 *
 * ⚠️ AUTO-GENERATED BY PRISM - DO NOT EDIT
 */

'use client';

import React, {
  createContext,
  useContext,
  useState,
  useCallback,
  useRef,
  useEffect,
  type ReactNode,
} from 'react';
import type {
  ToastType,
  ToastEntry,
  ToastOptions,
  ToastState,
  ToastActions,
  UseToastResult,
} from '../types';

// ============================================================================
// Utilities
// ============================================================================

let toastIdCounter = 0;

function generateId(): string {
  return `toast-${++toastIdCounter}-${Date.now()}`;
}

function normalizeOptions(titleOrOptions: string | ToastOptions): ToastOptions {
  if (typeof titleOrOptions === 'string') {
    return { title: titleOrOptions };
  }
  return titleOrOptions;
}

// ============================================================================
// Context
// ============================================================================

interface ToastContextValue extends ToastState, ToastActions {}

const ToastContext = createContext<ToastContextValue | null>(null);

// ============================================================================
// Provider
// ============================================================================

interface ToastProviderProps {
  children: ReactNode;
  /** Default duration for toasts in milliseconds (default: 5000) */
  defaultDuration?: number;
  /** Maximum number of toasts to show at once (default: 5) */
  maxToasts?: number;
  /**
   * Custom toast container component. If not provided, you must render your own
   * container using useToastState().
   */
  ContainerComponent?: React.ComponentType<ToastContainerProps>;
}

export interface ToastContainerProps {
  toasts: ToastEntry[];
  onDismiss: (id: string) => void;
}

/**
 * Provider for toast notifications.
 * Must be mounted at the root of your app.
 */
export function ToastProvider({
  children,
  defaultDuration = 5000,
  maxToasts = 5,
  ContainerComponent,
}: ToastProviderProps): JSX.Element {
  const [toasts, setToasts] = useState<ToastEntry[]>([]);
  const timersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());

  // Cleanup timers on unmount
  useEffect(() => {
    return () => {
      timersRef.current.forEach((timer) => clearTimeout(timer));
      timersRef.current.clear();
    };
  }, []);

  const dismiss = useCallback((id: string) => {
    // Clear timer if exists
    const timer = timersRef.current.get(id);
    if (timer) {
      clearTimeout(timer);
      timersRef.current.delete(id);
    }

    setToasts((prev) => prev.filter((t) => t.id !== id));
  }, []);

  const dismissAll = useCallback(() => {
    // Clear all timers
    timersRef.current.forEach((timer) => clearTimeout(timer));
    timersRef.current.clear();

    setToasts([]);
  }, []);

  const addToast = useCallback(
    (type: ToastType, options: ToastOptions): string => {
      const id = generateId();
      const duration = options.duration ?? defaultDuration;
      const dismissible = options.dismissible ?? true;

      const entry: ToastEntry = {
        id,
        type,
        title: options.title,
        description: options.description,
        duration,
        dismissible,
        action: options.action,
      };

      setToasts((prev) => {
        // Remove oldest toasts if we're at max
        let newToasts = [...prev, entry];
        if (newToasts.length > maxToasts) {
          const toRemove = newToasts.slice(0, newToasts.length - maxToasts);
          toRemove.forEach((t) => {
            const timer = timersRef.current.get(t.id);
            if (timer) {
              clearTimeout(timer);
              timersRef.current.delete(t.id);
            }
          });
          newToasts = newToasts.slice(-maxToasts);
        }
        return newToasts;
      });

      // Set auto-dismiss timer if duration > 0
      if (duration > 0) {
        const timer = setTimeout(() => {
          dismiss(id);
        }, duration);
        timersRef.current.set(id, timer);
      }

      return id;
    },
    [defaultDuration, maxToasts, dismiss]
  );

  const success = useCallback(
    (titleOrOptions: string | ToastOptions): string => {
      return addToast('success', normalizeOptions(titleOrOptions));
    },
    [addToast]
  );

  const error = useCallback(
    (titleOrOptions: string | ToastOptions): string => {
      return addToast('error', normalizeOptions(titleOrOptions));
    },
    [addToast]
  );

  const warning = useCallback(
    (titleOrOptions: string | ToastOptions): string => {
      return addToast('warning', normalizeOptions(titleOrOptions));
    },
    [addToast]
  );

  const info = useCallback(
    (titleOrOptions: string | ToastOptions): string => {
      return addToast('info', normalizeOptions(titleOrOptions));
    },
    [addToast]
  );

  const value: ToastContextValue = {
    toasts,
    success,
    error,
    warning,
    info,
    dismiss,
    dismissAll,
  };

  return (
    <ToastContext.Provider value={value}>
      {children}
      {ContainerComponent && (
        <ContainerComponent toasts={toasts} onDismiss={dismiss} />
      )}
    </ToastContext.Provider>
  );
}

// ============================================================================
// Hook
// ============================================================================

/**
 * Hook for showing toast notifications.
 * Must be used within a ToastProvider.
 *
 * @returns Toast state and actions
 */
export function useToast(): UseToastResult {
  const context = useContext(ToastContext);

  if (!context) {
    throw new Error('useToast must be used within a ToastProvider');
  }

  return context;
}

/**
 * Hook for accessing the toast state (for building custom toast containers).
 * Must be used within a ToastProvider.
 *
 * @returns Toast context value
 */
export function useToastState(): ToastContextValue {
  const context = useContext(ToastContext);

  if (!context) {
    throw new Error('useToastState must be used within a ToastProvider');
  }

  return context;
}

export default useToast;
