import { NextResponse } from 'next/server';
import { recordEvent } from '@/lib/auditDb';
import { startGenerationLock } from '@/lib/generationLocks';
import { generateAssetPack } from '@/lib/packGenerator';
import { detectProvider } from '@/lib/providers';
import { loadSession, saveSession } from '@/lib/storage';
import { getPackTemplates, PACK_ORDER } from '@/lib/templates';
import type { AssetPack, GenerateAllPacksRequest, GenerateAllPacksResponse, GenSession } from '@/lib/types';

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

async function persistPackProgress(session: GenSession, imageId: string, pack: AssetPack) {
  session.characterSpec = pack.characterSpec;
  session.packs = [
    ...(session.packs ?? []).filter(existing => !(existing.kind === pack.kind && existing.sourceImageId === imageId)),
    { ...pack, assets: [...pack.assets] },
  ];
  await saveSession(session);
}

function isCompletePack(pack: AssetPack, imageId: string): boolean {
  if (pack.sourceImageId !== imageId || pack.status !== 'complete') return false;
  const expectedIds = new Set(getPackTemplates(pack.kind).map(template => template.id));
  const assetIds = new Set(pack.assets.map(asset => asset.templateId));
  return expectedIds.size > 0 && [...expectedIds].every(templateId => assetIds.has(templateId));
}

export async function POST(req: Request) {
  const { sessionId, imageId, background = false } = (await req.json()) as GenerateAllPacksRequest;
  if (!sessionId || !imageId) {
    return NextResponse.json({ error: 'sessionId and imageId required' }, { status: 400 });
  }

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

  const sourceImage = session.images.find(image => image.id === imageId);
  if (!sourceImage) return NextResponse.json({ error: 'image not found' }, { status: 404 });
  if (sourceImage.status !== 'selected') {
    return NextResponse.json({ error: 'image must be selected before generating packs' }, { status: 400 });
  }

  const baseSession = session;
  const baseSourceImage = sourceImage;
  const releaseAllLock = startGenerationLock(`packs:all:${sessionId}:${imageId}`);
  if (!releaseAllLock) {
    recordEvent({
      action: 'packs.generate_all_blocked_running',
      sessionId,
      targetType: 'pack_all',
      targetId: imageId,
      status: 'blocked',
      provider: detectProvider(),
    });
    return NextResponse.json({
      ok: true,
      background: true,
      running: true,
      provider: detectProvider(),
    }, { status: 202 });
  }
  const releaseAll = releaseAllLock;

  async function run() {
    const packs: AssetPack[] = [];
    const manifests = [];
    let workingSession: GenSession = baseSession;

    try {
      recordEvent({ action: 'packs.generate_all_started', sessionId, targetType: 'pack_all', targetId: imageId, status: 'started', provider: detectProvider(), metadata: { background } });
      for (const kind of PACK_ORDER) {
        const existingPack = workingSession.packs?.find(pack => pack.kind === kind && isCompletePack(pack, imageId));
        if (existingPack) {
          recordEvent({ action: 'pack.generate_skipped_existing', sessionId, targetType: 'pack', targetId: existingPack.id, status: 'ok', provider: detectProvider(), metadata: { kind, assets: existingPack.assets.length } });
          const existingManifest = workingSession.exports?.find(manifest => (
            manifest.packKind === kind &&
            manifest.source.sourceImageId === imageId &&
            manifest.packId === existingPack.id
          ));
          packs.push(existingPack);
          if (existingManifest) manifests.push(existingManifest);
          continue;
        }

        const releasePackLock = startGenerationLock(`pack:${sessionId}:${imageId}:${kind}`);
        if (!releasePackLock) {
          recordEvent({ action: 'pack.generate_blocked_running', sessionId, targetType: 'pack', targetId: kind, status: 'blocked', provider: detectProvider(), metadata: { imageId, fromAll: true } });
          continue;
        }

        try {
          recordEvent({ action: 'pack.generate_started', sessionId, targetType: 'pack', targetId: kind, status: 'started', provider: detectProvider(), metadata: { imageId, fromAll: true } });
          const generated = await generateAssetPack({
            session: workingSession,
            sourceImage: baseSourceImage,
            kind,
            onProgress: async progressPack => {
              await persistPackProgress(workingSession, imageId, progressPack);
              recordEvent({ action: 'pack.generate_progress', sessionId, targetType: 'pack', targetId: progressPack.id, status: 'running', provider: detectProvider(), metadata: { kind, assets: progressPack.assets.length, packStatus: progressPack.status, fromAll: true } });
            },
          });
          recordEvent({ action: 'pack.generate_completed', sessionId, targetType: 'pack', targetId: generated.pack.id, status: 'ok', provider: generated.provider, metadata: { kind, assets: generated.pack.assets.length, fromAll: true } });
          packs.push(generated.pack);
          manifests.push(generated.manifest);
          workingSession = {
            ...workingSession,
            characterSpec: generated.pack.characterSpec,
            packs: [
              ...(workingSession.packs ?? []).filter(existing => !(existing.kind === kind && existing.sourceImageId === imageId)),
              generated.pack,
            ],
            exports: [
              ...(workingSession.exports ?? []).filter(existing => !(existing.packKind === kind && existing.source.sourceImageId === imageId)),
              generated.manifest,
            ],
          };
        } finally {
          releasePackLock();
        }
      }

      await saveSession(workingSession);
      recordEvent({ action: 'packs.generate_all_completed', sessionId, targetType: 'pack_all', targetId: imageId, status: 'ok', provider: detectProvider(), metadata: { packs: packs.length, manifests: manifests.length } });

      return {
        packs,
        manifests,
        provider: detectProvider(),
      } satisfies GenerateAllPacksResponse;
    } finally {
      releaseAll();
    }
  }

  if (background) {
    void run().catch(error => {
      recordEvent({ action: 'packs.generate_all_failed', sessionId, targetType: 'pack_all', targetId: imageId, status: 'error', provider: detectProvider(), message: String(error), metadata: { background: true } });
      console.error('[packs:all] background generation failed', error);
    });
    return NextResponse.json({
      ok: true,
      background: true,
      provider: detectProvider(),
    }, { status: 202 });
  }

  try {
    return NextResponse.json(await run());
  } catch (error) {
    recordEvent({ action: 'packs.generate_all_failed', sessionId, targetType: 'pack_all', targetId: imageId, status: 'error', provider: detectProvider(), message: String(error), metadata: { background: false } });
    return NextResponse.json({ error: String(error) }, { status: 500 });
  }
}
