'use client';

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

/**
 * Persona generation state values.
 */
export type PersonaGenerationState =
  | 'idle'
  | 'spec_generating'    // LLM pipeline running (persona_spec_started received)
  | 'images_generating'  // Images being generated (persona_spec_complete received)
  | 'videos_generating'  // Videos being generated (persona_image_complete received)
  | 'complete'           // All done (all 4 motion types resolved)
  | 'failed';            // persona_failed received

/**
 * Result of the usePersonaGeneration hook.
 */
export interface UsePersonaGenerationResult {
  state: PersonaGenerationState;
  jobId: string | null;
  personaId: string | null;
  /** 0–100 accumulated from persona_image_progress.progress */
  imageProgress: number;
  /** URLs accumulated from persona_image_progress result.url */
  imageUrls: string[];
  /** motionType → url for completed videos */
  videoUrls: Record<string, string>;
  /** motionTypes that failed (non-fatal) */
  videoFailures: string[];
  error: string | null;
  generate: (description: string, gender: string, name?: string) => Promise<void>;
  reset: () => void;
}

/**
 * Configuration for the usePersonaGeneration hook.
 */
export interface UsePersonaGenerationConfig {
  /** API base path, e.g. '/api/example-api' */
  apiBase: string;
  /** User ID for subscribing to user channel */
  userId: string;
  /** Auth token for API requests */
  authToken?: string;
  /** SSE endpoint (default: '{apiBase}/events') */
  sseEndpoint?: string;
}

// Total motion types that the backend generates (keep in sync with Go VideoSpec).
const TOTAL_MOTION_TYPES = 4;

/**
 * Hook for managing persona generation state with real-time progress via SSE.
 *
 * Calls POST {apiBase}/persona/generate, then listens on the user SSE channel
 * for the 9-event sequence and accumulates image URLs and video URLs in real time.
 *
 * @example
 * ```tsx
 * const { state, imageUrls, videoUrls, generate, reset } = usePersonaGeneration({
 *   apiBase: '/api/example-api',
 *   userId: currentUser.id,
 *   authToken: token,
 * });
 *
 * return (
 *   <button onClick={() => generate('athletic woman, 30s', 'female', 'Aria')}>
 *     Generate Persona
 *   </button>
 * );
 * ```
 */
export function usePersonaGeneration(
  config: UsePersonaGenerationConfig
): UsePersonaGenerationResult {
  const { apiBase, userId, authToken, sseEndpoint } = config;

  const [state, setState] = useState<PersonaGenerationState>('idle');
  const [jobId, setJobId] = useState<string | null>(null);
  const [personaId, setPersonaId] = useState<string | null>(null);
  const [imageProgress, setImageProgress] = useState(0);
  const [imageUrls, setImageUrls] = useState<string[]>([]);
  const [videoUrls, setVideoUrls] = useState<Record<string, string>>({});
  const [videoFailures, setVideoFailures] = useState<string[]>([]);
  const [error, setError] = useState<string | null>(null);

  // Refs for event handler closures (avoids stale state).
  const jobIdRef = useRef<string | null>(null);
  jobIdRef.current = jobId;

  const videoUrlsRef = useRef<Record<string, string>>({});
  videoUrlsRef.current = videoUrls;

  const videoFailuresRef = useRef<string[]>([]);
  videoFailuresRef.current = videoFailures;

  const effectiveSseEndpoint = sseEndpoint ?? `${apiBase}/events`;

  const headers = useCallback((): Record<string, string> => {
    const h: Record<string, string> = { 'Content-Type': 'application/json' };
    if (authToken) h['Authorization'] = `Bearer ${authToken}`;
    return h;
  }, [authToken]);

  const handleEvent = useCallback((event: ChannelEvent) => {
    const currentJobId = jobIdRef.current;
    if (!currentJobId) return;

    // Filter events to the active job.
    const result = event.result as Record<string, unknown> | undefined;
    const eventJobId = (event as unknown as Record<string, unknown>)['jobId'] as string | undefined;
    if (eventJobId && eventJobId !== currentJobId) return;

    switch (event.type) {
      case 'persona_spec_started': {
        setState('spec_generating');
        break;
      }

      case 'persona_spec_complete': {
        const pid = result?.['personaId'] as string | undefined;
        if (pid) setPersonaId(pid);
        setState('images_generating');
        break;
      }

      case 'persona_image_started': {
        // No state change needed; images_generating already set.
        break;
      }

      case 'persona_image_progress': {
        const progress = (event as unknown as Record<string, unknown>)['progress'] as number | undefined;
        const url = result?.['url'] as string | undefined;
        if (typeof progress === 'number') setImageProgress(progress);
        if (url) setImageUrls((prev: string[]) => [...prev, url]);
        break;
      }

      case 'persona_image_complete': {
        setImageProgress(100);
        setState('videos_generating');
        break;
      }

      case 'persona_video_started': {
        // No state change needed; videos_generating already set.
        break;
      }

      case 'persona_video_complete': {
        const motionType = result?.['motionType'] as string | undefined;
        const url = result?.['url'] as string | undefined;
        if (!motionType || !url) break;
        setVideoUrls((prev: Record<string, string>) => {
          const next = { ...prev, [motionType]: url };
          // Check if all motion types have resolved.
          const resolved = Object.keys(next).length + videoFailuresRef.current.length;
          if (resolved >= TOTAL_MOTION_TYPES) setState('complete');
          return next;
        });
        break;
      }

      case 'persona_video_failed': {
        const motionType = result?.['motionType'] as string | undefined;
        if (!motionType) break;
        setVideoFailures((prev: string[]) => {
          const next = [...prev, motionType];
          // Check if all motion types have resolved.
          const resolved = Object.keys(videoUrlsRef.current).length + next.length;
          if (resolved >= TOTAL_MOTION_TYPES) setState('complete');
          return next;
        });
        break;
      }

      case 'persona_failed': {
        const errMsg = result?.['error'] as string | undefined;
        setError(errMsg ?? 'Persona generation failed');
        setState('failed');
        break;
      }
    }
  }, []);

  // Subscribe to the user SSE channel.
  useEventChannel({
    endpoint: effectiveSseEndpoint,
    channel: `user:${userId}`,
    onEvent: handleEvent,
  });

  const generate = useCallback(
    async (description: string, gender: string, name?: string) => {
      // Guard against double-triggering.
      if (state !== 'idle') return;

      setError(null);
      setState('spec_generating');

      try {
        const body: Record<string, string> = { description, gender };
        if (name) body['name'] = name;

        const res = await fetch(`${apiBase}/persona/generate`, {
          method: 'POST',
          headers: headers(),
          body: JSON.stringify(body),
        });

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

        const json = (await res.json()) as { jobId?: string; job_id?: string };
        const id = json.jobId ?? json.job_id ?? null;
        setJobId(id);
        jobIdRef.current = id;
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Failed to start persona generation');
        setState('idle');
      }
    },
    [state, apiBase, headers]
  );

  const reset = useCallback(() => {
    setState('idle');
    setJobId(null);
    jobIdRef.current = null;
    setPersonaId(null);
    setImageProgress(0);
    setImageUrls([]);
    setVideoUrls({});
    videoUrlsRef.current = {};
    setVideoFailures([]);
    videoFailuresRef.current = [];
    setError(null);
  }, []);

  return {
    state,
    jobId,
    personaId,
    imageProgress,
    imageUrls,
    videoUrls,
    videoFailures,
    error,
    generate,
    reset,
  };
}
