./kaisetsu-app/src/app/api/export/route.ts

import { NextRequest, NextResponse } from 'next/server';
import { writeFile, unlink, readFile, mkdir } from 'fs/promises';
import { join } from 'path';
import { tmpdir } from 'os';
import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

interface NarrationBlock {
  id: string;
  startTime: number;
  audioData: string; // Base64 encoded audio
}

interface ExportRequest {
  videoData: string; // Base64 encoded video
  narrations: NarrationBlock[];
  format: 'video' | 'audio-only';
  includeOriginalAudio: boolean;
}

async function checkFFmpeg(): Promise<boolean> {
  try {
    await execAsync('which ffmpeg');
    return true;
  } catch {
    return false;
  }
}

export async function POST(request: NextRequest) {
  const tempDir = join(tmpdir(), `export-${Date.now()}`);
  const tempFiles: string[] = [];

  try {
    // Check FFmpeg availability
    const hasFFmpeg = await checkFFmpeg();
    if (!hasFFmpeg) {
      return NextResponse.json(
        { error: 'FFmpeg is not installed on the server' },
        { status: 500 }
      );
    }

    const body: ExportRequest = await request.json();
    const { videoData, narrations, format, includeOriginalAudio } = body;

    if (!videoData) {
      return NextResponse.json({ error: 'No video data provided' }, { status: 400 });
    }

    // Create temp directory
    await mkdir(tempDir, { recursive: true });

    // Save original video
    const videoBuffer = Buffer.from(videoData, 'base64');
    const videoPath = join(tempDir, 'input.mp4');
    await writeFile(videoPath, videoBuffer);
    tempFiles.push(videoPath);

    // Save narration audio files
    const narrationFiles: { path: string; startTime: number }[] = [];
    for (let i = 0; i < narrations.length; i++) {
      const narration = narrations[i];
      const audioBuffer = Buffer.from(narration.audioData, 'base64');
      const audioPath = join(tempDir, `narration-${i}.mp3`);
      await writeFile(audioPath, audioBuffer);
      tempFiles.push(audioPath);
      narrationFiles.push({ path: audioPath, startTime: narration.startTime });
    }

    const outputPath = join(tempDir, format === 'video' ? 'output.mp4' : 'output.mp3');
    tempFiles.push(outputPath);

    if (narrationFiles.length === 0) {
      // No narrations, just return original
      if (format === 'video') {
        const outputBuffer = await readFile(videoPath);
        return new NextResponse(outputBuffer, {
          headers: {
            'Content-Type': 'video/mp4',
            'Content-Disposition': 'attachment; filename="output.mp4"',
          },
        });
      } else {
        // Extract audio only
        await execAsync(`ffmpeg -i "${videoPath}" -vn -acodec libmp3lame -q:a 2 "${outputPath}"`);
        const outputBuffer = await readFile(outputPath);
        return new NextResponse(outputBuffer, {
          headers: {
            'Content-Type': 'audio/mpeg',
            'Content-Disposition': 'attachment; filename="output.mp3"',
          },
        });
      }
    }

    // Build FFmpeg filter complex for mixing narrations
    let filterComplex = '';
    let inputs = `-i "${videoPath}"`;

    // Add narration inputs
    for (let i = 0; i < narrationFiles.length; i++) {
      inputs += ` -i "${narrationFiles[i].path}"`;
    }

    // Build filter complex
    // First, handle original audio
    if (includeOriginalAudio) {
      filterComplex += '[0:a]volume=1.0[original];';
    }

    // Add delay to each narration
    for (let i = 0; i < narrationFiles.length; i++) {
      const delayMs = Math.round(narrationFiles[i].startTime * 1000);
      filterComplex += `[${i + 1}:a]adelay=${delayMs}|${delayMs}[nar${i}];`;
    }

    // Mix all audio streams
    let mixInputs = includeOriginalAudio ? '[original]' : '';
    for (let i = 0; i < narrationFiles.length; i++) {
      mixInputs += `[nar${i}]`;
    }
    const streamCount = narrationFiles.length + (includeOriginalAudio ? 1 : 0);
    filterComplex += `${mixInputs}amix=inputs=${streamCount}:duration=longest:dropout_transition=0[mixed]`;

    if (format === 'video') {
      // Output video with mixed audio
      const cmd = `ffmpeg -y ${inputs} -filter_complex "${filterComplex}" -map 0:v -map "[mixed]" -c:v copy -c:a aac -b:a 192k "${outputPath}"`;
      await execAsync(cmd);

      const outputBuffer = await readFile(outputPath);
      return new NextResponse(outputBuffer, {
        headers: {
          'Content-Type': 'video/mp4',
          'Content-Disposition': 'attachment; filename="output.mp4"',
        },
      });
    } else {
      // Output audio only
      const cmd = `ffmpeg -y ${inputs} -filter_complex "${filterComplex}" -map "[mixed]" -c:a libmp3lame -q:a 2 "${outputPath}"`;
      await execAsync(cmd);

      const outputBuffer = await readFile(outputPath);
      return new NextResponse(outputBuffer, {
        headers: {
          'Content-Type': 'audio/mpeg',
          'Content-Disposition': 'attachment; filename="output.mp3"',
        },
      });
    }

  } catch (error) {
    console.error('Export error:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Export failed' },
      { status: 500 }
    );
  } finally {
    // Cleanup temp files
    for (const file of tempFiles) {
      try {
        await unlink(file);
      } catch {
        // Ignore cleanup errors
      }
    }
    try {
      await execAsync(`rm -rf "${tempDir}"`);
    } catch {
      // Ignore cleanup errors
    }
  }
}