import { NextResponse } from 'next/server';
import { PACK_ORDER } from '@/lib/templates';
import { loadSession, readImageUrl } from '@/lib/storage';
import type { PackKind, ToyAsset } from '@/lib/types';

export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

const CRC_TABLE = new Uint32Array(256).map((_, index) => {
  let value = index;
  for (let bit = 0; bit < 8; bit += 1) {
    value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
  }
  return value >>> 0;
});

function crc32(buffer: Buffer) {
  let crc = 0xffffffff;
  for (const byte of buffer) {
    crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
  }
  return (crc ^ 0xffffffff) >>> 0;
}

function safeName(input: string) {
  return input
    .trim()
    .replace(/[\\/:*?"<>|]+/g, '-')
    .replace(/\s+/g, '_')
    .replace(/^-+|-+$/g, '')
    .slice(0, 80) || 'asset';
}

function extFromAsset(asset: ToyAsset, sourceFilename: string, mimeType: string) {
  const fromName = sourceFilename.match(/\.([a-z0-9]+)$/i)?.[1];
  if (fromName) return fromName.toLowerCase() === 'jpeg' ? 'jpg' : fromName.toLowerCase();
  if (mimeType.includes('jpeg')) return 'jpg';
  if (mimeType.includes('webp')) return 'webp';
  if (mimeType.includes('svg')) return 'svg';
  return asset.url.match(/\.([a-z0-9]+)(?:\?|$)/i)?.[1] ?? 'png';
}

function makeZip(files: Array<{ name: string; data: Buffer }>) {
  const localParts: Buffer[] = [];
  const centralParts: Buffer[] = [];
  let offset = 0;
  const dosTime = 0;
  const dosDate = 33;

  for (const file of files) {
    const name = Buffer.from(file.name, 'utf8');
    const data = file.data;
    const crc = crc32(data);

    const local = Buffer.alloc(30);
    local.writeUInt32LE(0x04034b50, 0);
    local.writeUInt16LE(20, 4);
    local.writeUInt16LE(0x0800, 6);
    local.writeUInt16LE(0, 8);
    local.writeUInt16LE(dosTime, 10);
    local.writeUInt16LE(dosDate, 12);
    local.writeUInt32LE(crc, 14);
    local.writeUInt32LE(data.length, 18);
    local.writeUInt32LE(data.length, 22);
    local.writeUInt16LE(name.length, 26);
    local.writeUInt16LE(0, 28);
    localParts.push(local, name, data);

    const central = Buffer.alloc(46);
    central.writeUInt32LE(0x02014b50, 0);
    central.writeUInt16LE(20, 4);
    central.writeUInt16LE(20, 6);
    central.writeUInt16LE(0x0800, 8);
    central.writeUInt16LE(0, 10);
    central.writeUInt16LE(dosTime, 12);
    central.writeUInt16LE(dosDate, 14);
    central.writeUInt32LE(crc, 16);
    central.writeUInt32LE(data.length, 20);
    central.writeUInt32LE(data.length, 24);
    central.writeUInt16LE(name.length, 28);
    central.writeUInt16LE(0, 30);
    central.writeUInt16LE(0, 32);
    central.writeUInt16LE(0, 34);
    central.writeUInt16LE(0, 36);
    central.writeUInt32LE(0, 38);
    central.writeUInt32LE(offset, 42);
    centralParts.push(central, name);

    offset += local.length + name.length + data.length;
  }

  const centralSize = centralParts.reduce((sum, part) => sum + part.length, 0);
  const end = Buffer.alloc(22);
  end.writeUInt32LE(0x06054b50, 0);
  end.writeUInt16LE(0, 4);
  end.writeUInt16LE(0, 6);
  end.writeUInt16LE(files.length, 8);
  end.writeUInt16LE(files.length, 10);
  end.writeUInt32LE(centralSize, 12);
  end.writeUInt32LE(offset, 16);
  end.writeUInt16LE(0, 20);

  return Buffer.concat([...localParts, ...centralParts, end]);
}

export async function GET(req: Request) {
  const url = new URL(req.url);
  const sessionId = url.searchParams.get('sessionId')?.trim();
  const kind = url.searchParams.get('kind')?.trim() as PackKind | null;

  if (!sessionId || !kind || !PACK_ORDER.includes(kind)) {
    return NextResponse.json({ error: 'bad request' }, { status: 400 });
  }

  const session = await loadSession(sessionId);
  if (!session) return NextResponse.json({ error: 'session not found' }, { status: 404 });

  const selectedImage = session.images.find(image => image.status === 'selected') ?? session.images[0];
  const pack = session.packs?.find(item => item.kind === kind && (!selectedImage || item.sourceImageId === selectedImage.id))
    ?? session.packs?.find(item => item.kind === kind);

  if (!pack?.assets.length) {
    return NextResponse.json({ error: 'pack has no images' }, { status: 404 });
  }

  const files = await Promise.all(pack.assets.map(async (asset, index) => {
    const image = await readImageUrl(asset.url);
    const ext = extFromAsset(asset, image.filename, image.type);
    const fileName = `${String(index + 1).padStart(2, '0')}_${safeName(asset.templateId)}.${ext}`;
    return { name: fileName, data: image.buf };
  }));

  const zip = makeZip(files);
  const filename = `${safeName(session.characterSpec?.name ?? session.prompt ?? session.id)}_${kind}_${pack.assets.length}张.zip`;

  return new Response(zip, {
    headers: {
      'Content-Type': 'application/zip',
      'Content-Length': String(zip.length),
      'Content-Disposition': `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`,
      'Cache-Control': 'no-store',
    },
  });
}
