/**
 * Post-generation QA governance — scores caption, brand, banner, and video
 * before a draft is considered publish-ready.
 */

import { reviewCaptionQuality, type CaptionQualityReview } from "@/lib/generation/caption-quality-review";
import { scoreHumanWriting } from "@/lib/context/scoring";
import { validateBrandAlignment } from "@/lib/context/validators/brand-validator";
import { validateHumanWriting } from "@/lib/context/validators/human-validator";
import type { SocialImageContext } from "@/lib/generation/social-image";
import type { MasterContext } from "@/lib/context/intelligence/types";
import type { VideoGenerationStatus } from "@/types/workflow";
import {
  resolveBannerLayoutId,
  type BannerLayoutId,
} from "@/lib/generation/banner-layouts";

export const DEFAULT_QA_THRESHOLD = 8;

export interface GenerationQaScores {
  /** Caption content quality (caption rubric overall). */
  contentQuality: number;
  humanLikeness: number;
  brandAlignment: number;
  bannerCompleteness: number;
  videoQuality: number;
  /** Weighted overall 0–10. */
  overall: number;
}

export interface GenerationQaReport {
  scores: GenerationQaScores;
  passed: boolean;
  threshold: number;
  captionReview: CaptionQualityReview;
  layoutId: BannerLayoutId;
  layoutSeed: number;
  mediaAssetId: string;
  issues: string[];
  improvements: string[];
  attempts: number;
  /** True when caption was regenerated because the first draft scored below threshold. */
  captionRetried: boolean;
}

function clamp10(n: number): number {
  return Math.max(0, Math.min(10, Math.round(n * 10) / 10));
}

function toTen(score0to100: number): number {
  return clamp10(score0to100 / 10);
}

export function getQaThreshold(): number {
  const raw = Number(process.env.GENERATION_QA_MIN_SCORE ?? DEFAULT_QA_THRESHOLD);
  if (!Number.isFinite(raw)) return DEFAULT_QA_THRESHOLD;
  return Math.max(1, Math.min(10, raw));
}

/** Structural checklist for 1080×1080 social banners. */
export function scoreBannerCompleteness(input: {
  socialContext: SocialImageContext;
  imageUrl: string | null;
  imageProvider?: string;
  brandTemplateComposed?: boolean;
  previousLayoutId?: BannerLayoutId | null;
}): { score: number; missing: string[] } {
  const ctx = input.socialContext;
  const style = ctx.brandStyle;
  const missing: string[] = [];
  let score = 10;

  if (!input.imageUrl) {
    missing.push("Banner image missing");
    return { score: 0, missing };
  }
  if (!ctx.logoDataUrl) {
    missing.push("Company logo");
    score -= 1.5;
  }
  if (!ctx.headline?.trim()) {
    missing.push("Main heading");
    score -= 1.5;
  }
  if (!ctx.subheadline?.trim() && !style.tagline?.trim()) {
    missing.push("Supporting subheading");
    score -= 1;
  } else if (/\.\.\.|…$/.test((ctx.subheadline || "").trim())) {
    missing.push("Subheading appears truncated");
    score -= 0.5;
  }
  if (!style.features?.length) {
    missing.push("Feature bullets");
    score -= 1.5;
  }
  if (!style.ctaLine?.trim()) {
    missing.push("CTA section");
    score -= 1;
  }
  if (!style.website?.trim()) {
    missing.push("Website URL");
    score -= 0.5;
  }
  if (!style.contactPhone?.trim() && !style.contactEmail?.trim()) {
    missing.push("Phone or email contact");
    score -= 0.5;
  }
  if (!ctx.visualConcept && !style.productVisualHint && !ctx.scenePlan?.sceneDescription) {
    missing.push("Business illustration cue");
    score -= 0.5;
  }
  if (!style.primaryColor || !style.accentColor) {
    missing.push("Brand colors");
    score -= 0.5;
  }

  const layoutId =
    ctx.bannerLayoutId || ctx.scenePlan?.layoutId || resolveBannerLayoutId(ctx.layoutSeed ?? 0);
  if (input.previousLayoutId && layoutId === input.previousLayoutId) {
    missing.push("Layout unchanged from previous generation");
    score -= 1.5;
  }
  // Template+hero is preferred when an exact brand logo is uploaded (avoids AI wordmarks).
  // Only penalize template fallback when full AI was expected and logo was absent.
  if (input.brandTemplateComposed && !ctx.logoDataUrl) {
    missing.push("Used template composite (full AI banner preferred)");
    score -= 1;
  }
  if (input.imageProvider === "placeholder") score = Math.min(10, score + 0.25);

  return { score: clamp10(score), missing };
}

export function scoreVideoQuality(input: {
  videoStatus?: VideoGenerationStatus | null;
  videoUrl?: string | null;
  videoSlideCount?: number;
  videoStoryboard?: string | null;
  videoWarning?: string | null;
}): number {
  const status = input.videoStatus ?? "skipped";
  if (status === "failed") return 3;
  if (status === "skipped" || !input.videoUrl) {
    return clamp10(input.videoWarning ? 4 : 5);
  }

  let score = 7;
  if ((input.videoSlideCount ?? 0) >= 5) score += 1.5;
  else if ((input.videoSlideCount ?? 0) >= 2) score += 0.5;
  if ((input.videoStoryboard?.length ?? 0) > 400) score += 0.8;
  if (input.videoWarning) score -= 1;
  return clamp10(score);
}

export function scoreBrandAlignmentTen(
  caption: string,
  master: MasterContext,
  captionBrandScore: number,
): number {
  const validation = validateBrandAlignment(caption, master);
  let score = captionBrandScore;
  if (!validation.valid) score -= 1.5 * validation.issues.length;
  return clamp10(score);
}

export function scoreHumanLikenessTen(
  caption: string,
  captionHumanScore: number,
): number {
  const human0to100 = scoreHumanWriting(caption);
  const validation = validateHumanWriting(caption);
  let score = (captionHumanScore * 0.55 + toTen(human0to100) * 0.45);
  if (!validation.valid) score -= Math.min(2, validation.issues.length * 0.6);
  return clamp10(score);
}

/**
 * Build a full QA report after caption + media are ready.
 */
export function evaluateGenerationQa(input: {
  caption: string;
  companyName: string;
  industry?: string;
  master: MasterContext;
  socialContext: SocialImageContext;
  imageUrl: string | null;
  imageProvider?: string;
  brandTemplateComposed?: boolean;
  previousLayoutId?: BannerLayoutId | null;
  videoStatus?: VideoGenerationStatus | null;
  videoUrl?: string | null;
  videoSlideCount?: number;
  videoStoryboard?: string | null;
  videoWarning?: string | null;
  layoutSeed: number;
  mediaAssetId: string;
  attempts?: number;
  captionRetried?: boolean;
  threshold?: number;
}): GenerationQaReport {
  const threshold = input.threshold ?? getQaThreshold();
  const captionReview = reviewCaptionQuality({
    caption: input.caption,
    companyName: input.companyName,
    industry: input.industry,
  });

  const banner = scoreBannerCompleteness({
    socialContext: input.socialContext,
    imageUrl: input.imageUrl,
    imageProvider: input.imageProvider,
    brandTemplateComposed: input.brandTemplateComposed,
    previousLayoutId: input.previousLayoutId,
  });
  const videoQuality = scoreVideoQuality({
    videoStatus: input.videoStatus,
    videoUrl: input.videoUrl,
    videoSlideCount: input.videoSlideCount,
    videoStoryboard: input.videoStoryboard,
    videoWarning: input.videoWarning,
  });
  const humanLikeness = scoreHumanLikenessTen(
    input.caption,
    captionReview.scores.humanQuality,
  );
  const brandAlignment = scoreBrandAlignmentTen(
    input.caption,
    input.master,
    captionReview.scores.brandPositioning,
  );
  const contentQuality = captionReview.overallOutOf10;

  const overall = clamp10(
    contentQuality * 0.35 +
      humanLikeness * 0.15 +
      brandAlignment * 0.15 +
      banner.score * 0.2 +
      videoQuality * 0.15,
  );

  const issues: string[] = [];
  const improvements: string[] = [...captionReview.needsImprovement.slice(0, 4)];
  if (contentQuality < threshold) {
    issues.push(`Content quality ${contentQuality}/10 below ${threshold}`);
  }
  if (humanLikeness < threshold) {
    issues.push(`Human-likeness ${humanLikeness}/10 below ${threshold}`);
    improvements.push("Reduce clichés and add one concrete operational scenario.");
  }
  if (brandAlignment < threshold) {
    issues.push(`Brand alignment ${brandAlignment}/10 below ${threshold}`);
    improvements.push(`Mention ${input.companyName} once with a practical capability.`);
  }
  if (banner.score < threshold) {
    issues.push(`Banner completeness ${banner.score}/10 below ${threshold}`);
    improvements.push(...banner.missing.map((m) => `Banner: add ${m}`));
  }
  if (videoQuality < threshold - 1) {
    issues.push(`Video quality ${videoQuality}/10 is weak`);
    improvements.push("Ensure multi-slide video renders successfully for this post.");
  }

  const layoutId =
    input.socialContext.bannerLayoutId ||
    input.socialContext.scenePlan?.layoutId ||
    resolveBannerLayoutId(input.layoutSeed);

  return {
    scores: {
      contentQuality,
      humanLikeness,
      brandAlignment,
      bannerCompleteness: banner.score,
      videoQuality,
      overall,
    },
    passed: overall >= threshold,
    threshold,
    captionReview,
    layoutId,
    layoutSeed: input.layoutSeed,
    mediaAssetId: input.mediaAssetId,
    issues,
    improvements: [...new Set(improvements)].slice(0, 8),
    attempts: input.attempts ?? 1,
    captionRetried: Boolean(input.captionRetried),
  };
}

/** Map QA overall /10 → engagement-style 0–100. */
export function qaScoreToEngagement(overallOutOf10: number): number {
  return Math.max(0, Math.min(98, Math.round(overallOutOf10 * 10)));
}
