/**
 * نمایش امن متن مقاله‌های مجله (و پاسخ‌های دستیار هوشمند).
 *
 * Markdown ساده مستقیماً به المان‌های React تبدیل می‌شود (بدون HTML خام، پس اسکریپت تزریقی اجرا نمی‌شود):
 *   ## تیتر ۲      ### تیتر ۳      - فهرست      1. فهرست شماره‌دار      > نقل‌قول
 *   **پررنگ**  *کج*  [متن لینک](/p/13)  ![توضیح عکس](آدرس عکس)
 * بلوک‌های ویژه (هر کدام در یک خط جدا):
 *   [[product:13]]              کارت محصول با دکمه خرید
 *   [[cta:مشاهده ضدآفتاب‌ها|/c/مراقبت-پوست/ضدآفتاب]]   دکمه دعوت به اقدام
 * لینک‌ها فقط به آدرس داخلی (/...) یا http(s) مجازند؛ لینک بیرونی با nofollow و در تب جدید باز می‌شود.
 */
import Link from "next/link";
import type { ReactNode } from "react";
import { imageSrc, imageSrcSet } from "./media";
import { slugifyFa } from "./persian";

export type Block =
  | { type: "h2" | "h3"; text: string; id: string }
  | { type: "p"; text: string }
  | { type: "ul" | "ol"; items: string[] }
  | { type: "quote"; text: string }
  | { type: "img"; alt: string; src: string }
  | { type: "product"; id: number }
  | { type: "cta"; label: string; href: string };

const safeHref = (href: string) => (/^\/(?!\/)[^\s]*$/.test(href) || /^https?:\/\/[^\s]+$/i.test(href) ? href : undefined);

/** متن Markdown را به بلوک‌ها تقسیم می‌کند (برای نمایش، فهرست مطالب و بررسی سئو) */
export function parseArticle(md: string): Block[] {
  const blocks: Block[] = [];
  const lines = md.replace(/\r\n?/g, "\n").split("\n");
  const usedIds = new Set<string>();
  let para: string[] = [];
  let list: { type: "ul" | "ol"; items: string[] } | null = null;

  const flush = () => {
    if (para.length) blocks.push({ type: "p", text: para.join(" ") });
    if (list) blocks.push(list);
    para = [];
    list = null;
  };
  const headingId = (text: string) => {
    let id = slugifyFa(text) || "section";
    for (let n = 2; usedIds.has(id); n++) id = `${slugifyFa(text)}-${n}`;
    usedIds.add(id);
    return id;
  };

  for (const raw of lines) {
    const line = raw.trim();
    let m: RegExpMatchArray | null;
    if (!line) {
      flush();
    } else if ((m = line.match(/^(#{2,3})\s+(.+)$/))) {
      flush();
      blocks.push({ type: m[1].length === 2 ? "h2" : "h3", text: m[2], id: headingId(m[2]) });
    } else if ((m = line.match(/^\[\[product:(\d{1,9})\]\]$/))) {
      flush();
      blocks.push({ type: "product", id: Number(m[1]) });
    } else if ((m = line.match(/^\[\[cta:([^|\]]{1,60})\|([^\]\s]{1,200})\]\]$/))) {
      flush();
      const href = safeHref(m[2]);
      if (href) blocks.push({ type: "cta", label: m[1].trim(), href });
    } else if ((m = line.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/))) {
      flush();
      const src = safeHref(m[2]);
      if (src) blocks.push({ type: "img", alt: m[1], src });
    } else if ((m = line.match(/^>\s?(.*)$/))) {
      flush();
      blocks.push({ type: "quote", text: m[1] });
    } else if ((m = line.match(/^[-*]\s+(.+)$/)) || (m = line.match(/^\d+[.)]\s+(.+)$/))) {
      const type = /^\d/.test(line) ? "ol" : "ul";
      if (para.length) {
        blocks.push({ type: "p", text: para.join(" ") });
        para = [];
      }
      if (!list || list.type !== type) {
        if (list) blocks.push(list);
        list = { type, items: [] };
      }
      list.items.push(m[1]);
    } else {
      if (list) {
        blocks.push(list);
        list = null;
      }
      para.push(line);
    }
  }
  flush();
  return blocks;
}

/** متن ساده بدون علائم Markdown (برای خلاصه خودکار، تعداد کلمات و بررسی کلمه کلیدی) */
export function plainText(md: string) {
  return parseArticle(md)
    .flatMap((b) => ("text" in b ? [b.text] : "items" in b ? b.items : []))
    .join(" ")
    .replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1")
    .replace(/\*\*?|__?/g, "")
    .replace(/\s+/g, " ")
    .trim();
}

/** قالب‌بندی درون خطی: پررنگ، کج و لینک */
export function Inline({ text }: { text: string }) {
  const out: ReactNode[] = [];
  const re = /\*\*(.+?)\*\*|\*(.+?)\*|\[([^\]]+)\]\(([^)\s]+)\)/g;
  let last = 0;
  let k = 0;
  for (const m of text.matchAll(re)) {
    if (m.index! > last) out.push(text.slice(last, m.index));
    if (m[1] !== undefined) out.push(<strong key={k++}>{m[1]}</strong>);
    else if (m[2] !== undefined) out.push(<em key={k++}>{m[2]}</em>);
    else {
      const href = safeHref(m[4]);
      if (!href) out.push(m[3]);
      else if (href.startsWith("/"))
        out.push(
          <Link key={k++} href={href} className="font-medium underline underline-offset-4">
            {m[3]}
          </Link>,
        );
      else
        out.push(
          <a key={k++} href={href} target="_blank" rel="noopener noreferrer nofollow" className="font-medium underline underline-offset-4">
            {m[3]}
          </a>,
        );
    }
    last = m.index! + m[0].length;
  }
  if (last < text.length) out.push(text.slice(last));
  return <>{out}</>;
}

/** عکس داخل مقاله؛ عکس‌های آپلودی سایت (آدرس پایه بدون پسوند) با srcset نمایش داده می‌شوند */
export function ArticleImage({ src, alt, priority }: { src: string; alt: string; priority?: boolean }) {
  const isBase = !/\.(webp|jpe?g|png|gif|avif)$/i.test(src);
  return (
    // eslint-disable-next-line @next/next/no-img-element
    <img
      src={isBase ? imageSrc(src, 1600) : src}
      srcSet={isBase ? imageSrcSet(src) : undefined}
      sizes="(min-width: 640px) 560px, 100vw"
      alt={alt}
      loading={priority ? "eager" : "lazy"}
      fetchPriority={priority ? "high" : undefined}
      decoding="async"
      width={800}
      height={800}
      className="mx-auto aspect-square w-full max-w-[560px] rounded-2xl bg-mist object-contain"
    />
  );
}

/** رندر بلوک‌ها؛ کارت محصول از بیرون (با داده واقعی) داده می‌شود */
export function ArticleBody({ blocks, renderProduct }: { blocks: Block[]; renderProduct?: (id: number) => ReactNode }) {
  return (
    <>
      {blocks.map((b, i) => {
        switch (b.type) {
          case "h2":
            return <h2 key={i} id={b.id} className="mt-10 scroll-mt-28 text-xl font-extrabold md:text-2xl"><Inline text={b.text} /></h2>;
          case "h3":
            return <h3 key={i} id={b.id} className="mt-8 scroll-mt-28 text-lg font-bold"><Inline text={b.text} /></h3>;
          case "p":
            return <p key={i} className="mt-4 leading-8 text-ink-soft"><Inline text={b.text} /></p>;
          case "ul":
          case "ol": {
            const Tag = b.type;
            return (
              <Tag key={i} className={`mt-4 space-y-2 ps-6 leading-8 text-ink-soft ${b.type === "ul" ? "list-disc" : "list-decimal"}`}>
                {b.items.map((it, j) => <li key={j}><Inline text={it} /></li>)}
              </Tag>
            );
          }
          case "quote":
            return <blockquote key={i} className="mt-6 border-s-2 border-ink ps-4 text-lg leading-8 font-medium"><Inline text={b.text} /></blockquote>;
          case "img":
            return (
              <figure key={i} className="mt-6">
                <ArticleImage src={b.src} alt={b.alt} />
                {b.alt && <figcaption className="mt-2 text-center text-xs text-muted">{b.alt}</figcaption>}
              </figure>
            );
          case "product":
            return <div key={i} className="mt-6">{renderProduct?.(b.id)}</div>;
          case "cta":
            return (
              <p key={i} className="mt-6 text-center">
                {b.href.startsWith("/") ? (
                  <Link href={b.href} className="btn-primary inline-flex h-12 px-8">{b.label}</Link>
                ) : (
                  <a href={b.href} target="_blank" rel="noopener noreferrer nofollow" className="btn-primary inline-flex h-12 px-8">{b.label}</a>
                )}
              </p>
            );
        }
      })}
    </>
  );
}

/** شناسه محصولاتی که داخل متن آمده‌اند (برای خواندن یک‌جای داده) */
export const productIdsIn = (blocks: Block[]) => blocks.flatMap((b) => (b.type === "product" ? [b.id] : []));

/** زمان تقریبی مطالعه (دقیقه)؛ حدود ۲۰۰ کلمه فارسی در دقیقه */
export const readingMinutes = (md: string) => Math.max(1, Math.round(plainText(md).split(" ").filter(Boolean).length / 200));
