/**
 * Media generation types and utilities.
 *
 * IMPORTANT: For generation, use @persona-community-5/realtime's useMediaGeneration hook.
 * That hook implements the correct async pattern:
 * 1. POST returns job ID immediately
 * 2. Progress and results come via SSE events
 * 3. Never blocks waiting for generation
 *
 * @example
 * ```tsx
 * import { useMediaGeneration } from '@persona-community-5/realtime';
 *
 * const { status, progress, result, generate } = useMediaGeneration({
 *   endpoint: '/api/generate/video',
 *   userId: user.id,
 * });
 *
 * await generate({ prompt: 'A cat playing piano' });
 * // Results arrive via SSE - no blocking
 * ```
 *
 * The synchronous functions in this file are DEPRECATED.
 * Only use them for simple image generation where blocking is acceptable.
 */

import { AIResponseMeta, createAIError, ErrorCodes } from './types';

/** Image size presets. */
export type ImageSize = '256x256' | '512x512' | '1024x1024' | '1024x1792' | '1792x1024';

/** Aspect ratio presets. */
export type AspectRatio = '1:1' | '16:9' | '9:16' | '4:3' | '3:4';

/** Video duration presets. */
export type VideoDuration = '5s' | '10s';

// ---------------------------------------------------------------------------
// Image types
// ---------------------------------------------------------------------------

/** Request for image generation. */
export interface ImageGenRequest {
  prompt: string;
  model?: string;
  size?: ImageSize;
  aspectRatio?: AspectRatio;
  count?: number;
  negativePrompt?: string;
  style?: string;
}

/** A single generated image. */
export interface GeneratedImage {
  /** Base64-encoded image data or URL. */
  data: string;
  /** Whether data is a URL (true) or base64 (false). */
  isUrl: boolean;
  /** Provider that generated this image. */
  provider: string;
  /** Generation seed if available. */
  seed?: number;
  /** Revised prompt if the provider modified it. */
  revisedPrompt?: string;
}

/** Response from image generation. */
export interface ImageGenResponse extends AIResponseMeta {
  images: GeneratedImage[];
}

// ---------------------------------------------------------------------------
// Video types
// ---------------------------------------------------------------------------

/** Request for video generation. */
export interface VideoGenRequest {
  prompt: string;
  model?: string;
  aspectRatio?: AspectRatio;
  duration?: VideoDuration;
}

/** A single generated video. */
export interface GeneratedVideo {
  /** Base64-encoded video data or URL. */
  data: string;
  /** Whether data is a URL (true) or base64 (false). */
  isUrl: boolean;
  /** MIME type (e.g. "video/mp4"). */
  mimeType: string;
  /** Provider that generated this video. */
  provider: string;
  /** Generation seed if available. */
  seed?: number;
}

/** Response from video generation. */
export interface VideoGenResponse extends AIResponseMeta {
  videos: GeneratedVideo[];
}

// ---------------------------------------------------------------------------
// Shared types
// ---------------------------------------------------------------------------

/** Progress update during generation. */
export interface GenerationProgress {
  percent: number;
  stage: string;
}

/** Options for API calls. */
export interface MediaGenOptions {
  timeout?: number;
  headers?: Record<string, string>;
}

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

/** Parse image array from backend response. */
function parseImages(data: Record<string, unknown>): GeneratedImage[] {
  const images = data.images as Record<string, unknown>[] | undefined;
  if (!images) return [];
  return images.map((img) => ({
    data: (img.data as string) || (img.url as string) || '',
    isUrl: !!img.isUrl || !!img.url,
    provider: (img.provider as string) || (data.provider as string) || '',
    seed: img.seed as number | undefined,
    revisedPrompt: img.revised_prompt as string | undefined,
  }));
}

/** Parse video array from backend response. */
function parseVideos(data: Record<string, unknown>): GeneratedVideo[] {
  const videos = data.videos as Record<string, unknown>[] | undefined;
  if (!videos) return [];
  return videos.map((v) => ({
    data: (v.data as string) || (v.url as string) || '',
    isUrl: !!v.isUrl || !!v.url,
    mimeType: (v.mimeType as string) || 'video/mp4',
    provider: (v.provider as string) || (data.provider as string) || '',
    seed: v.seed as number | undefined,
  }));
}

/** Handle HTTP errors from generation endpoints. */
function handleHttpError(status: number, errorData: Record<string, unknown>): never {
  throw createAIError(
    (errorData.message as string) || `HTTP ${status}`,
    status === 429 ? ErrorCodes.RATE_LIMITED : ErrorCodes.PROVIDER_ERROR,
    undefined,
    status === 429 || status >= 500,
  );
}

/**
 * Simulate progress ticks during a synchronous fetch.
 * Returns a cleanup function.
 */
function simulateProgress(
  onProgress: (p: GenerationProgress) => void,
  stage: string,
  maxPercent: number,
  intervalMs: number,
): () => void {
  let current = 10;
  const id = setInterval(() => {
    if (current < maxPercent) {
      current += Math.random() * 8 + 2;
      current = Math.min(current, maxPercent);
      onProgress({ percent: Math.round(current), stage });
    }
  }, intervalMs);
  return () => clearInterval(id);
}

// ---------------------------------------------------------------------------
// Image generation
// ---------------------------------------------------------------------------

/**
 * Generate images from a text prompt.
 *
 * @param endpoint - The image generation API endpoint URL
 * @param request - The generation request
 * @param options - Additional options
 * @returns Promise resolving to generated images
 *
 * @example
 * ```ts
 * const response = await generateImage('/api/my-api/generate/image', {
 *   prompt: 'A sunset over mountains',
 *   count: 2,
 * });
 * ```
 */
export async function generateImage(
  endpoint: string,
  request: ImageGenRequest,
  options: MediaGenOptions = {},
): Promise<ImageGenResponse> {
  const startTime = Date.now();

  const controller = new AbortController();
  const timeoutId = options.timeout
    ? setTimeout(() => controller.abort(), options.timeout)
    : undefined;

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

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      handleHttpError(response.status, errorData);
    }

    const data = await response.json();

    return {
      images: parseImages(data),
      provider: (data.provider as string) || '',
      model: data.model as string | undefined,
      latencyMs: Date.now() - startTime,
    };
  } catch (error) {
    if (error instanceof Error && error.name === 'AbortError') {
      throw createAIError('Request timed out', ErrorCodes.TIMEOUT, undefined, true);
    }
    throw error;
  } finally {
    if (timeoutId) clearTimeout(timeoutId);
  }
}

/**
 * Generate images with progress feedback.
 *
 * The backend generates images synchronously. Progress is simulated
 * to give the user visual feedback while waiting.
 *
 * @param endpoint - The image generation API endpoint URL
 * @param request - The generation request
 * @param onProgress - Callback for progress updates
 * @param options - Additional options
 * @returns Promise resolving to generated images
 *
 * @example
 * ```ts
 * const response = await generateImageWithProgress(
 *   '/api/my-api/generate/image',
 *   { prompt: 'A detailed fantasy landscape', count: 4 },
 *   (progress) => setProgress(progress.percent),
 * );
 * ```
 */
export async function generateImageWithProgress(
  endpoint: string,
  request: ImageGenRequest,
  onProgress: (progress: GenerationProgress) => void,
  options: MediaGenOptions = {},
): Promise<ImageGenResponse> {
  onProgress({ percent: 0, stage: 'Starting generation...' });

  const stopSimulation = simulateProgress(onProgress, 'Generating images...', 85, 800);

  try {
    const result = await generateImage(endpoint, request, options);
    onProgress({ percent: 100, stage: 'Complete' });
    return result;
  } catch (error) {
    onProgress({ percent: 0, stage: 'Failed' });
    throw error;
  } finally {
    stopSimulation();
  }
}

// ---------------------------------------------------------------------------
// Video generation
// ---------------------------------------------------------------------------

/**
 * @deprecated Use useMediaGeneration from @persona-community-5/realtime instead.
 * Video generation takes 2-5 minutes. Blocking HTTP requests is wrong.
 *
 * Correct pattern:
 * ```tsx
 * import { useMediaGeneration } from '@persona-community-5/realtime';
 * const { generate, progress, result } = useMediaGeneration({
 *   endpoint: '/api/generate/video',
 *   userId: user.id,
 * });
 * await generate({ prompt: '...' }); // Returns immediately
 * // Result arrives via SSE
 * ```
 */
export async function generateVideo(
  endpoint: string,
  request: VideoGenRequest,
  options: MediaGenOptions = {},
): Promise<VideoGenResponse> {
  const startTime = Date.now();

  const controller = new AbortController();
  // Video generation can take minutes; default timeout is 5 min
  const timeout = options.timeout || 300_000;
  const timeoutId = setTimeout(() => controller.abort(), timeout);

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

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      handleHttpError(response.status, errorData);
    }

    const data = await response.json();

    return {
      videos: parseVideos(data),
      provider: (data.provider as string) || '',
      model: data.model as string | undefined,
      latencyMs: Date.now() - startTime,
    };
  } catch (error) {
    if (error instanceof Error && error.name === 'AbortError') {
      throw createAIError('Video generation timed out', ErrorCodes.TIMEOUT, undefined, true);
    }
    throw error;
  } finally {
    clearTimeout(timeoutId);
  }
}

/**
 * @deprecated Use useMediaGeneration from @persona-community-5/realtime instead.
 * This function uses FAKE simulated progress. Real progress comes from SSE events.
 *
 * Correct pattern:
 * ```tsx
 * import { useMediaGeneration } from '@persona-community-5/realtime';
 * const { generate, progress, result } = useMediaGeneration({
 *   endpoint: '/api/generate/video',
 *   userId: user.id,
 * });
 * await generate({ prompt: '...' });
 * // progress updates automatically from real SSE events
 * ```
 */
export async function generateVideoWithProgress(
  endpoint: string,
  request: VideoGenRequest,
  onProgress: (progress: GenerationProgress) => void,
  options: MediaGenOptions = {},
): Promise<VideoGenResponse> {
  onProgress({ percent: 0, stage: 'Starting video generation...' });

  // Video takes longer; simulate progress more slowly
  const stopSimulation = simulateProgress(onProgress, 'Rendering video...', 80, 2000);

  try {
    const result = await generateVideo(endpoint, request, options);
    onProgress({ percent: 100, stage: 'Complete' });
    return result;
  } catch (error) {
    onProgress({ percent: 0, stage: 'Failed' });
    throw error;
  } finally {
    stopSimulation();
  }
}
