/**
 * بررسی و بهینه‌سازی خودکار سئو (و GEO: دیده شدن در پاسخ موتورهای هوش مصنوعی) برای مقاله‌های مجله.
 * هم در فرم ویرایش (نمایش زنده) و هم در صفحه «سئو و GEO» پنل (اصلاح گروهی) استفاده می‌شود.
 */
import { parseArticle, plainText } from "./article-markdown";
import { faNumber } from "./format";
import { normalizeFa, slugifyFa } from "./persian";

export interface ArticleSeoInput {
  title: string;
  slug: string;
  excerpt: string;
  body: string;
  coverUrl?: string | null;
  coverAlt?: string | null;
  seoTitle?: string | null;
  seoDescription?: string | null;
  focusKeyword?: string | null;
  faq?: { q: string; a: string }[];
  ctaHref?: string | null;
  productIds?: number[];
}

export type CheckStatus = "good" | "warn" | "bad";
export interface SeoCheck {
  status: CheckStatus;
  label: string;
  /** این مورد با «بهینه‌سازی خودکار» قابل اصلاح است */
  autoFixable?: boolean;
}

export const SEO_TITLE_RANGE = [30, 60] as const;
export const SEO_DESC_RANGE = [120, 160] as const;

/** کوتاه کردن متن تا حداکثر طول، بدون بریدن کلمه */
export function clip(text: string, max: number) {
  const t = text.replace(/\s+/g, " ").trim();
  if (t.length <= max) return t;
  const cut = t.slice(0, max - 1);
  return cut.slice(0, Math.max(cut.lastIndexOf(" "), max * 0.6)).replace(/[،,.؛:\s]+$/, "") + "…";
}

const has = (hay: string, needle: string) => !!needle && normalizeFa(hay).includes(normalizeFa(needle));

export function checkArticle(a: ArticleSeoInput, siteName = "زهورا"): { checks: SeoCheck[]; score: number } {
  const blocks = parseArticle(a.body);
  const text = plainText(a.body);
  const words = text.split(" ").filter(Boolean).length;
  const title = (a.seoTitle || a.title).trim();
  const fullTitle = `${title} | ${siteName}`;
  const desc = (a.seoDescription || a.excerpt).trim();
  const kw = (a.focusKeyword ?? "").trim();
  const firstPara = blocks.find((b) => b.type === "p");
  const imgs = blocks.filter((b) => b.type === "img");
  const internalLinks = (a.body.match(/\]\(\/[^)]*\)/g) ?? []).length + blocks.filter((b) => b.type === "product" || (b.type === "cta" && b.href.startsWith("/"))).length;
  const h2s = blocks.filter((b) => b.type === "h2").length;

  const checks: SeoCheck[] = [];
  const add = (ok: boolean, okLabel: string, badLabel: string, level: CheckStatus = "bad", autoFixable = false) =>
    checks.push(ok ? { status: "good", label: okLabel } : { status: level, label: badLabel, autoFixable });

  add(fullTitle.length >= SEO_TITLE_RANGE[0] && fullTitle.length <= SEO_TITLE_RANGE[1] + 10, `طول تگ عنوان مناسب است (${faNumber(fullTitle.length)} کاراکتر)`, `طول تگ عنوان ${faNumber(fullTitle.length)} کاراکتر است؛ بهتر است بین ۳۰ تا ۶۰ باشد`, "warn", true);
  add(desc.length >= SEO_DESC_RANGE[0] && desc.length <= SEO_DESC_RANGE[1], `طول توضیحات متا مناسب است (${faNumber(desc.length)} کاراکتر)`, `توضیحات متا ${faNumber(desc.length)} کاراکتر است؛ بهتر است ۱۲۰ تا ۱۶۰ باشد`, desc.length < 50 ? "bad" : "warn", true);
  add(/^[\p{L}\p{N}-]+$/u.test(a.slug) && a.slug.length <= 75, "آدرس (URL) کوتاه و خواناست", "آدرس مقاله بلند یا دارای کاراکتر نامناسب است", "warn", true);
  if (kw) {
    add(has(title, kw), "کلمه کلیدی در عنوان آمده است", "کلمه کلیدی در تگ عنوان نیامده است", "warn");
    add(has(desc, kw), "کلمه کلیدی در توضیحات متا آمده است", "کلمه کلیدی در توضیحات متا نیامده است", "warn", true);
    add(!!firstPara && has(firstPara.text, kw), "کلمه کلیدی در پاراگراف اول آمده است", "کلمه کلیدی را در پاراگراف اول بیاورید", "warn");
  } else {
    checks.push({ status: "warn", label: "کلمه کلیدی اصلی تعیین نشده است", autoFixable: true });
  }
  add(words >= 300, `متن مقاله ${faNumber(words)} کلمه است`, `متن مقاله ${faNumber(words)} کلمه است؛ حداقل ۳۰۰ کلمه پیشنهاد می‌شود`, words < 120 ? "bad" : "warn");
  add(h2s >= 2, "مقاله با تیترهای فرعی بخش‌بندی شده است", "حداقل دو تیتر فرعی (##) برای خوانایی و فهرست مطالب اضافه کنید", "warn");
  add(!!a.coverUrl, "عکس کاور دارد", "عکس کاور ندارد (برای نمایش در گوگل و شبکه‌های اجتماعی لازم است)", "warn");
  add(!a.coverUrl || !!a.coverAlt?.trim(), "متن جایگزین عکس کاور نوشته شده است", "متن جایگزین (alt) عکس کاور خالی است", "bad", true);
  add(imgs.every((b) => b.type === "img" && b.alt.trim()), "همه عکس‌های متن alt دارند", "بعضی عکس‌های متن alt ندارند", "bad", true);
  add(internalLinks >= 2, `${faNumber(internalLinks)} لینک داخلی به محصولات یا صفحات سایت`, "حداقل دو لینک داخلی به محصولات یا دسته‌ها بگذارید (برای سئو و فروش)", "warn");
  add(!!a.ctaHref || blocks.some((b) => b.type === "cta"), "دکمه دعوت به اقدام دارد", "دکمه دعوت به اقدام (CTA) ندارد؛ خواننده را به خرید هدایت کنید", "warn");
  add((a.faq?.length ?? 0) >= 2, "بخش پرسش‌های متداول دارد (کمک به GEO)", "حداقل دو پرسش متداول اضافه کنید؛ موتورهای هوش مصنوعی پاسخ‌های کوتاه و دقیق را نقل می‌کنند", "warn");

  const score = Math.round((checks.reduce((s, c) => s + (c.status === "good" ? 1 : c.status === "warn" ? 0.5 : 0), 0) / checks.length) * 100);
  return { checks, score };
}

/**
 * بهینه‌سازی خودکار قاعده‌محور (بدون هوش مصنوعی): فقط فیلدهای خالی یا خارج از اندازه پر/اصلاح می‌شوند
 * و متنی که مدیر نوشته دست نمی‌خورد.
 */
export function autoOptimizeArticle<T extends ArticleSeoInput>(a: T): T {
  const text = plainText(a.body);
  const blocks = parseArticle(a.body);
  const out = { ...a };

  if (!out.slug || out.slug.length > 75) out.slug = slugifyFa(clip(out.title, 60).replace("…", ""));
  if (!out.excerpt?.trim()) out.excerpt = clip(text, 200);
  if (!out.focusKeyword?.trim()) out.focusKeyword = guessKeyword(out.title);
  const desc = (out.seoDescription || "").trim();
  if (desc.length < SEO_DESC_RANGE[0] || desc.length > SEO_DESC_RANGE[1]) {
    let base = desc.length >= 50 ? desc : out.excerpt.length >= 80 ? out.excerpt : text;
    if (out.focusKeyword && !has(base, out.focusKeyword)) base = `${out.focusKeyword}: ${base}`;
    // کوتاه‌تر از حد مناسب: با جمله‌های ابتدای متن کامل می‌شود
    if (base.length < SEO_DESC_RANGE[0]) base = `${base.replace(/[.،\s]+$/, "")}. ${text}`;
    out.seoDescription = clip(base, SEO_DESC_RANGE[1] - 5);
  }
  if (!out.seoTitle?.trim() || out.seoTitle.length > SEO_TITLE_RANGE[1]) out.seoTitle = clip(out.seoTitle?.trim() || out.title, 52);
  if (out.coverUrl && !out.coverAlt?.trim()) out.coverAlt = out.title;
  // alt عکس‌های داخل متن: نزدیک‌ترین تیتر قبل از عکس یا عنوان مقاله
  let heading = out.title;
  const fixed = new Map<string, string>();
  for (const b of blocks) {
    if (b.type === "h2" || b.type === "h3") heading = b.text;
    if (b.type === "img" && !b.alt.trim()) fixed.set(b.src, heading);
  }
  if (fixed.size) out.body = out.body.replace(/!\[\s*\]\(([^)\s]+)\)/g, (m, src: string) => (fixed.has(src) ? `![${fixed.get(src)}](${src})` : m));
  return out;
}

/** کلمه کلیدی پیشنهادی: دو یا سه کلمه اول عنوان بدون کلمات ربط */
const STOP = new Set(["و", "در", "به", "از", "که", "با", "برای", "این", "آن", "را", "تا", "یا", "چه", "چطور", "چگونه", "بهترین", "راهنمای", "کامل"]);
export function guessKeyword(title: string) {
  const words = normalizeFa(title).replace(/[^\p{L}\p{N}\s]/gu, " ").split(" ").filter((w) => w && !STOP.has(w));
  return words.slice(0, 2).join(" ");
}
