import { NextRequest, NextResponse } from 'next/server';
import fs from 'fs/promises';
import path from 'path';
import matter from 'gray-matter';
import { getPersonajes, getCharacterAssetsMap, getIdeaMotriz } from '@/lib/file-service';
import type { PersonajeEnPlano, PromptComposition, ReferenceAsset, FondoReferencia } from '@/types/project';

const PROJECTS_DIR = path.join(process.cwd(), 'projects');
const DEFAULT_ESTILO = 'Pixar-style 3D animation, vibrant colors, expressive characters';

async function findCapDir(capitulosDir: string, numero: string) {
  const dirs = await fs.readdir(capitulosDir, { withFileTypes: true });
  return dirs.find(d =>
    d.isDirectory() && d.name.startsWith(numero.padStart(2, '0'))
  );
}

/** Compose a final prompt string from sub-fields */
function composePrompt(comp: PromptComposition): string {
  const parts = [
    comp.tipoPlano,
    comp.camara,
    comp.optica,
    comp.personajes,
    comp.accion,
    comp.luz,
    comp.estilo,
    comp.custom,
  ].filter(Boolean);
  return parts.join('. ') + '.';
}

/** Build referenceAssets with 3 T-poses + emotion for each character in the plano */
function buildReferenceAssets(
  detalle: PersonajeEnPlano[],
  assetsMap: Record<string, any>
): ReferenceAsset[] {
  const referenceAssets: ReferenceAsset[] = [];
  for (const pd of detalle) {
    const summary = assetsMap[pd.personajeId];
    if (!summary) continue;
    referenceAssets.push({
      personajeId: pd.personajeId,
      nombre: pd.nombre,
      emocion: pd.emocion,
      tposeFrontUrl: summary.tposeFrontUrl,
      tposeSideUrl: summary.tposeSideUrl,
      tposeBackUrl: summary.tposeBackUrl,
      emotionUrl: summary.emotions[pd.emocion],
    });
  }
  return referenceAssets;
}

/** Map segmentoGuion string (e.g. "I.1 El Silencio") to fondoId */
function getFondoIdForPlano(plano: any, fondos: FondoReferencia[]): string | undefined {
  const segGuion: string = plano.segmentoGuion || '';
  const segId = segGuion.split(' ')[0];
  const fondo = fondos.find(f => f.segmentoId === segId);
  return fondo?.id;
}

/** Ensure a value is a string (LLM may return arrays/objects for some fields) */
function ensureString(val: any): string {
  if (typeof val === 'string') return val;
  if (val == null) return '';
  if (Array.isArray(val)) {
    return val.map(v => typeof v === 'object' ? Object.values(v).filter(Boolean).join(', ') : String(v)).join('. ');
  }
  if (typeof val === 'object') return Object.values(val).filter(Boolean).join(', ');
  return String(val);
}

/** Enrich a plano from LLM output: inject estilo, compose promptImagen, add refs + fondoId */
function enrichPlano(
  plano: any,
  estiloVisual: string,
  assetsMap: Record<string, any>,
  fondos: FondoReferencia[]
): any {
  const detalle: PersonajeEnPlano[] = plano.personajesDetalle || [];
  const referenceAssets = buildReferenceAssets(detalle, assetsMap);
  const fondoId = getFondoIdForPlano(plano, fondos);

  // Build promptComposition: LLM provides most fields, we inject estilo
  const llmComp = plano.promptComposition || {};
  const promptComposition: PromptComposition = {
    personajes: ensureString(llmComp.personajes),
    accion: ensureString(llmComp.accion),
    luz: ensureString(llmComp.luz),
    camara: ensureString(llmComp.camara),
    optica: ensureString(llmComp.optica),
    tipoPlano: ensureString(llmComp.tipoPlano),
    estilo: estiloVisual,
    custom: ensureString(llmComp.custom),
  };

  const promptImagen = composePrompt(promptComposition);

  return { ...plano, promptComposition, promptImagen, referenceAssets, fondoId };
}

export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ slug: string; numero: string }> }
) {
  try {
    const { slug, numero } = await params;
    const capitulosDir = path.join(PROJECTS_DIR, slug, 'capitulos');
    const capDir = await findCapDir(capitulosDir, numero);

    if (!capDir) {
      return NextResponse.json(null);
    }

    const promptsPath = path.join(capitulosDir, capDir.name, 'prompts.md');

    try {
      const fileContent = await fs.readFile(promptsPath, 'utf-8');
      const { data, content } = matter(fileContent);
      return NextResponse.json({ ...data, contenidoMarkdown: content });
    } catch {
      return NextResponse.json(null);
    }
  } catch (error: any) {
    console.error('Error fetching prompts:', error);
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
}

export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ slug: string; numero: string }> }
) {
  try {
    const { slug, numero } = await params;
    const { action, planoId } = await request.json();

    const capitulosDir = path.join(PROJECTS_DIR, slug, 'capitulos');
    const capDir = await findCapDir(capitulosDir, numero);

    if (!capDir) {
      return NextResponse.json({ error: 'Capítulo no encontrado' }, { status: 404 });
    }

    const capPath = path.join(capitulosDir, capDir.name);
    const promptsPath = path.join(capPath, 'prompts.md');

    // Shared: read estiloVisual + fondos
    const ideaMotriz = await getIdeaMotriz(slug);
    const estiloVisual = ideaMotriz?.estiloVisual || DEFAULT_ESTILO;

    let fondos: FondoReferencia[] = [];
    try {
      fondos = JSON.parse(await fs.readFile(path.join(capPath, 'fondos.json'), 'utf-8'));
    } catch { /* no fondos yet */ }

    if (action === 'generate') {
      // Read planos from server-side JSON (saved by escaleta generation)
      const planosPath = path.join(capPath, 'escaleta-planos.json');
      let planos: any[] = [];
      try {
        const planosJson = await fs.readFile(planosPath, 'utf-8');
        planos = JSON.parse(planosJson);
      } catch {
        return NextResponse.json({ error: 'No se encontraron planos. Regenera la escaleta primero.' }, { status: 400 });
      }
      if (planos.length === 0) {
        return NextResponse.json({ error: 'La escaleta no tiene planos. Regenera la escaleta.' }, { status: 400 });
      }

      // Read escaleta frontmatter for metadata
      let escaletaMeta: Record<string, any> = {};
      try {
        const escaletaContent = await fs.readFile(path.join(capPath, 'escaleta.md'), 'utf-8');
        const { data } = matter(escaletaContent);
        escaletaMeta = data;
      } catch { /* ok */ }

      const personajes = await getPersonajes(slug);
      const assetsMap = await getCharacterAssetsMap(slug, personajes);

      const { generateImagePrompts } = await import('@/lib/llm-service');
      const escaletaForLLM = { ...escaletaMeta, planos } as any;
      const planosConPrompts = await generateImagePrompts(escaletaForLLM, personajes);

      // Enrich each plano
      const planosEnriched = planosConPrompts.map((plano: any) =>
        enrichPlano(plano, estiloVisual, assetsMap, fondos)
      );

      // Save enriched planos as JSON for downstream image generation
      const promptsPlanosPath = path.join(capPath, 'prompts-planos.json');
      await fs.writeFile(promptsPlanosPath, JSON.stringify(planosEnriched, null, 2));

      const mdContent = generatePromptsMarkdown(planosEnriched);
      const frontmatter = {
        capitulo: escaletaMeta.capitulo || '',
        totalPrompts: planosEnriched.length,
      };
      const fileContent = matter.stringify(mdContent, frontmatter);
      await fs.writeFile(promptsPath, fileContent);

      return NextResponse.json({ ...frontmatter, contenidoMarkdown: mdContent, planos: planosEnriched });
    }

    if (action === 'generate-stream') {
      const escaletaPlanosPath = path.join(capPath, 'escaleta-planos.json');
      let streamPlanos: any[] = [];
      try {
        const planosJson = await fs.readFile(escaletaPlanosPath, 'utf-8');
        streamPlanos = JSON.parse(planosJson);
      } catch {
        return NextResponse.json({ error: 'No se encontraron planos. Regenera la escaleta primero.' }, { status: 400 });
      }
      if (streamPlanos.length === 0) {
        return NextResponse.json({ error: 'La escaleta no tiene planos. Regenera la escaleta.' }, { status: 400 });
      }

      let escaletaMeta: Record<string, any> = {};
      try {
        const escaletaContent = await fs.readFile(path.join(capPath, 'escaleta.md'), 'utf-8');
        const { data } = matter(escaletaContent);
        escaletaMeta = data;
      } catch { /* ok */ }

      const personajes = await getPersonajes(slug);
      const assetsMap = await getCharacterAssetsMap(slug, personajes);

      const encoder = new TextEncoder();
      const stream = new ReadableStream({
        async start(controller) {
          const send = (data: any) => {
            try {
              controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
            } catch { /* client disconnected */ }
          };

          try {
            const { generateImagePrompts } = await import('@/lib/llm-service');
            const escaletaForLLM = { ...escaletaMeta, planos: streamPlanos } as any;
            const planosConPrompts = await generateImagePrompts(escaletaForLLM, personajes,
              (current, total, label) => {
                send({ type: 'progress', current, total, label });
              }
            );

            const planosEnriched = planosConPrompts.map((plano: any) =>
              enrichPlano(plano, estiloVisual, assetsMap, fondos)
            );

            const promptsPlanosPath = path.join(capPath, 'prompts-planos.json');
            await fs.writeFile(promptsPlanosPath, JSON.stringify(planosEnriched, null, 2));

            const mdContent = generatePromptsMarkdown(planosEnriched);
            const frontmatter = {
              capitulo: escaletaMeta.capitulo || '',
              totalPrompts: planosEnriched.length,
            };
            const fileContent = matter.stringify(mdContent, frontmatter);
            await fs.writeFile(promptsPath, fileContent);

            send({ type: 'done', result: { ...frontmatter, contenidoMarkdown: mdContent, planos: planosEnriched } });
          } catch (error: any) {
            send({ type: 'error', message: error.message });
          }

          controller.close();
        }
      });

      return new Response(stream, {
        headers: {
          'Content-Type': 'text/event-stream',
          'Cache-Control': 'no-cache',
          'X-Accel-Buffering': 'no',
        },
      });
    }

    if (action === 'generate-single') {
      if (!planoId) {
        return NextResponse.json({ error: 'Se necesita planoId' }, { status: 400 });
      }

      // Read escaleta planos
      const planosPath = path.join(capPath, 'escaleta-planos.json');
      let escaletaPlanos: any[] = [];
      try {
        escaletaPlanos = JSON.parse(await fs.readFile(planosPath, 'utf-8'));
      } catch {
        return NextResponse.json({ error: 'No se encontraron planos. Regenera la escaleta.' }, { status: 400 });
      }

      const targetPlano = escaletaPlanos.find((p: any) => p.id === planoId);
      if (!targetPlano) {
        return NextResponse.json({ error: 'Plano no encontrado' }, { status: 404 });
      }

      const personajes = await getPersonajes(slug);
      const assetsMap = await getCharacterAssetsMap(slug, personajes);

      // Generate prompt for this single plano
      const { generateImagePrompts } = await import('@/lib/llm-service');
      const singleEscaleta = { planos: [targetPlano] } as any;
      const result = await generateImagePrompts(singleEscaleta, personajes);
      const planoConPrompt = result[0];
      if (!planoConPrompt) {
        return NextResponse.json({ error: 'No se generó prompt' }, { status: 500 });
      }

      // Enrich
      const enrichedPlano = enrichPlano(
        { ...targetPlano, ...planoConPrompt, personajesDetalle: targetPlano.personajesDetalle || planoConPrompt.personajesDetalle },
        estiloVisual,
        assetsMap,
        fondos
      );

      // Update prompts-planos.json: merge into existing or create
      const promptsPlanosPath = path.join(capPath, 'prompts-planos.json');
      let existingPlanos: any[] = [];
      try {
        existingPlanos = JSON.parse(await fs.readFile(promptsPlanosPath, 'utf-8'));
      } catch { /* first time */ }

      const existingIdx = existingPlanos.findIndex((p: any) => p.id === planoId);
      if (existingIdx >= 0) {
        existingPlanos[existingIdx] = enrichedPlano;
      } else {
        existingPlanos.push(enrichedPlano);
      }
      await fs.writeFile(promptsPlanosPath, JSON.stringify(existingPlanos, null, 2));

      return NextResponse.json({ plano: enrichedPlano });
    }

    return NextResponse.json({ error: 'Acción inválida' }, { status: 400 });
  } catch (error: any) {
    console.error('Error en prompts:', error);
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
}

function generatePromptsMarkdown(planos: any[]): string {
  let md = `# Prompts de Imágenes\n\n`;
  md += `Total: ${planos.length} prompts generados\n\n`;

  for (const plano of planos) {
    md += `## Plano ${plano.numeroPlano}\n\n`;
    md += `**Segmento:** ${plano.segmentoGuion} | **Duración:** ${plano.tiempoSegundos}s\n\n`;

    const detalle: PersonajeEnPlano[] = plano.personajesDetalle || [];
    if (detalle.length > 0) {
      md += `**Personajes:**\n`;
      for (const pd of detalle) {
        md += `- ${pd.nombre} — *${pd.emocion}*\n`;
      }
      md += `\n`;
    }

    md += `**Acción:** ${plano.accionDescripcion}\n\n`;

    if (plano.promptImagen) {
      md += `> **Prompt final:** ${plano.promptImagen}\n\n`;
    }

    // Show composition sub-fields
    const comp = plano.promptComposition;
    if (comp) {
      md += `**Composición:**\n`;
      if (comp.tipoPlano) md += `- Tipo plano: ${comp.tipoPlano}\n`;
      if (comp.camara) md += `- Cámara: ${comp.camara}\n`;
      if (comp.optica) md += `- Óptica: ${comp.optica}\n`;
      if (comp.luz) md += `- Luz: ${comp.luz}\n`;
      md += `\n`;
    }

    // Reference assets
    if (plano.referenceAssets?.length) {
      md += `**Assets de referencia:**\n`;
      for (const ref of plano.referenceAssets) {
        const parts: string[] = [];
        if (ref.tposeFrontUrl) parts.push('Front');
        if (ref.tposeSideUrl) parts.push('Side');
        if (ref.tposeBackUrl) parts.push('Back');
        if (ref.emotionUrl) parts.push(`Emoción: ${ref.emocion}`);
        const hasTpose = ref.tposeFrontUrl || ref.tposeSideUrl || ref.tposeBackUrl;
        const missing = !hasTpose ? ' ⚠️ sin T-pose' : (!ref.emotionUrl ? ` ⚠️ sin emoción ${ref.emocion}` : '');
        md += `- **${ref.nombre}**: ${parts.join(' + ')}${missing}\n`;
      }
      md += `\n`;
    }

    if (plano.fondoId) {
      md += `**Fondo ref:** ${plano.fondoId}\n\n`;
    }

    md += `---\n\n`;
  }

  return md;
}
