import { NextResponse } from 'next/server';
import { randomBytes } from 'node:crypto';
import { recordEvent } from '@/lib/auditDb';
import { buildCharacterSpec, cleanupCharacterAnchor } from '@/lib/packGenerator';
import { detectProvider, generateGptImageEdit, generateMock } from '@/lib/providers';
import { saveGeneratedImage, saveSession } from '@/lib/storage';
import type {
  GenImage,
  GenSession,
  PreFilledSlot,
  ProjectFromUploadRequest,
  ProjectFromUploadResponse,
  UploadedImage,
} from '@/lib/types';

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

const VIEW_SLOT: Partial<Record<UploadedImage['role'], string>> = {
  'view-front': 'patent_front',
  'view-back': 'patent_back',
  'view-left': 'patent_left',
  'view-right': 'patent_right',
  'view-top': 'patent_top',
  'view-bottom': 'patent_bottom',
};

function clampCount(count: unknown): number {
  const n = Number(count);
  if (n === 4 || n === 8 || n === 12) return n;
  return 8;
}

function assertUpload(image: UploadedImage | undefined): UploadedImage {
  if (!image) throw new Error('subject upload required');
  if (!image.url.startsWith('/api/img/uploads/')) throw new Error('uploaded image URL must point to uploads bucket');
  return image;
}

function preFilledSlotsFromUploads(images: UploadedImage[]): PreFilledSlot[] {
  return images.flatMap(image => {
    const templateId = VIEW_SLOT[image.role];
    if (!templateId) return [];
    return [{
      uploadedImageId: image.id,
      templateId,
      role: image.role,
      url: image.url,
    }];
  });
}

async function createRemixSession(body: ProjectFromUploadRequest, sessionId: string): Promise<ProjectFromUploadResponse> {
  const reference = assertUpload(body.uploadedImages[0]);
  const count = clampCount(body.count);
  const prompt = [
    body.remixPrompt?.trim() || '基于上传参考图生成原创玩具风格变体',
    body.styleId ? `风格 ID：${body.styleId}` : '',
    '保留主体轮廓、五官相对位置、配件轮廓和核心识别点；可以改变材质、色彩和整体风格。',
    '避免直接复刻迪士尼、三丽鸥、泡泡玛特等已注册 IP；生成结果必须偏向原创玩具设计。',
  ].filter(Boolean).join('\n');
  const provider = detectProvider();

  const rawImages = provider === 'gpt'
    ? await Promise.all(Array.from({ length: count }).map(() => generateGptImageEdit({
      sessionId,
      prompt,
      anchorImage: reference.url,
      size: '1024x1024',
    })))
    : await generateMock({ sessionId, prompt, count });

  const images = await Promise.all(rawImages.map(async (image, index) => {
    const id = `img_${sessionId}_${index}`;
    const url = image.url.startsWith('data:') ? await saveGeneratedImage(sessionId, id, image.url) : image.url;
    return {
      ...image,
      id,
      url,
      prompt,
      meta: { ...(image.meta ?? {}), mode: 'remix', uploadedImageId: reference.id },
    };
  }));

  const session: GenSession = {
    id: sessionId,
    createdAt: Date.now(),
    prompt,
    refImages: body.uploadedImages.map(image => image.url),
    count,
    inputMode: 'remix',
    uploadedImages: body.uploadedImages,
    images,
  };
  await saveSession(session);
  return { sessionId, images, provider };
}

async function createReplicateOrExtendSession(body: ProjectFromUploadRequest, sessionId: string): Promise<ProjectFromUploadResponse> {
  const subject = assertUpload(body.uploadedImages.find(image => image.role === 'subject') ?? body.uploadedImages[0]);
  const prompt = body.userHint?.trim() || body.remixPrompt?.trim() || subject.originalFilename || '复刻上传主体玩具';
  const preFilledSlots = body.mode === 'extend' ? preFilledSlotsFromUploads(body.uploadedImages) : [];
  const sourceImage: GenImage = {
    id: `img_${sessionId}_upload_l0`,
    url: subject.url,
    prompt,
    status: 'selected',
    meta: {
      provider: 'upload',
      source: 'upload',
      mode: body.mode,
      uploadedImageId: subject.id,
      uploadRole: subject.role,
    },
  };
  const session: GenSession = {
    id: sessionId,
    createdAt: Date.now(),
    prompt,
    refImages: body.uploadedImages.map(image => image.url),
    count: 1,
    inputMode: body.mode,
    uploadedImages: body.uploadedImages,
    preFilledSlots,
    images: [sourceImage],
  };

  const characterSpec = await buildCharacterSpec(session, sourceImage);
  const cleaned = await cleanupCharacterAnchor({
    session,
    sourceImage,
    characterSpec,
    force: true,
    preserveLevel: 'strict',
  });
  session.characterSpec = cleaned.characterSpec;
  await saveSession(session);

  return {
    sessionId,
    characterSpec: cleaned.characterSpec,
    l1AnchorUrl: cleaned.cleanReferenceImageUrl,
    preFilledSlots,
    provider: cleaned.provider,
  };
}

export async function POST(req: Request) {
  try {
    const body = (await req.json()) as ProjectFromUploadRequest;
    if (!Array.isArray(body.uploadedImages) || body.uploadedImages.length === 0) {
      return NextResponse.json({ error: 'uploadedImages required' }, { status: 400 });
    }
    if (!['remix', 'replicate', 'extend'].includes(body.mode)) {
      return NextResponse.json({ error: 'valid mode required' }, { status: 400 });
    }

    const sessionId = `s_${Date.now().toString(36)}_${randomBytes(3).toString('hex')}`;
    recordEvent({
      action: 'upload_project.create_started',
      sessionId,
      targetType: 'session',
      targetId: sessionId,
      status: 'started',
      provider: detectProvider(),
      metadata: { mode: body.mode, uploads: body.uploadedImages.length, count: body.count ?? null },
    });
    const response = body.mode === 'remix'
      ? await createRemixSession(body, sessionId)
      : await createReplicateOrExtendSession(body, sessionId);
    recordEvent({
      action: 'upload_project.create_completed',
      sessionId,
      targetType: 'session',
      targetId: sessionId,
      status: 'ok',
      provider: response.provider,
      metadata: { mode: body.mode, images: response.images?.length ?? 1, preFilledSlots: response.preFilledSlots?.length ?? 0 },
    });

    return NextResponse.json(response satisfies ProjectFromUploadResponse);
  } catch (error) {
    recordEvent({
      action: 'upload_project.create_failed',
      status: 'error',
      provider: detectProvider(),
      message: String(error),
    });
    return NextResponse.json({ error: String(error) }, { status: 500 });
  }
}
