import { randomUUID } from "crypto";
import { generateImage, generateText, PostAiUsageTracker } from "@/lib/ai";
import {
  buildProjectContextPayload,
  buildTextSystemPrompt,
  buildTextUserPrompt,
} from "@/lib/context/build-project-context";
import { buildSocialImageContext, buildContextualImagePrompt } from "@/lib/generation/social-image";
import { generateImageBrief } from "@/lib/generation/image-brief";
import { buildCreativeMediaSummary } from "@/lib/generation/creative-media-summary";
import { persistGeneratedImage } from "@/lib/storage/media-files";
import { generateSlideshowVideo } from "@/lib/generation/slideshow-video";
import { buildMasterContextForGeneration } from "@/lib/context/build-full-project-context";
import {
  buildImageGenerationContext,
  formatImageContextPrompt,
} from "@/lib/context/builders/image-context-builder";
import { finalizePostImage } from "@/lib/generation/finalize-post-image";
import { applyHeroOverlayToPersistedImage } from "@/lib/generation/hero-overlay";
import { applyLogoToPersistedImage } from "@/lib/generation/logo-overlay";
import { scoreEngagement } from "@/lib/context/scoring";
import {
  reviewCaptionQuality,
  qualityScoreToEngagement,
} from "@/lib/generation/caption-quality-review";
import {
  evaluateGenerationQa,
  getQaThreshold,
  qaScoreToEngagement,
} from "@/lib/generation/generation-qa";
import { buildBannerScenePlan } from "@/lib/generation/scene-planner";
import { buildPostBannerContent } from "@/lib/generation/banner-post-content";
import { resolveBrandVisualStyle } from "@/lib/generation/brand-visual-style";
import type { BannerLayoutId } from "@/lib/generation/banner-layouts";
import { trackGenerationAnalytics } from "@/lib/analytics/regeneration-analytics";
import {
  formatProductConfigForPrompt,
  resolveDynamicProductConfig,
} from "@/lib/generation/dynamic-product-config";
import {
  getPostDisplayLabel,
  getSequenceContent,
} from "@/lib/context/format-sequence-context";
import { parseStoryContextSections } from "@/lib/context/story-context-builder";
import { resolveConnectedPublishPlatforms } from "@/lib/social/publish-platforms";
import {
  createMediaAssetId,
  postLayoutSeed,
  withMediaCacheBust,
} from "@/lib/storage/media-asset-id";
import { saveGeneratedPost, getNextVersionForSequence, getGeneratedPost, listGeneratedPosts } from "@/lib/storage/generated-posts";
import { logGeneration, truncateForLog } from "@/lib/logging/generation-logger";
import type { GeneratePostRequest, GeneratedPost } from "@/types/workflow";
import {
  buildPublishedContentAvoidanceBlock,
  buildPublishingMetadataBlock,
} from "@/lib/generation/content-strategist";

export interface GenerationOptions {
  userId?: string;
  /** @deprecated Use replacePostId for in-place regenerate — no longer creates child rows. */
  parentPostId?: string | null;
  /** When set, regenerate updates this post ID instead of inserting a duplicate. */
  replacePostId?: string | null;
  /** Preserve createdAt from the original post on in-place regenerate. */
  preserveCreatedAt?: string | null;
}

export async function runPostGeneration(
  input: GeneratePostRequest,
  options: GenerationOptions = {},
): Promise<GeneratedPost> {
  if (input.project.status !== "active") {
    throw new Error("Project is paused. Activate the project to generate content.");
  }

  if (!input.context.posts.length) {
    throw new Error("Posting context has no sequence items. Add posts first.");
  }

  const sequenceItem = input.context.posts.find((p) => p.id === input.sequencePostId);
  if (!sequenceItem) {
    throw new Error("Sequence post not found in posting context.");
  }

  const sequenceIndex = input.context.posts.findIndex((p) => p.id === input.sequencePostId) + 1;

  const contextInput = {
    projectId: input.projectId,
    project: input.project,
    brand: input.brand,
    context: input.context,
    sequenceItem,
    schedule: input.schedule,
    automation: input.automation,
    accounts: input.accounts,
  };

  const { master, website, beatIndex, creativeBrief } = await buildMasterContextForGeneration(contextInput);

  const contextText = creativeBrief;

  if (!contextText) {
    throw new Error("Context preview is empty. Set website URL and brand profile first.");
  }

  const fullContext = master.markdown;

  const contextSnapshot = buildProjectContextPayload({
    projectId: input.projectId,
    sequenceItem,
    fullContext,
  });

  const accountList = input.accounts ?? [];
  const platforms =
    resolveConnectedPublishPlatforms(accountList).length > 0
      ? resolveConnectedPublishPlatforms(accountList)
      : input.connectedPlatforms.length > 0
        ? input.connectedPlatforms
        : ["linkedin", "facebook"];

  const sequenceTitle =
    sequenceItem.title || getPostDisplayLabel(sequenceItem, sequenceIndex - 1);
  const storySections = parseStoryContextSections(contextText);
  const productConfig = resolveDynamicProductConfig({
    brand: input.brand,
    project: input.project,
    website,
    sequenceTitle,
    problem: storySections.problem,
    impact: storySections.impact,
    brandResponse: storySections.brandResponse,
  });

  const isInPlaceUpdate = Boolean(options.replacePostId?.trim());
  const postId = options.replacePostId?.trim() || randomUUID();
  const priorPostEarly = isInPlaceUpdate ? await getGeneratedPost(postId) : null;
  const priorProjectPosts = await listGeneratedPosts(
    input.projectId,
    input.project.name,
    options.userId,
  );
  const publishedAvoidanceBlock = buildPublishedContentAvoidanceBlock(
    priorProjectPosts,
    postId,
  );
  const targetMarkets = input.project.countries?.length
    ? input.project.countries.join(", ")
    : undefined;
  const excludeLayoutId = (priorPostEarly?.bannerLayoutId as BannerLayoutId | undefined) ?? null;
  const mediaAssetId = createMediaAssetId(postId);
  const forceFreshVisuals = true;
  const usageTracker = new PostAiUsageTracker({
    postId,
    projectId: input.projectId,
  });

  await logGeneration({
    level: "INFO",
    event: "generation_started",
    postId,
    projectId: input.projectId,
    message: `${isInPlaceUpdate ? "Updating" : "Generating"} post for ${input.project.name} — ${sequenceTitle}`,
    details: {
      sequencePostId: sequenceItem.id,
      sequenceIndex,
      platforms,
      inPlaceUpdate: isInPlaceUpdate,
      replacePostId: options.replacePostId ?? null,
      mediaAssetId,
      excludeLayoutId,
      forceFreshVisuals,
      storage: process.env.STORAGE_BACKEND || "auto",
      websiteUrl: input.project.website || null,
      websiteScraped: Boolean(website?.ok),
      scrapedSummary: website?.ok
        ? truncateForLog(website.analysisMarkdown || website.primaryService || "", 1200)
        : null,
      brand: {
        companyName: input.brand.companyName || input.project.name,
        tagline: input.brand.tagline || null,
        contactPhone: input.brand.contactPhone || null,
        contactEmail: input.brand.contactEmail || null,
      },
      product: {
        solutionCategory: productConfig.solutionCategory,
        solutionName: productConfig.solutionName,
        primaryHeadline: productConfig.primaryHeadline,
      },
    },
  });

  await logGeneration({
    level: "INFO",
    event: "content_strategy",
    postId,
    projectId: input.projectId,
    message: `Content category: ${storySections.contentCategory || "default"} · prior posts checked: ${priorProjectPosts.length}`,
    details: {
      contentCategory: storySections.contentCategory,
      seoKeywords: storySections.seoKeywords,
      publishedAvoidancePreview: truncateForLog(publishedAvoidanceBlock, 800),
      publishingMetadata: buildPublishingMetadataBlock({
        contentCategory: storySections.contentCategory || "B2B educational",
        seoKeywords: storySections.seoKeywords
          ? storySections.seoKeywords.split(",").map((k) => k.trim())
          : [],
        platforms,
        sequenceIndex,
        beatTitle: sequenceTitle,
      }),
    },
  });

  const captionSystem = buildTextSystemPrompt({
    companyName: input.brand.companyName || input.project.name,
    industry: input.brand.industry || input.project.industry,
    brandVoice: input.brand.brandVoice,
    tagline: input.brand.tagline,
    platforms: [...platforms, "youtube"],
    targetMarkets,
  });
  const captionUser = buildTextUserPrompt(contextText, {
    companyName: input.brand.companyName || input.project.name,
    industry: input.brand.industry || input.project.industry,
    projectName: input.project.name,
    tagline: input.brand.tagline,
    sequenceTitle,
    fullContext,
    productConfigBlock: formatProductConfigForPrompt(productConfig),
    publishedAvoidanceBlock,
    targetMarkets,
  });

  await logGeneration({
    level: "INFO",
    event: "caption_prompt",
    postId,
    projectId: input.projectId,
    message: "Caption prompts prepared",
    details: {
      systemPrompt: truncateForLog(captionSystem, 2500),
      userPrompt: truncateForLog(captionUser, 4000),
    },
  });

  const textResult = await generateText(
    {
      systemPrompt: captionSystem,
      userPrompt: captionUser,
    },
    { operation: "caption", tracker: usageTracker },
  );

  await logGeneration({
    level: "SUCCESS",
    event: "caption_generated",
    postId,
    projectId: input.projectId,
    message: `Caption ready (${textResult.provider}/${textResult.model})`,
    details: {
      captionPreview: truncateForLog(textResult.content, 800),
      wordCount: textResult.content.split(/\s+/).filter(Boolean).length,
    },
  });

  // Soft-trim oversized captions so Live Generation stays promotional.
  let captionContent = textResult.content.trim();
  const wordCount = captionContent.split(/\s+/).filter(Boolean).length;
  if (wordCount > 180) {
    const paragraphs = captionContent.split(/\n\n+/);
    const kept = paragraphs.slice(0, 4).join("\n\n");
    const hashLine = captionContent
      .split("\n")
      .filter((l) => l.trim().startsWith("#"))
      .join(" ");
    captionContent = hashLine && !kept.includes("#") ? `${kept}\n\n${hashLine}` : kept;
    await logGeneration({
      level: "WARNING",
      event: "caption_trimmed",
      postId,
      projectId: input.projectId,
      message: `Caption trimmed from ${wordCount} words for promo length`,
    });
  }

  const companyName = input.brand.companyName || input.project.name;
  const industry = input.brand.industry || input.project.industry || "B2B";
  const qaThreshold = getQaThreshold();

  let qualityReview = reviewCaptionQuality({
    caption: captionContent,
    companyName,
    industry,
  });
  let captionRetried = false;
  let captionAttempts = 1;

  if (qualityReview.overallOutOf10 < qaThreshold) {
    await logGeneration({
      level: "WARNING",
      event: "caption_quality_retry",
      postId,
      projectId: input.projectId,
      message: `Caption ${qualityReview.overallOutOf10}/10 < ${qaThreshold} — regenerating once`,
      details: {
        needsImprovement: qualityReview.needsImprovement.slice(0, 5),
      },
    });

    const retryUser = [
      captionUser,
      "",
      "## Quality gate — rewrite required",
      `Previous draft scored ${qualityReview.overallOutOf10}/10 (minimum ${qaThreshold}).`,
      "Fix these issues while keeping the same topic and brand:",
      ...qualityReview.needsImprovement.slice(0, 5).map((item) => `- ${item}`),
      "",
      "Write a completely new caption. Do not reuse previous paragraphs.",
    ].join("\n");

    const retryResult = await generateText(
      {
        systemPrompt: captionSystem,
        userPrompt: retryUser,
      },
      { operation: "caption", tracker: usageTracker },
    );
    captionContent = retryResult.content.trim();
    captionRetried = true;
    captionAttempts = 2;
    qualityReview = reviewCaptionQuality({
      caption: captionContent,
      companyName,
      industry,
    });
  }

  await logGeneration({
    level: qualityReview.overallOutOf10 >= qaThreshold ? "INFO" : "WARNING",
    event: "caption_quality_review",
    postId,
    projectId: input.projectId,
    message: `Caption quality ${qualityReview.overallOutOf10}/10`,
    details: {
      overall: qualityReview.overallOutOf10,
      scores: qualityReview.scores,
      summary: qualityReview.executiveSummary,
      needsImprovement: qualityReview.needsImprovement.slice(0, 5),
      reviewPreview: truncateForLog(qualityReview.formattedReview, 1200),
      captionRetried,
      captionAttempts,
      threshold: qaThreshold,
    },
  });

  const category = sequenceItem.tag?.trim() || "Social post";

  const imageBrief = await generateImageBrief({
    caption: captionContent,
    companyName,
    industry,
    category,
    sequenceTitle,
    problem: storySections.problem,
    impact: storySections.impact,
    tagline: input.brand.tagline,
    keyTheme: storySections.keyTheme,
    brandResponse: storySections.brandResponse,
    engagementQuestion: storySections.engagementQuestion,
    contentCategory: storySections.contentCategory,
    seoKeywords: storySections.seoKeywords,
    websiteSummary: website?.ok ? website.analysisMarkdown : undefined,
    tracker: usageTracker,
  });

  const layoutSeed = postLayoutSeed({
    mediaAssetId,
    sequencePostId: sequenceItem.id,
    sequenceIndex,
    caption: captionContent,
  });

  const postBannerContent = buildPostBannerContent({
    caption: captionContent,
    storySections,
    imageBrief,
    sequenceTitle,
    sequenceIndex,
    layoutSeed,
    productHeadline: productConfig.primaryHeadline,
    productSubheadline: productConfig.subheadline,
    solutionCategory: productConfig.solutionCategory,
    brandFeatures: resolveBrandVisualStyle(
      input.brand,
      input.project,
      storySections,
      website,
    ).features,
  });

  const scenePlan = buildBannerScenePlan({
    layoutSeed,
    sequenceIndex,
    industry: input.brand.industry || input.project.industry,
    solutionCategory: productConfig.solutionCategory,
    visualHint: productConfig.modernVisualDirection,
    visualHook: postBannerContent.visualHook,
    headline: postBannerContent.headline,
    caption: captionContent,
    companyName: input.brand.companyName || input.project.name,
    problemTheme: storySections.problem,
    subheadline: postBannerContent.subheadline,
    excludeLayoutId,
  });
  const bannerLayoutId = scenePlan.layoutId;

  await logGeneration({
    level: "INFO",
    event: "banner_scene_planned",
    postId,
    projectId: input.projectId,
    message: `Banner scene: ${scenePlan.layoutId} / ${scenePlan.sceneTitle}`,
    details: {
      layoutSeed,
      bannerLayoutId,
      sceneTitle: scenePlan.sceneTitle,
      designStyle: scenePlan.designStyle,
      headline: postBannerContent.headline,
      featureLabels: postBannerContent.features.map((f) => f.label),
    },
  });

  const socialImageContext = buildSocialImageContext({
    project: input.project,
    brand: input.brand,
    sequenceItem,
    caption: captionContent,
    brief: imageBrief,
    storySections,
    website,
    layoutSeed,
    forceFreshVisuals,
    scenePlan,
    bannerLayoutId,
    postBannerContent,
    sequenceIndex,
    mediaAssetId,
  });
  socialImageContext.mediaAssetId = mediaAssetId;

  const mediaSummary = buildCreativeMediaSummary({
    project: input.project,
    brand: input.brand,
    website,
    storySections,
    brandStyle: socialImageContext.brandStyle,
    captionExcerpt: captionContent.slice(0, 280),
  });

  const imageIntel = buildImageGenerationContext(
    master,
    storySections,
    captionContent.slice(0, 280),
  );
  const imageIntelPrompt = formatImageContextPrompt(imageIntel);

  const imagePrompt = [
    buildContextualImagePrompt(
      socialImageContext,
      sequenceItem,
      storySections,
      mediaSummary.imagePromptBrief,
    ),
    "",
    "## Visual intelligence",
    imageIntelPrompt,
    "",
    "## Master creative summary (company URL + brand)",
    mediaSummary.imagePromptBrief,
  ].join("\n");

  await logGeneration({
    level: "INFO",
    event: "image_prompt",
    postId,
    projectId: input.projectId,
    message: "Image prompt prepared",
    details: {
      imagePrompt: truncateForLog(imagePrompt, 5000),
      mediaSummary: truncateForLog(mediaSummary.imagePromptBrief, 1500),
      headline: socialImageContext.headline,
      preferBrandedTemplate: Boolean(
        socialImageContext.logoDataUrl ||
          socialImageContext.brandStyle.website ||
          socialImageContext.brandStyle.contactPhone,
      ),
      brandLock: {
        tagline: socialImageContext.brandStyle.tagline,
        website: socialImageContext.brandStyle.website,
        phone: socialImageContext.brandStyle.contactPhone || null,
        email: socialImageContext.brandStyle.contactEmail || null,
      },
      websiteScraped: Boolean(website?.ok),
    },
  });

  const imageResult = await generateImage(imagePrompt, socialImageContext, usageTracker);

  await logGeneration({
    level: "SUCCESS",
    event: "image_generated",
    postId,
    projectId: input.projectId,
    message: `Image ready via ${imageResult.provider} (${imageResult.providerChain ?? ""})`,
    details: {
      warning: imageResult.warning,
      hasUrl: Boolean(imageResult.imageUrl),
      mediaAssetId,
      layoutSeed,
      forceFreshVisuals,
      provider: imageResult.provider,
      providerChain: imageResult.providerChain,
      brandTemplateComposed: Boolean(imageResult.brandTemplateComposed),
    },
  });

  const version = isInPlaceUpdate
    ? await getNextVersionForSequence(input.projectId, sequenceItem.id, postId)
    : await getNextVersionForSequence(
        input.projectId,
        sequenceItem.id,
        options.parentPostId,
      );

  const aiUsage = usageTracker.summary();

  const persistedImageUrl = await persistGeneratedImage(
    mediaAssetId,
    imageResult.imageUrl,
  );
  // Rasterize first — never skip the real brand logo when one is uploaded.
  const imageRaster = await finalizePostImage({
    postId,
    mediaAssetId,
    imageApiUrl: persistedImageUrl,
    logoDataUrl: null,
    skipLogoOverlay: true,
  });
  const imageWithHero = await applyHeroOverlayToPersistedImage({
    imageApiUrl: imageRaster,
    heroFilePath: imageResult.heroImageFilePath,
    assetId: mediaAssetId,
    layoutSeed,
    layoutId: bannerLayoutId,
  });
  // Always stamp the exact uploaded logo last (opaque wipe + real asset).
  if (!input.brand.logoDataUrl?.trim()) {
    console.warn("[PostSync] No brand logo uploaded — banner may show AI-invented mark");
  }
  const imageWithBrandLogo = await applyLogoToPersistedImage({
    postId: mediaAssetId,
    imageApiUrl: imageWithHero,
    logoDataUrl: input.brand.logoDataUrl,
    logoPlacement: "top_right",
    wipeColor: socialImageContext.primaryColor || socialImageContext.brandStyle.primaryColor,
  });
  const imageWithLogo = withMediaCacheBust(imageWithBrandLogo, mediaAssetId);

  const videoResult = await generateSlideshowVideo({
    postId,
    mediaAssetId,
    imageUrl: imageWithBrandLogo,
    project: input.project,
    brand: input.brand,
    sequenceItem,
    caption: captionContent,
    storySections,
    sequenceIndex,
    marketingContext: getSequenceContent(sequenceItem),
    websiteAnalysis: [
      website?.ok ? website.analysisMarkdown : "",
      mediaSummary.videoPromptBrief,
    ]
      .filter(Boolean)
      .join("\n\n"),
    socialImageContext,
  });
  const videoUrl = withMediaCacheBust(videoResult.videoUrl, mediaAssetId);

  await logGeneration({
    level: "INFO",
    event: "video_prompt",
    postId,
    projectId: input.projectId,
    message: `Video status: ${videoResult.videoStatus ?? "unknown"}`,
    details: {
      videoPrompt: truncateForLog(videoResult.videoPrompt, 3000),
      videoSummary: truncateForLog(mediaSummary.videoPromptBrief, 1200),
      videoUrl,
      mediaAssetId,
      warning: videoResult.videoWarning,
      websiteScraped: Boolean(website?.ok),
      scrapedSummary: website?.ok
        ? truncateForLog(website.analysisMarkdown || "", 800)
        : null,
    },
  });

  const priorPost = priorPostEarly;
  const regenerationCount = isInPlaceUpdate
    ? (priorPost?.regenerationCount ?? Math.max(0, (priorPost?.version ?? 1) - 1)) + 1
    : 0;

  const qaReport = evaluateGenerationQa({
    caption: captionContent,
    companyName,
    industry,
    master,
    socialContext: socialImageContext,
    imageUrl: imageWithLogo,
    imageProvider: imageResult.provider,
    brandTemplateComposed: Boolean(imageResult.brandTemplateComposed),
    previousLayoutId: excludeLayoutId,
    videoStatus: videoResult.videoStatus,
    videoUrl,
    videoSlideCount: videoResult.videoSlideCount,
    videoStoryboard: videoResult.videoStoryboard,
    videoWarning: videoResult.videoWarning,
    layoutSeed,
    mediaAssetId,
    attempts: captionAttempts,
    captionRetried,
    threshold: qaThreshold,
  });

  await logGeneration({
    level: qaReport.passed ? "SUCCESS" : "WARNING",
    event: "generation_qa",
    postId,
    projectId: input.projectId,
    message: `Generation QA ${qaReport.scores.overall}/10 (${qaReport.passed ? "passed" : "below threshold"})`,
    details: {
      scores: qaReport.scores,
      passed: qaReport.passed,
      threshold: qaReport.threshold,
      layoutId: qaReport.layoutId,
      mediaAssetId,
      issues: qaReport.issues,
      improvements: qaReport.improvements,
      captionRetried,
    },
  });

  const qualitySnapshot = {
    scores: qaReport.scores,
    passed: qaReport.passed,
    threshold: qaReport.threshold,
    layoutId: qaReport.layoutId,
    layoutSeed: qaReport.layoutSeed,
    mediaAssetId: qaReport.mediaAssetId,
    issues: qaReport.issues,
    improvements: qaReport.improvements,
    attempts: qaReport.attempts,
    captionRetried: qaReport.captionRetried,
  };

  const post: GeneratedPost = {
    id: postId,
    userId: options.userId,
    projectId: input.projectId,
    projectName: input.project.name,
    contextName: input.context.name,
    sequencePostId: sequenceItem.id,
    sequenceIndex,
    // In-place regenerate keeps a single record — no parent/child duplicates.
    parentPostId: isInPlaceUpdate ? null : (options.parentPostId ?? null),
    version,
    title: getPostDisplayLabel(sequenceItem, sequenceIndex - 1),
    caption: captionContent,
    imageUrl: imageWithLogo,
    imagePrompt: imageResult.imagePrompt,
    imageProvider: imageResult.provider,
    imageProviderChain: imageResult.providerChain ?? undefined,
    imageWarning: imageResult.warning ?? null,
    imageApproved: false,
    videoUrl,
    videoPrompt: videoResult.videoPrompt,
    videoStoryboard: videoResult.videoStoryboard,
    videoStatus: videoResult.videoStatus,
    videoDurationSeconds: videoResult.videoDurationSeconds,
    videoResolution: videoResult.videoResolution,
    videoWarning: videoResult.videoWarning ?? null,
    platforms,
    status: "draft",
    provider: textResult.provider,
    model: textResult.model,
    contextSnapshot,
    engagementScore: Math.max(
      scoreEngagement(captionContent),
      qualityScoreToEngagement(qualityReview.overallOutOf10),
      qaScoreToEngagement(qaReport.scores.overall),
    ),
    aiCostUsd: aiUsage.totalCostUsd,
    aiUsage,
    qualityReport: qualitySnapshot,
    mediaAssetId,
    bannerLayoutId,
    regenerationCount,
    createdAt: options.preserveCreatedAt || new Date().toISOString(),
    // Content changed — clear prior publish markers so schedule/publish re-validate.
    publishedAt: undefined,
    platformPermalinks: undefined,
  };

  const saved = await saveGeneratedPost(post);

  await trackGenerationAnalytics({
    type: isInPlaceUpdate ? "regenerate" : "generate",
    postId: saved.id,
    projectId: input.projectId,
    industry,
    mediaAssetId,
    layoutId: qaReport.layoutId,
    layoutSeed,
    ctaLabel: socialImageContext.brandStyle.ctaLine || "",
    qaScores: qaReport.scores,
    qaPassed: qaReport.passed,
    version: saved.version,
    imageUrl: saved.imageUrl,
    videoUrl: saved.videoUrl ?? null,
    videoWatchPercentage: null,
  });

  await logGeneration({
    level: "SUCCESS",
    event: "generation_saved",
    postId: saved.id,
    projectId: input.projectId,
    message: `Draft saved (v${saved.version}) — ${saved.title}`,
    details: {
      status: saved.status,
      imageProvider: saved.imageProvider,
      platforms: saved.platforms,
      imageUrl: saved.imageUrl,
      videoUrl: saved.videoUrl,
      mediaAssetId,
      layoutSeed,
      layoutId: saved.bannerLayoutId,
      qaOverall: qaReport.scores.overall,
      qaPassed: qaReport.passed,
      regenerationCount,
    },
  });

  return saved;
}

export async function runNextSequenceGeneration(
  input: Omit<GeneratePostRequest, "sequencePostId">,
  options: GenerationOptions = {},
): Promise<GeneratedPost> {
  const posts = input.context.posts;
  if (!posts.length) throw new Error("No posts in sequence.");

  const latest = await import("@/lib/storage/generated-posts").then((m) =>
    m.listGeneratedPosts(input.projectId, input.project.name),
  );

  const lastIndex = latest[0]?.sequenceIndex ?? 0;
  const nextItem = posts[lastIndex % posts.length] ?? posts[0];

  return runPostGeneration({ ...input, sequencePostId: nextItem.id }, options);
}

export async function runPostRegeneration(
  parentPost: GeneratedPost,
  input: GeneratePostRequest,
  userId?: string,
): Promise<GeneratedPost> {
  // Update the same post record — never insert a duplicate child row.
  return runPostGeneration(
    { ...input, sequencePostId: parentPost.sequencePostId },
    {
      userId,
      replacePostId: parentPost.id,
      preserveCreatedAt: parentPost.createdAt,
    },
  );
}
