'use client';

import { useState, useCallback, useRef } from 'react';
import { useEventChannel, type ChannelEvent, type SSEState } from './useEventChannel';

/**
 * Generation status.
 */
export type GenerationStatus = 'idle' | 'pending' | 'generating' | 'complete' | 'failed';

/**
 * Configuration for media generation hook.
 */
export interface UseMediaGenerationConfig {
  /** API endpoint for starting the generation job */
  endpoint: string;
  /** SSE endpoint for events (default: '/api/events') */
  sseEndpoint?: string;
  /** User ID for subscribing to user channel */
  userId: string;
  /** Additional headers for the POST request */
  headers?: Record<string, string>;
}

/**
 * Result from the media generation hook.
 */
export interface UseMediaGenerationResult<T> {
  /** Current generation status */
  status: GenerationStatus;
  /** SSE connection state */
  sseState: SSEState;
  /** Progress percentage (0-100) */
  progress: number;
  /** Current stage message */
  message: string;
  /** Generated result (available when status is 'complete') */
  result: T | null;
  /** Error message (available when status is 'failed') */
  error: string | null;
  /** Job ID of the current generation */
  jobId: string | null;
  /** Start a new generation */
  generate: (request: Record<string, unknown>) => Promise<void>;
  /** Reset state for a new generation */
  reset: () => void;
}

/**
 * Hook for async media generation with real-time progress via SSE events.
 *
 * This hook implements the correct async pattern:
 * 1. POST to endpoint returns job ID immediately
 * 2. Progress and results come via SSE events
 * 3. Never blocks waiting for generation
 *
 * @example
 * ```tsx
 * const {
 *   status,
 *   progress,
 *   message,
 *   result,
 *   error,
 *   generate,
 * } = useMediaGeneration<VideoResult>({
 *   endpoint: '/api/generate/video',
 *   userId: currentUser.id,
 * });
 *
 * // Start generation
 * await generate({ prompt: 'A cat playing piano', aspectRatio: '16:9' });
 *
 * // UI automatically updates as SSE events arrive
 * return (
 *   <div>
 *     <p>Status: {status}</p>
 *     <p>Progress: {progress}%</p>
 *     <p>{message}</p>
 *     {result && <video src={result.url} />}
 *     {error && <p className="error">{error}</p>}
 *   </div>
 * );
 * ```
 */
export function useMediaGeneration<T = unknown>(
  config: UseMediaGenerationConfig
): UseMediaGenerationResult<T> {
  const {
    endpoint,
    sseEndpoint = '/api/events',
    userId,
    headers,
  } = config;

  const [status, setStatus] = useState<GenerationStatus>('idle');
  const [progress, setProgress] = useState(0);
  const [message, setMessage] = useState('');
  const [result, setResult] = useState<T | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [jobId, setJobId] = useState<string | null>(null);

  const jobIdRef = useRef<string | null>(null);

  // Handle incoming SSE events
  const handleEvent = useCallback((event: ChannelEvent) => {
    // Only handle events for our job
    if (!jobIdRef.current || event.jobId !== jobIdRef.current) return;

    switch (event.type) {
      case 'generation_started':
        setStatus('generating');
        setProgress(0);
        setMessage('Starting generation...');
        break;

      case 'generation_progress':
        setProgress(event.progress ?? 0);
        setMessage(event.message ?? 'Generating...');
        break;

      case 'generation_complete':
        setStatus('complete');
        setProgress(100);
        setMessage('Complete');
        setResult(event.result as T);
        break;

      case 'generation_failed':
        setStatus('failed');
        setError(event.error ?? 'Generation failed');
        break;

      case 'upload_started':
        setStatus('generating');
        setProgress(0);
        setMessage('Starting upload...');
        break;

      case 'upload_progress':
        setProgress(event.progress ?? 0);
        setMessage('Uploading...');
        break;

      case 'upload_complete':
        setStatus('complete');
        setProgress(100);
        setMessage('Complete');
        setResult(event.result as T);
        break;

      case 'upload_failed':
        setStatus('failed');
        setError(event.error ?? 'Upload failed');
        break;
    }
  }, []);

  // Subscribe to SSE events for user channel
  const { state: sseState } = useEventChannel({
    endpoint: sseEndpoint,
    channel: `user:${userId}`,
    onEvent: handleEvent,
  });

  const generate = useCallback(
    async (request: Record<string, unknown>) => {
      // Reset state
      setStatus('pending');
      setProgress(0);
      setMessage('Submitting job...');
      setResult(null);
      setError(null);

      try {
        const response = await fetch(endpoint, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            ...headers,
          },
          body: JSON.stringify(request),
        });

        if (!response.ok) {
          const errorData = await response.json().catch(() => ({}));
          throw new Error(
            (errorData as { message?: string }).message || `HTTP ${response.status}`
          );
        }

        // Handle both envelope-wrapped ({ data: { jobId } }) and direct ({ jobId }) responses
        const json = (await response.json()) as
          | { data: { jobId: string }; meta?: unknown }
          | { jobId: string };
        const resolvedJobId =
          'data' in json && json.data?.jobId
            ? json.data.jobId
            : (json as { jobId: string }).jobId;

        if (!resolvedJobId) {
          throw new Error('No job ID returned from server');
        }

        // Store job ID and transition to generating immediately.
        // The 202 response confirms the job was accepted — don't wait for
        // the generation_started SSE event to leave the 'pending' state.
        jobIdRef.current = resolvedJobId;
        setJobId(resolvedJobId);
        setStatus('generating');
        setMessage('Generation in progress...');
      } catch (err) {
        setStatus('failed');
        setError(err instanceof Error ? err.message : 'Failed to start generation');
      }
    },
    [endpoint, headers]
  );

  const reset = useCallback(() => {
    jobIdRef.current = null;
    setJobId(null);
    setStatus('idle');
    setProgress(0);
    setMessage('');
    setResult(null);
    setError(null);
  }, []);

  return {
    status,
    sseState,
    progress,
    message,
    result,
    error,
    jobId,
    generate,
    reset,
  };
}
