import OpenAI from 'openai';
import { toFile } from 'openai/uploads';

export const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

export type RecipeAnalysis = {
  ingredients: Array<{ name: string; quantity: string | null; unit: string | null; notes?: string | null }>;
  prep_steps: string[];
  cooking_steps: string[];
};

export async function uploadImageToOpenAI(bytes: Uint8Array, filename = 'image.jpg') {
  const file = await openai.files.create({
    file: await toFile(bytes, filename, { type: 'image/jpeg' }),
    purpose: 'vision',
  });
  return file;
}

export async function analyzeCookingImages(fileIds: string[]): Promise<RecipeAnalysis> {
  const system = `You are a culinary expert. Analyze ONLY the attached video. 
Return STRICT JSON with keys: ingredients, prep_steps, cooking_steps. 
- ingredients: array of { name, quantity (string|null), unit (string|null), notes (string|null) }
- prep_steps: array of strings
- cooking_steps: array of strings
Do not invent details not visible or clearly inferable. If unknown, use null.`;

  const resp = await openai.responses.create({
    model: process.env.OPENAI_MODEL || 'gpt-4o',
    input: [
      {
        role: 'system',
        content: [{ type: 'input_text', text: system }],
      },
      {
        role: 'user',
        content: [
          // Provide multiple thumbnails sampled from the video
          ...fileIds.map((id) => ({ type: 'input_image' as const, image: { file_id: id }, detail: 'high' as const })),
          {
            type: 'input_text',
            text: 'Extract ingredients, prep steps, and cooking steps. Output only JSON. No prose.',
          },
        ],
      },
    ],
    temperature: 0.2,
  });

  // The SDK exposes a helper for text output
  const text = (resp as any).output_text || (resp as any).content?.[0]?.text || '';
  try {
    return JSON.parse(text);
  } catch {
    // Try to salvage JSON if the model added code fences
    const m = text.match(/\{[\s\S]*\}/);
    if (m) {
      return JSON.parse(m[0]);
    }
    throw new Error('Model did not return valid JSON');
  }
}

export async function analyzeCookingThumbnails(images: Uint8Array[], description?: string): Promise<RecipeAnalysis> {
  const system = `You are a culinary expert. Analyze ONLY the attached images (video thumbnails). 
Return STRICT JSON with keys: ingredients, prep_steps, cooking_steps. 
- ingredients: array of { name, quantity (string|null), unit (string|null), notes (string|null) }
- prep_steps: array of strings
- cooking_steps: array of strings
Do not invent details not visible or clearly inferable. If unknown, use null.`;

  const contentImages = images.map((bytes) => ({
    type: 'input_image' as const,
    image_url: `data:image/jpeg;base64,${Buffer.from(bytes).toString('base64')}`,
    detail: 'high' as const,
  }));

  const resp = await openai.responses.create({
    model: process.env.OPENAI_MODEL || 'gpt-4o',
    input: [
      { role: 'system', content: [{ type: 'input_text', text: system }] },
      {
        role: 'user',
        content: [
          ...(description && description.trim()
            ? ([{ type: 'input_text' as const, text: `Caption/Description: ${description}` }] as const)
            : ([] as const)),
          ...contentImages,
          { type: 'input_text', text: 'Extract ingredients, prep steps, and cooking steps. Output only JSON. No prose.' },
        ],
      },
    ],
    temperature: 0.2,
  });

  const text = (resp as any).output_text || (resp as any).content?.[0]?.text || '';
  try {
    return JSON.parse(text);
  } catch {
    const m = text.match(/\{[\s\S]*\}/);
    if (m) return JSON.parse(m[0]);
    throw new Error('Model did not return valid JSON');
  }
}

export async function transcribeAudioBytes(audioBytes: Uint8Array): Promise<string> {
  const model = process.env.OPENAI_TRANSCRIBE_MODEL || 'gpt-4o-transcribe';
  const resp = await (openai as any).audio.transcriptions.create({
    file: await toFile(audioBytes, 'audio.mp3', { type: 'audio/mpeg' }),
    model,
  });
  return (resp as any).text || '';
}

export async function analyzeFromTranscript(transcript: string): Promise<RecipeAnalysis> {
  const system = `You are a culinary expert. Analyze ONLY the provided transcript of a cooking video. \nReturn STRICT JSON with keys: ingredients, prep_steps, cooking_steps. \n- ingredients: array of { name, quantity (string|null), unit (string|null), notes (string|null) }\n- prep_steps: array of strings\n- cooking_steps: array of strings\nDo not invent details not present. If unknown, use null.`;

  const resp = await openai.responses.create({
    model: process.env.OPENAI_MODEL || 'gpt-4o',
    input: [
      { role: 'system', content: [{ type: 'input_text', text: system }] },
      { role: 'user', content: [{ type: 'input_text', text: transcript }] },
    ],
    temperature: 0.2,
  });
  const text = (resp as any).output_text || (resp as any).content?.[0]?.text || '';
  try {
    return JSON.parse(text);
  } catch {
    const m = text.match(/\{[\s\S]*\}/);
    if (m) return JSON.parse(m[0]);
    throw new Error('Model did not return valid JSON');
  }
}

export async function analyzeFromTranscriptAndImages(
  transcript: string,
  images: Uint8Array[],
  description?: string,
): Promise<RecipeAnalysis> {
  const system = `You are a culinary expert. Analyze ONLY the provided transcript and ordered thumbnails from a cooking video.\nReturn STRICT JSON with keys: ingredients, prep_steps, cooking_steps.\n- ingredients: array of { name, quantity (string|null), unit (string|null), notes (string|null) }\n- prep_steps: array of strings\n- cooking_steps: array of strings\nIf unknown, use null. Consider the images in order.`;

  const contentImages = images.map((bytes) => ({
    type: 'input_image' as const,
    image_url: `data:image/jpeg;base64,${Buffer.from(bytes).toString('base64')}`,
    detail: 'high' as const,
  }));

  const resp = await openai.responses.create({
    model: process.env.OPENAI_MODEL || 'gpt-4o',
    input: [
      { role: 'system', content: [{ type: 'input_text', text: system }] },
      {
        role: 'user',
        content: [
          ...(description && description.trim()
            ? ([{ type: 'input_text' as const, text: `Caption/Description: ${description}` }] as const)
            : ([] as const)),
          ...contentImages, // maintain order
          { type: 'input_text', text: transcript },
          { type: 'input_text', text: 'Extract ingredients, prep steps, and cooking steps. Output only JSON. No prose.' },
        ],
      },
    ],
    temperature: 0.2,
  });

  const text = (resp as any).output_text || (resp as any).content?.[0]?.text || '';
  try {
    return JSON.parse(text);
  } catch {
    const m = text.match(/\{[\s\S]*\}/);
    if (m) return JSON.parse(m[0]);
    throw new Error('Model did not return valid JSON');
  }
}

export async function generateRecipeTitle(input: { description?: string; transcript?: string; analysis?: RecipeAnalysis }): Promise<string> {
  const prompt = `Given the following context from a cooking video, generate a concise, appealing recipe title (3-7 words). No emojis. No quotes. Return only the title text.\n\nDescription: ${input.description || ''}\n\nTranscript: ${input.transcript || ''}\n\nIngredients: ${(input.analysis?.ingredients || []).map(i => i.name).join(', ')}`;
  const resp = await openai.responses.create({
    model: process.env.OPENAI_MODEL || 'gpt-4o-mini',
    input: [{ role: 'user', content: [{ type: 'input_text', text: prompt }] }],
    temperature: 0.4,
  });
  const text = (resp as any).output_text || (resp as any).content?.[0]?.text || '';
  return text.trim().replace(/^"|"$/g, '').slice(0, 120);
}


