'use client';

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

/**
 * Event from SSE channel.
 */
export interface ChannelEvent {
  type: string;
  timestamp: string;
  jobId?: string;
  progress?: number;
  message?: string;
  result?: unknown;
  error?: string;
  [key: string]: unknown;
}

/**
 * Connection state for SSE.
 */
export type SSEState = 'connecting' | 'connected' | 'disconnected' | 'error';

/**
 * Configuration for useEventChannel hook.
 */
export interface UseEventChannelConfig {
  /** SSE endpoint URL (e.g., '/api/events') */
  endpoint: string;
  /** Channel to subscribe to (e.g., 'user:123') */
  channel: string;
  /** Handler for incoming events */
  onEvent?: (event: ChannelEvent) => void;
  /** Handler for connection state changes */
  onStateChange?: (state: SSEState) => void;
  /** Auto-reconnect on disconnect (default: true) */
  autoReconnect?: boolean;
  /** Reconnect delay in ms (default: 3000) */
  reconnectDelay?: number;
}

/**
 * Hook for subscribing to SSE event channels.
 *
 * Connects to an SSE endpoint and receives events for a specific channel.
 * Automatically reconnects on disconnect.
 *
 * @example
 * ```tsx
 * const { state } = useEventChannel({
 *   endpoint: '/api/events',
 *   channel: `user:${userId}`,
 *   onEvent: (event) => {
 *     switch (event.type) {
 *       case 'generation_complete':
 *         setResult(event.result);
 *         break;
 *       case 'generation_progress':
 *         setProgress(event.progress);
 *         break;
 *     }
 *   },
 * });
 * ```
 */
export function useEventChannel(config: UseEventChannelConfig) {
  const {
    endpoint,
    channel,
    onEvent,
    onStateChange,
    autoReconnect = true,
    reconnectDelay = 3000,
  } = config;

  const [state, setState] = useState<SSEState>('disconnected');
  const eventSourceRef = useRef<EventSource | null>(null);
  const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout>>();

  // Refs for callbacks to avoid stale closures
  const onEventRef = useRef(onEvent);
  const onStateChangeRef = useRef(onStateChange);

  useEffect(() => {
    onEventRef.current = onEvent;
    onStateChangeRef.current = onStateChange;
  });

  const updateState = useCallback((newState: SSEState) => {
    setState(newState);
    onStateChangeRef.current?.(newState);
  }, []);

  const connect = useCallback(() => {
    // Close existing connection
    if (eventSourceRef.current) {
      eventSourceRef.current.close();
    }

    // Build URL with channel parameter
    const url = new URL(endpoint, window.location.origin);
    url.searchParams.set('channel', channel);

    updateState('connecting');

    const eventSource = new EventSource(url.toString());
    eventSourceRef.current = eventSource;

    eventSource.onopen = () => {
      updateState('connected');
    };

    eventSource.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data) as ChannelEvent;
        onEventRef.current?.(data);
      } catch {
        console.error('[SSE] Failed to parse event:', event.data);
      }
    };

    eventSource.onerror = () => {
      updateState('error');
      eventSource.close();
      eventSourceRef.current = null;

      // Reconnect if enabled
      if (autoReconnect) {
        reconnectTimeoutRef.current = setTimeout(() => {
          connect();
        }, reconnectDelay);
      }
    };
  }, [endpoint, channel, autoReconnect, reconnectDelay, updateState]);

  const disconnect = useCallback(() => {
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
    }
    if (eventSourceRef.current) {
      eventSourceRef.current.close();
      eventSourceRef.current = null;
    }
    updateState('disconnected');
  }, [updateState]);

  // Connect on mount, disconnect on unmount
  useEffect(() => {
    connect();

    return () => {
      disconnect();
    };
  }, [connect, disconnect]);

  return {
    /** Current connection state */
    state,
    /** Manually reconnect */
    reconnect: connect,
    /** Manually disconnect */
    disconnect,
  };
}
