import { NextRequest, NextResponse } from 'next/server';
import { FileExampleManager } from '../../../src/data/fileExamples';

// Simple ZIP creation without external dependencies
class SimpleZipCreator {
  private files: Array<{ filename: string; content: string }> = [];

  addFile(filename: string, content: string) {
    this.files.push({ filename, content });
  }

  // Create a basic ZIP file structure
  create(): number[] {
    const encoder = new TextEncoder();
    const chunks: number[][] = [];
    
    let offset = 0;
    const centralDirectory: Array<{
      name: string;
      offset: number;
      size: number;
      compressedSize: number;
    }> = [];

    // Process each file
    for (const file of this.files) {
      const contentBytes = Array.from(encoder.encode(file.content));
      const filenameBytes = Array.from(encoder.encode(file.filename));
      
      // Local file header
      const localHeader: number[] = [];
      
      // Local file header signature (little endian)
      localHeader.push(0x50, 0x4b, 0x03, 0x04);
      // Version needed to extract
      localHeader.push(20, 0);
      // General purpose bit flag
      localHeader.push(0, 0);
      // Compression method (0 = store)
      localHeader.push(0, 0);
      // Last modified time/date
      localHeader.push(0, 0, 0, 0);
      // CRC32 (0 for simplicity)
      localHeader.push(0, 0, 0, 0);
      // Compressed size
      localHeader.push(...intToLittleEndian(contentBytes.length, 4));
      // Uncompressed size
      localHeader.push(...intToLittleEndian(contentBytes.length, 4));
      // Filename length
      localHeader.push(...intToLittleEndian(filenameBytes.length, 2));
      // Extra field length
      localHeader.push(0, 0);
      
      // Add filename
      localHeader.push(...filenameBytes);
      
      chunks.push(localHeader);
      chunks.push(contentBytes);
      
      // Store info for central directory
      centralDirectory.push({
        name: file.filename,
        offset: offset,
        size: contentBytes.length,
        compressedSize: contentBytes.length
      });
      
      offset += localHeader.length + contentBytes.length;
    }

    // Central directory
    const centralDirectoryChunks: number[][] = [];
    let centralDirectoryOffset = offset;

    for (const entry of centralDirectory) {
      const filenameBytes = Array.from(encoder.encode(entry.name));
      const centralHeader: number[] = [];
      
      // Central directory header signature
      centralHeader.push(0x50, 0x4b, 0x01, 0x02);
      // Version made by
      centralHeader.push(20, 0);
      // Version needed to extract
      centralHeader.push(20, 0);
      // General purpose bit flag
      centralHeader.push(0, 0);
      // Compression method
      centralHeader.push(0, 0);
      // Last modified time/date
      centralHeader.push(0, 0, 0, 0);
      // CRC32
      centralHeader.push(0, 0, 0, 0);
      // Compressed size
      centralHeader.push(...intToLittleEndian(entry.compressedSize, 4));
      // Uncompressed size
      centralHeader.push(...intToLittleEndian(entry.size, 4));
      // Filename length
      centralHeader.push(...intToLittleEndian(filenameBytes.length, 2));
      // Extra field length
      centralHeader.push(0, 0);
      // File comment length
      centralHeader.push(0, 0);
      // Disk number start
      centralHeader.push(0, 0);
      // Internal file attributes
      centralHeader.push(0, 0);
      // External file attributes
      centralHeader.push(0, 0, 0, 0);
      // Relative offset of local header
      centralHeader.push(...intToLittleEndian(entry.offset, 4));
      
      // Add filename
      centralHeader.push(...filenameBytes);
      
      centralDirectoryChunks.push(centralHeader);
    }

    const centralDirectorySize = centralDirectoryChunks.reduce((sum, chunk) => sum + chunk.length, 0);
    
    // End of central directory
    const endOfCentralDirectory: number[] = [];
    
    // End of central directory signature
    endOfCentralDirectory.push(0x50, 0x4b, 0x05, 0x06);
    // Number of this disk
    endOfCentralDirectory.push(0, 0);
    // Number of the disk with the start of the central directory
    endOfCentralDirectory.push(0, 0);
    // Total number of entries on this disk
    endOfCentralDirectory.push(...intToLittleEndian(centralDirectory.length, 2));
    // Total number of entries
    endOfCentralDirectory.push(...intToLittleEndian(centralDirectory.length, 2));
    // Size of the central directory
    endOfCentralDirectory.push(...intToLittleEndian(centralDirectorySize, 4));
    // Offset of start of central directory
    endOfCentralDirectory.push(...intToLittleEndian(centralDirectoryOffset, 4));
    // ZIP file comment length
    endOfCentralDirectory.push(0, 0);

    // Combine all chunks
    const result: number[] = [];
    chunks.forEach(chunk => result.push(...chunk));
    centralDirectoryChunks.forEach(chunk => result.push(...chunk));
    result.push(...endOfCentralDirectory);
    
    return result;
  }
}

// Helper function to convert integer to little endian bytes
function intToLittleEndian(value: number, bytes: number): number[] {
  const result: number[] = [];
  for (let i = 0; i < bytes; i++) {
    result.push((value >> (i * 8)) & 0xff);
  }
  return result;
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { fileIds } = body;

    if (!Array.isArray(fileIds) || fileIds.length === 0) {
      return NextResponse.json(
        { error: 'fileIds array is required and must not be empty' },
        { status: 400 }
      );
    }

    // Get file contents
    const files = await Promise.all(
      fileIds.map(async (id) => {
        const file = await FileExampleManager.getFileExample(id);
        if (!file) {
          throw new Error(`File with id ${id} not found`);
        }
        return file;
      })
    );

    // Create ZIP
    const zipCreator = new SimpleZipCreator();
    files.forEach(file => {
      zipCreator.addFile(file.filename, file.content);
    });

    const zipData = zipCreator.create();
    const buffer = Buffer.from(new Uint8Array(zipData));

    // Return ZIP file
    return new Response(buffer, {
      headers: {
        'Content-Type': 'application/zip',
        'Content-Disposition': `attachment; filename="code-examples-${Date.now()}.zip"`,
        'Cache-Control': 'no-cache',
      }
    });

  } catch (error) {
    console.error('ZIP download error:', error);
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    return NextResponse.json(
      { error: 'Failed to create zip file', details: errorMessage },
      { status: 500 }
    );
  }
}

export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url);
    const fileId = searchParams.get('id');
    
    if (!fileId) {
      return NextResponse.json(
        { error: 'id parameter is required' },
        { status: 400 }
      );
    }

    const file = await FileExampleManager.getFileExample(fileId);
    
    if (!file) {
      return NextResponse.json(
        { error: 'File not found' },
        { status: 404 }
      );
    }

    const encoder = new TextEncoder();
    const content = encoder.encode(file.content);
    const buffer = Buffer.from(content);

    return new Response(buffer, {
      headers: {
        'Content-Type': getMimeType(file.language),
        'Content-Disposition': `attachment; filename="${file.filename}"`,
        'Cache-Control': 'no-cache',
      }
    });

  } catch (error) {
    console.error('File download error:', error);
    return NextResponse.json(
      { error: 'Failed to download file' },
      { status: 500 }
    );
  }
}

// Helper function to get MIME type
function getMimeType(language: string): string {
  const mimeTypes: Record<string, string> = {
    'python': 'text/x-python',
    'typescript': 'text/x-typescript',
    'javascript': 'text/javascript',
    'dockerfile': 'text/x-dockerfile',
    'yaml': 'text/yaml',
    'json': 'application/json',
    'html': 'text/html',
    'css': 'text/css',
    'sql': 'text/x-sql',
    'bash': 'text/x-shellscript',
    'text': 'text/plain'
  };
  return mimeTypes[language] || 'text/plain';
}
