import { NextRequest } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { fetchPendingProducts, writeShopifyIdToSheet } from '@/lib/creationSheets' import { generateDescriptions } from '@/lib/openaiClient' import { ensureMetafieldDefinition, setProductMetafield } from '@/lib/shopifyMetafields' import { addProductToCollection } from '@/lib/shopifyCollections' import { createShopifyProduct, fetchAllShopifyProducts } from '@/lib/shopifySync' import { prisma } from '@/lib/prisma' import type { MetafieldResolution, CreationStreamEvent } from '@/types/creation' function encode(event: CreationStreamEvent): string { return JSON.stringify(event) + '\n' } export async function POST(req: NextRequest) { const session = await getServerSession(authOptions) if (!session) { return new Response(JSON.stringify({ error: 'Non authentifié' }), { status: 401 }) } const userId = (session.user as { id: string }).id const body = await req.json().catch(() => ({})) as { resolutions?: MetafieldResolution[]; tab?: string; collectionOverride?: string } const resolutions: MetafieldResolution[] = body.resolutions ?? [] const tabFilter: string | undefined = body.tab ?? undefined const collectionOverride: string | undefined = body.collectionOverride ?? undefined const resolutionMap = new Map(resolutions.map(r => [r.columnHeader, r])) const stream = new ReadableStream({ async start(controller) { const send = (event: CreationStreamEvent) => controller.enqueue(encode(event)) try { const [existingRows, shopifyProducts] = await Promise.all([ prisma.productCreation.findMany({ select: { sheetTab: true, sheetRow: true } }), fetchAllShopifyProducts(), ]) const alreadyCreated = new Set(existingRows.map(e => `${e.sheetTab}:${e.sheetRow}`)) const shopifyRefs = new Set(shopifyProducts.map(p => p.ref.trim().toLowerCase())) const pending = await fetchPendingProducts(alreadyCreated, tabFilter) const total = pending.length if (total === 0) { send({ type: 'done', summary: { created: 0, skipped: 0, errors: 0, status: 'success' } }) controller.close() return } // Crée les définitions manquantes en amont for (const resolution of resolutions) { if (resolution.action === 'create') await ensureMetafieldDefinition(resolution) } let created = 0 let skipped = 0 let errors = 0 for (let i = 0; i < pending.length; i++) { const product = pending[i] // Vérifier si le produit existe déjà dans Shopify par SKU if (shopifyRefs.has(product.sku.trim().toLowerCase())) { send({ type: 'skipped', title: product.title, sku: product.sku }) skipped++ continue } send({ type: 'progress', current: i + 1, total, tab: product.tab, title: product.title, step: 'Génération descriptions…' }) try { const { longDesc } = await generateDescriptions({ title: product.title, rawDescription: '', comment: product.comment, vendor: product.vendor, famille: product.famille, }) send({ type: 'progress', current: i + 1, total, tab: product.tab, title: product.title, step: 'Création produit Shopify…' }) console.log(`[creation] ${product.sku} stock=${product.stock}`) const shopifyId = await createShopifyProduct({ ref: product.sku, handle: product.sku.toLowerCase().replace(/[^a-z0-9]+/g, '-'), title: product.title, description: longDesc, price: product.priceActual || '0.00', stock: product.stock, weightKg: product.weightKg, status: 'active', }) const effectiveCollectionId = product.collectionId || collectionOverride || '' if (effectiveCollectionId) await addProductToCollection(shopifyId, effectiveCollectionId) if (product.subCollectionId) await addProductToCollection(shopifyId, product.subCollectionId) send({ type: 'progress', current: i + 1, total, tab: product.tab, title: product.title, step: 'Métachamps…' }) for (const [colHeader, value] of Object.entries(product.specificFields)) { const resolution = resolutionMap.get(colHeader) if (!resolution || resolution.action === 'skip') continue await setProductMetafield(shopifyId, resolution, value) } await prisma.productCreation.create({ data: { userId, sheetTab: product.tab, sheetRow: product.rowNumber, shopifyId, sku: product.sku, title: product.title, status: 'success' }, }) await writeShopifyIdToSheet(product.tab, product.rowNumber, shopifyId) created++ } catch (err) { const message = err instanceof Error ? err.message : 'Erreur inconnue' send({ type: 'error', title: product.title, message }) await prisma.productCreation.create({ data: { userId, sheetTab: product.tab, sheetRow: product.rowNumber, shopifyId: '', sku: product.sku, title: product.title, status: 'error', errorMessage: message }, }) errors++ } } const status = errors === 0 ? 'success' : created === 0 ? 'error' : 'partial' send({ type: 'done', summary: { created, skipped, errors, status } }) } catch (err) { const message = err instanceof Error ? err.message : 'Erreur inconnue' send({ type: 'done', summary: { created: 0, skipped: 0, errors: 1, status: 'error' } }) console.error('[creation/execute]', message) } finally { controller.close() } }, }) return new Response(stream, { headers: { 'Content-Type': 'application/x-ndjson', 'Transfer-Encoding': 'chunked' }, }) }