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_LABELS, PACK_ORDER } from '@/lib/templates';
import type { AssetPack, GeneratePackRequest, GeneratePackResponse, GenSession, PackKind } from '@/lib/types';

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

const PACK_KINDS: PackKind[] = PACK_ORDER;

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));
}

function getPreviousKind(kind: PackKind): PackKind | null {
  const index = PACK_ORDER.indexOf(kind);
  if (index <= 0) return null;
  return PACK_ORDER[index - 1];
}

function findCompletePack(session: GenSession, imageId: string, kind: PackKind) {
  return session.packs?.find(pack => pack.kind === kind && isCompletePack(pack, imageId));
}

function validateSequentialGate(session: GenSession, imageId: string, kind: PackKind) {
  if (!session.characterSpec) {
    return {
      error: '请先锁定角色设定，然后从专利包开始生成。',
      requiredKind: 'character',
    };
  }

  const previousKind = getPreviousKind(kind);
  if (!previousKind) return null;

  if (!findCompletePack(session, imageId, previousKind)) {
    return {
      error: `请先完成${PACK_LABELS[previousKind]}，再生成${PACK_LABELS[kind]}。`,
      requiredKind: previousKind,
    };
  }

  return null;
}

export async function POST(req: Request) {
  const { sessionId, imageId, kind, background = false } = (await req.json()) as GeneratePackRequest;
  if (!sessionId || !imageId || !PACK_KINDS.includes(kind)) {
    return NextResponse.json({ error: 'sessionId, imageId and valid kind 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 gate = validateSequentialGate(session, imageId, kind);
  if (gate) {
    recordEvent({
      action: 'pack.generate_blocked_sequence',
      sessionId,
      targetType: 'pack',
      targetId: kind,
      status: 'blocked',
      provider: detectProvider(),
      message: gate.error,
      metadata: { imageId, kind, requiredKind: gate.requiredKind },
    });
    return NextResponse.json({ error: gate.error, requiredKind: gate.requiredKind }, { status: 409 });
  }

  const baseSession = session;
  const baseSourceImage = sourceImage;
  const releaseLock = startGenerationLock(`pack:${sessionId}:${imageId}:${kind}`);
  if (!releaseLock) {
    recordEvent({
      action: 'pack.generate_blocked_running',
      sessionId,
      targetType: 'pack',
      targetId: kind,
      status: 'blocked',
      provider: detectProvider(),
      metadata: { imageId, kind },
    });
    return NextResponse.json({
      ok: true,
      background: true,
      running: true,
      kind,
      provider: detectProvider(),
    }, { status: 202 });
  }
  const release = releaseLock;

  async function run() {
    try {
      recordEvent({
        action: 'pack.generate_started',
        sessionId,
        targetType: 'pack',
        targetId: kind,
        status: 'started',
        provider: detectProvider(),
        metadata: { imageId, background },
      });
      const { pack, manifest, provider } = await generateAssetPack({
        session: baseSession,
        sourceImage: baseSourceImage,
        kind,
        onProgress: async progressPack => {
          await persistPackProgress(baseSession, 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 },
          });
        },
      });
      baseSession.characterSpec = pack.characterSpec;
      baseSession.packs = [
        ...(baseSession.packs ?? []).filter(existing => !(existing.kind === kind && existing.sourceImageId === imageId)),
        pack,
      ];
      baseSession.exports = [
        ...(baseSession.exports ?? []).filter(existing => !(existing.packKind === kind && existing.source.sourceImageId === imageId)),
        manifest,
      ];
      await saveSession(baseSession);
      recordEvent({
        action: 'pack.generate_completed',
        sessionId,
        targetType: 'pack',
        targetId: pack.id,
        status: 'ok',
        provider,
        metadata: { kind, assets: pack.assets.length, manifestId: manifest.id },
      });
      return { pack, manifest, provider } satisfies GeneratePackResponse;
    } finally {
      release();
    }
  }

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

  try {
    const response = await run();

    return NextResponse.json(response);
  } catch (error) {
    recordEvent({ action: 'pack.generate_failed', sessionId, targetType: 'pack', targetId: kind, status: 'error', provider: detectProvider(), message: String(error), metadata: { imageId, background: false } });
    return NextResponse.json({ error: String(error) }, { status: 500 });
  }
}
