import OpenAI from 'openai';
import type { IdeaMotriz, Personaje, GuionConductor, Escaleta, PlanoEscaleta, PreEscaleta, CharacterAsset } from '@/types/project';
import {
  getIdeaMotrizGeneratePrompt,
  getIdeaMotrizRefinePrompt,
  getPersonajesGeneratePrompt,
  getPersonajeRefinePrompt,
  getPersonajesImprovePrompt,
  getNewPersonajePrompt,
  getGuionConductorPrompt,
  getRegenerateActosPrompt,
  getEscaletaSegmentoPrompt,
  getImagePromptsPrompt,
  getCharacterAssetPromptsPrompt,
  TPOSE_VARIANTS,
  EMOTION_VARIANTS,
} from '@/lib/prompts';

let openaiClient: OpenAI | null = null;

function getOpenAI(): OpenAI {
  if (!openaiClient) {
    if (!process.env.OPENAI_API_KEY) {
      throw new Error('OPENAI_API_KEY no está configurada. Crea un archivo .env.local con tu API key.');
    }
    openaiClient = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });
  }
  return openaiClient;
}

async function callLLM(systemPrompt: string, userMessage?: string, options?: { temperature?: number; maxTokens?: number }): Promise<string> {
  const messages: { role: 'system' | 'user'; content: string }[] = [
    { role: 'system', content: systemPrompt },
  ];
  if (userMessage) {
    messages.push({ role: 'user', content: userMessage });
  }

  const response = await getOpenAI().chat.completions.create({
    model: 'gpt-4o',
    messages,
    response_format: { type: 'json_object' },
    temperature: options?.temperature ?? 0.8,
    ...(options?.maxTokens ? { max_tokens: options.maxTokens } : {}),
  });

  const content = response.choices[0]?.message?.content;
  if (!content) throw new Error('No se generó respuesta');
  return content;
}

// =====================
// IDEA MOTRIZ
// =====================

export async function generateIdeaMotriz(userInput: string): Promise<IdeaMotriz> {
  const content = await callLLM(getIdeaMotrizGeneratePrompt(), userInput);
  return JSON.parse(content) as IdeaMotriz;
}

export async function refineIdeaMotriz(current: IdeaMotriz, feedback: string): Promise<IdeaMotriz> {
  const content = await callLLM(getIdeaMotrizRefinePrompt(current), feedback, { temperature: 0.7 });
  return JSON.parse(content) as IdeaMotriz;
}

// =====================
// PERSONAJES
// =====================

export async function generatePersonajes(ideaMotriz: IdeaMotriz): Promise<Personaje[]> {
  const content = await callLLM(getPersonajesGeneratePrompt(ideaMotriz));
  const result = JSON.parse(content);
  return result.personajes as Personaje[];
}

export async function refinePersonaje(personaje: Personaje, feedback: string): Promise<Personaje> {
  const content = await callLLM(getPersonajeRefinePrompt(personaje), feedback, { temperature: 0.7 });
  return JSON.parse(content) as Personaje;
}

export async function improvePersonajes(personajes: Personaje[], feedback: string): Promise<Personaje[]> {
  const content = await callLLM(getPersonajesImprovePrompt(personajes), feedback, { temperature: 0.7 });
  const result = JSON.parse(content);
  return result.personajes as Personaje[];
}

export async function generateNewPersonaje(
  datos: { nombre: string; tipo: string; descripcion: string },
  ideaMotriz: IdeaMotriz,
  personajesExistentes: Personaje[]
): Promise<Personaje> {
  const content = await callLLM(getNewPersonajePrompt(datos, ideaMotriz, personajesExistentes));
  const personaje = JSON.parse(content) as Personaje;
  personaje.id = personaje.id || crypto.randomUUID();
  return personaje;
}

// =====================
// GUIÓN CONDUCTOR
// =====================

export async function generateGuionConductor(
  ideaMotriz: IdeaMotriz,
  personajes: Personaje[],
  capituloNumero: number,
  virtudSugerida?: string
): Promise<GuionConductor> {
  const content = await callLLM(getGuionConductorPrompt(ideaMotriz, personajes, capituloNumero, virtudSugerida));
  return JSON.parse(content) as GuionConductor;
}

export async function regenerateGuionActos(
  guionMetadata: {
    titulo: string;
    duracionTotal: string;
    virtudGanada: string;
    nudoDelMundo: string;
    aliado: { nombre: string; descripcion: string };
    gemaGanada: string;
  },
  ideaMotriz: IdeaMotriz,
  personajes: Personaje[],
  capituloNumero: number
): Promise<{ actos: GuionConductor['actos'] }> {
  const content = await callLLM(getRegenerateActosPrompt(guionMetadata, ideaMotriz, personajes, capituloNumero));
  return JSON.parse(content) as { actos: GuionConductor['actos'] };
}

// =====================
// ESCALETA
// =====================

export async function generateEscaleta(
  guion: GuionConductor,
  ideaMotriz: IdeaMotriz,
  personajes: Personaje[],
  preEscaleta: PreEscaleta,
  onProgress?: (current: number, total: number, label: string) => void
): Promise<Escaleta> {
  const allPlanos: PlanoEscaleta[] = [];
  let planoCounter = 1;
  const MAX_PLANOS_PER_CALL = 25;
  const totalSegments = preEscaleta.segmentos.length;

  // Use pre-escaleta segments as the source of truth for plano counts
  for (let segIdx = 0; segIdx < preEscaleta.segmentos.length; segIdx++) {
    const preSeg = preEscaleta.segmentos[segIdx];
    onProgress?.(segIdx, totalSegments, `Segmento ${preSeg.segmentoId} "${preSeg.nombre}"`);

    // Find matching segment in guion actos
    const acto = guion.actos?.find(a => a.numero === preSeg.actoNumero);
    const segmento = acto?.segmentos[preSeg.segmentoIndex];
    if (!segmento) continue;

    const targetPlanos = preSeg.totalPlanos;
    const duracionSeg = preSeg.duracionSegundos;

    if (targetPlanos <= MAX_PLANOS_PER_CALL) {
      const prompt = getEscaletaSegmentoPrompt(
        segmento, preSeg.actoNumero, preSeg.segmentoIndex, guion, ideaMotriz, personajes,
        targetPlanos, duracionSeg
      );
      const content = await callLLM(prompt, undefined, { temperature: 0.7, maxTokens: 16000 });
      const result = JSON.parse(content);
      const planos = (result.planos || []).map((p: any, i: number) => ({
        ...p,
        id: `plano-${String(planoCounter + i).padStart(3, '0')}`,
        tiempoSegundos: p.tiempoSegundos || 5,
      }));
      planoCounter += planos.length;
      allPlanos.push(...planos);
    } else {
      // Split into sub-batches for long segments
      const totalBatches = Math.ceil(targetPlanos / MAX_PLANOS_PER_CALL);
      let remaining = targetPlanos;

      for (let batch = 0; batch < totalBatches; batch++) {
        const planosInBatch = Math.min(MAX_PLANOS_PER_CALL, remaining);
        const startPlano = targetPlanos - remaining + 1;
        const prompt = getEscaletaSegmentoPrompt(
          segmento, preSeg.actoNumero, preSeg.segmentoIndex, guion, ideaMotriz, personajes,
          targetPlanos, duracionSeg,
          { batchIndex: batch, totalBatches, planosInThisBatch: planosInBatch, startPlano }
        );
        const content = await callLLM(prompt, undefined, { temperature: 0.7, maxTokens: 16000 });
        const result = JSON.parse(content);
        const planos = (result.planos || []).map((p: any, i: number) => ({
          ...p,
          id: `plano-${String(planoCounter + i).padStart(3, '0')}`,
          tiempoSegundos: p.tiempoSegundos || 5,
        }));
        planoCounter += planos.length;
        remaining -= planos.length;
        allPlanos.push(...planos);
      }
    }
  }

  return {
    capitulo: guion.titulo,
    virtudGanada: guion.virtudGanada,
    duracionTotalSegundos: allPlanos.reduce((sum, p) => sum + (p.tiempoSegundos || 0), 0),
    planos: allPlanos,
  } as Escaleta;
}

// =====================
// PROMPTS PARA IMÁGENES
// =====================

export async function generateImagePrompts(
  escaleta: Escaleta,
  personajes: Personaje[],
  onProgress?: (current: number, total: number, label: string) => void
): Promise<PlanoEscaleta[]> {
  const systemPrompt = getImagePromptsPrompt(personajes);

  const batchSize = 20;
  const allPlanos: PlanoEscaleta[] = [];
  const planos = escaleta.planos || [];
  const totalBatches = Math.ceil(planos.length / batchSize);

  for (let i = 0; i < planos.length; i += batchSize) {
    const batchNum = Math.floor(i / batchSize);
    onProgress?.(batchNum, totalBatches, `Lote ${batchNum + 1}/${totalBatches} (planos ${i + 1}-${Math.min(i + batchSize, planos.length)})`);

    const batch = planos.slice(i, i + batchSize);
    const content = await callLLM(
      systemPrompt,
      `Genera prompts para estos planos:\n${JSON.stringify(batch, null, 2)}`,
      { temperature: 0.7 }
    );
    const result = JSON.parse(content);
    // Merge LLM output with original plano data to preserve personajesDetalle etc.
    const merged = (result.planos || []).map((llmPlano: any, idx: number) => {
      const original = batch[idx];
      return { ...original, ...llmPlano, personajesDetalle: original?.personajesDetalle || llmPlano.personajesDetalle };
    });
    allPlanos.push(...merged);
  }

  return allPlanos;
}

// =====================
// CHARACTER ASSET PROMPTS (Nano Banana Pro)
// =====================

export async function generateCharacterAssetPrompts(
  personaje: Personaje
): Promise<CharacterAsset[]> {
  const systemPrompt = getCharacterAssetPromptsPrompt(personaje);
  const content = await callLLM(
    systemPrompt,
    `Genera el character sheet completo para ${personaje.nombre} (${personaje.especie}). Incluye 3 T-poses y 6 emociones.`,
    { temperature: 0.7 }
  );
  const result = JSON.parse(content);

  const assets: CharacterAsset[] = (result.assets || []).map((a: any, i: number) => {
    const variantInfo = a.type === 'tpose'
      ? TPOSE_VARIANTS.find(v => v.variant === a.variant)
      : EMOTION_VARIANTS.find(v => v.variant === a.variant);

    return {
      id: `${personaje.id}-${a.type}-${a.variant}`,
      type: a.type,
      variant: a.variant,
      label: variantInfo?.label || a.variant,
      prompt: a.prompt,
      status: 'prompt_ready' as const,
    };
  });

  return assets;
}
