// Insights — editorial section: projects / materials / news / inspiration.
// Index page with category filter tabs (URL-shareable via #/insights?cat=...)
// and a per-publication detail page rendered from a small Markdown subset.

const INSIGHT_CATEGORIES = [
  { key: "all",         labelKey: "insights.cat.all" },
  { key: "projects",    labelKey: "insights.cat.projects" },
  { key: "materials",   labelKey: "insights.cat.materials" },
  { key: "news",        labelKey: "insights.cat.news" },
  { key: "inspiration", labelKey: "insights.cat.inspiration" },
];

const _INSIGHTS_PAGE_SIZE = 12;

function _normalizeTags(tags) {
  return (tags || [])
    .map(t => (typeof t === "string" ? t : (t && t.tag) || ""))
    .filter(Boolean);
}

function _categoryLabel(key) {
  const c = INSIGHT_CATEGORIES.find(x => x.key === key);
  return c ? t(c.labelKey) : key;
}

function _formatInsightDate(iso) {
  if (!iso) return "";
  const d = new Date(iso);
  if (isNaN(d.getTime())) return iso;
  const lang = getLang();
  try {
    return d.toLocaleDateString(lang === "en" ? "en-US" : "ru-RU", {
      day: "numeric", month: "long", year: "numeric"
    });
  } catch (_) {
    return iso;
  }
}

function _readingMinutes(insight) {
  if (insight && Number.isFinite(insight.readingTime) && insight.readingTime > 0) {
    return insight.readingTime;
  }
  const body = String((insight && insight.body) || "");
  if (!body) return null;
  const words = body.trim().split(/\s+/).length;
  return Math.max(1, Math.round(words / 180));
}

function _parseRouteParams() {
  const hash = (typeof window !== "undefined" && window.location.hash.slice(1)) || "/";
  const [path, qs] = hash.split("?");
  const params = new URLSearchParams(qs || "");
  return { path, params };
}

// ---------- Markdown subset ----------------------------------------------
// Supports: # H2, ## H3, ### H4 (article title is H1, so the largest body
// heading is H2), paragraphs, ul / ol, blockquote, ![alt](url) images,
// horizontal rules (---), inline **bold**, *italic*, [text](url) links.

// Only http(s), mailto, tel and same-site relative paths (/foo) are accepted.
// Everything else (javascript:, data:, vbscript:, file:, …) is dropped so
// editor-authored Markdown cannot inject script execution sinks.
function _safeUrl(raw) {
  const s = String(raw || "").trim();
  if (!s) return "";
  // strip control chars that some browsers tolerate inside `javascript:`
  const cleaned = s.replace(/[\u0000-\u001f]/g, "");
  if (/^(https?:|mailto:|tel:)/i.test(cleaned)) return cleaned;
  if (/^[#/](?![/\\])/.test(cleaned)) return cleaned; // /relative or #fragment, not protocol-relative //evil.com
  return "";
}
function _safeImg(raw) {
  const s = String(raw || "").trim();
  if (!s) return "";
  if (/^(https?:|data:image\/(png|jpe?g|gif|webp|avif|svg\+xml);)/i.test(s)) return s;
  if (/^[/](?![/\\])/.test(s)) return s;
  return "";
}

function _renderInline(text, keyPrefix) {
  if (!text) return null;
  const out = [];
  const pattern = /(!\[([^\]]*)\]\(([^)]+)\))|(\[([^\]]+)\]\(([^)]+)\))|(\*\*([^*]+)\*\*)|(\*([^*]+)\*)/g;
  let last = 0, m, k = 0;
  while ((m = pattern.exec(text)) !== null) {
    if (m.index > last) out.push(text.slice(last, m.index));
    if (m[1]) {
      const src = _safeImg(m[3]);
      if (src) out.push(<img key={`${keyPrefix}-${k++}`} src={src} alt={m[2] || ""} loading="lazy"/>);
      else out.push(m[2] || "");
    } else if (m[4]) {
      const href = _safeUrl(m[6]);
      if (!href) {
        out.push(m[5]); // drop link entirely if URL is unsafe; keep label text
      } else {
        const external = /^https?:/i.test(href);
        out.push(
          <a
            key={`${keyPrefix}-${k++}`}
            href={external ? href : (href.startsWith("/") || href.startsWith("#") ? `#${href.startsWith("#") ? href.slice(1) : href}` : href)}
            target={external ? "_blank" : undefined}
            rel={external ? "noopener noreferrer" : undefined}
          >{m[5]}</a>
        );
      }
    } else if (m[7]) {
      out.push(<strong key={`${keyPrefix}-${k++}`}>{m[8]}</strong>);
    } else if (m[9]) {
      out.push(<em key={`${keyPrefix}-${k++}`}>{m[10]}</em>);
    }
    last = m.index + m[0].length;
  }
  if (last < text.length) out.push(text.slice(last));
  return out;
}

function renderMarkdown(md) {
  if (!md) return null;
  const text = String(md).replace(/\r\n/g, "\n");
  const lines = text.split("\n");
  const blocks = [];
  let i = 0;

  const isSpecialStart = (s) =>
    /^(#{1,3})\s/.test(s) ||
    /^[-*]\s/.test(s) ||
    /^\d+\.\s/.test(s) ||
    s.startsWith(">") ||
    /^!\[[^\]]*\]\([^)]+\)/.test(s) ||
    /^---+$/.test(s.trim()) ||
    /^\[\[gallery/.test(s.trim());

  while (i < lines.length) {
    const line = lines[i];
    if (!line.trim()) { i++; continue; }

    // horizontal rule
    if (/^---+$/.test(line.trim())) {
      blocks.push({ type: "hr" });
      i++; continue;
    }

    // heading
    let m = line.match(/^(#{1,3})\s+(.*)$/);
    if (m) {
      blocks.push({ type: "h" + (m[1].length + 1), text: m[2] });
      i++; continue;
    }

    // image (single-line)
    m = line.match(/^!\[([^\]]*)\]\(([^)]+)\)\s*(?:"([^"]*)")?\s*$/);
    if (m) {
      blocks.push({ type: "img", alt: m[1], src: m[2], caption: m[3] });
      i++; continue;
    }

    // gallery block:
    //   [[gallery cols=2]]
    //   url | caption | alt
    //   url | caption
    //   [[/gallery]]
    m = line.trim().match(/^\[\[gallery(?:\s+cols=(\d))?\]\]$/);
    if (m) {
      const cols = parseInt(m[1] || "2", 10);
      i++;
      const items = [];
      while (i < lines.length && !/^\[\[\/gallery\]\]\s*$/.test(lines[i].trim())) {
        const ln = lines[i].trim();
        if (ln) {
          const parts = ln.split("|").map(s => s.trim());
          items.push({ src: parts[0], caption: parts[1] || "", alt: parts[2] || parts[1] || "" });
        }
        i++;
      }
      if (i < lines.length) i++; // skip closing
      if (items.length) blocks.push({ type: "gallery", cols, items });
      continue;
    }

    // blockquote — collect adjacent "> " lines
    if (line.startsWith(">")) {
      const buf = [];
      while (i < lines.length && lines[i].startsWith(">")) {
        buf.push(lines[i].replace(/^>\s?/, ""));
        i++;
      }
      blocks.push({ type: "quote", text: buf.join(" ").trim() });
      continue;
    }

    // unordered list
    if (/^[-*]\s+/.test(line)) {
      const items = [];
      while (i < lines.length && /^[-*]\s+/.test(lines[i])) {
        items.push(lines[i].replace(/^[-*]\s+/, ""));
        i++;
      }
      blocks.push({ type: "ul", items });
      continue;
    }

    // ordered list
    if (/^\d+\.\s+/.test(line)) {
      const items = [];
      while (i < lines.length && /^\d+\.\s+/.test(lines[i])) {
        items.push(lines[i].replace(/^\d+\.\s+/, ""));
        i++;
      }
      blocks.push({ type: "ol", items });
      continue;
    }

    // paragraph — collect until blank or special
    const para = [];
    while (i < lines.length && lines[i].trim() !== "" && !isSpecialStart(lines[i])) {
      para.push(lines[i]);
      i++;
    }
    blocks.push({ type: "p", text: para.join(" ") });
  }

  return blocks.map((b, idx) => {
    const k = `b${idx}`;
    switch (b.type) {
      case "hr":    return <hr key={k} className="md-hr"/>;
      case "h2":    return <h2 key={k}>{_renderInline(b.text, k)}</h2>;
      case "h3":    return <h3 key={k}>{_renderInline(b.text, k)}</h3>;
      case "h4":    return <h4 key={k}>{_renderInline(b.text, k)}</h4>;
      case "quote": return <blockquote key={k}>{_renderInline(b.text, k)}</blockquote>;
      case "ul":    return <ul key={k}>{b.items.map((it, j) => <li key={j}>{_renderInline(it, `${k}-${j}`)}</li>)}</ul>;
      case "ol":    return <ol key={k}>{b.items.map((it, j) => <li key={j}>{_renderInline(it, `${k}-${j}`)}</li>)}</ol>;
      case "img":   {
        const src = _safeImg(b.src);
        if (!src) return null;
        return (
          <figure key={k} className="md-figure">
            <img src={src} alt={b.alt || ""} loading="lazy"/>
            {b.caption && <figcaption>{b.caption}</figcaption>}
          </figure>
        );
      }
      case "gallery": return (
        <div key={k} className={`md-gallery md-gallery-cols-${b.cols || 2}`}>
          {b.items.map((it, j) => {
            const src = _safeImg(it.src);
            if (!src) return null;
            return (
              <figure key={j} className="md-gallery-item">
                <img src={src} alt={it.alt || ""} loading="lazy"/>
                {it.caption && <figcaption>{it.caption}</figcaption>}
              </figure>
            );
          })}
        </div>
      );
      case "p":
      default:      return <p key={k}>{_renderInline(b.text, k)}</p>;
    }
  });
}

// ---------- Card --------------------------------------------------------

function InsightCard({ insight }) {
  const cat = insight.category || "projects";
  return (
    <Link to={`/insights/${insight.slug}`} className="insight-card" aria-label={insight.title}>
      <div className="insight-card-cover">
        {insight.coverImage
          ? <img src={insight.coverImage} alt={insight.coverAlt || ""} loading="lazy" width="800" height="450"/>
          : <div className="insight-card-cover-fallback" aria-hidden="true">{_categoryLabel(cat).slice(0, 2)}</div>
        }
        {insight.partnerProject && (
          <span className="insight-card-partner" title={insight.partnerProject}>
            <span className="insight-card-partner-label" aria-hidden="true">{t("insights.partnerLabel")}</span>
            <strong>{insight.partnerProject}</strong>
          </span>
        )}
      </div>
      <div className="insight-card-body">
        <span className={`insight-tag insight-tag-${cat}`}>{_categoryLabel(cat)}</span>
        <h3 className="insight-card-title">{insight.title}</h3>
        {insight.excerpt && <p className="insight-card-excerpt">{insight.excerpt}</p>}
      </div>
    </Link>
  );
}

// ---------- Index page --------------------------------------------------

function InsightsPage() {
  useLang();

  const initialCat = (() => {
    const { params } = _parseRouteParams();
    const c = params.get("cat");
    return c && INSIGHT_CATEGORIES.some(x => x.key === c) ? c : "all";
  })();

  const [activeCat, setActiveCat] = React.useState(initialCat);
  const [visible, setVisible] = React.useState(_INSIGHTS_PAGE_SIZE);

  React.useEffect(() => {
    const target = activeCat === "all" ? "#/insights" : `#/insights?cat=${activeCat}`;
    if (window.location.hash !== target) {
      window.history.replaceState(null, "", target);
    }
    setVisible(_INSIGHTS_PAGE_SIZE);
  }, [activeCat]);

  React.useEffect(() => {
    const prevTitle = document.title;
    document.title = `${t("insights.title")} — Artifex De Color`;
    _setMetaDescription(t("insights.lede"));
    return () => { document.title = prevTitle; };
  }, [getLang()]);

  const sorted = INSIGHTS.slice().sort((a, b) => {
    const da = a.date || "";
    const db = b.date || "";
    return db.localeCompare(da);
  });
  const filtered = activeCat === "all" ? sorted : sorted.filter(x => x.category === activeCat);
  const shown = filtered.slice(0, visible);

  const onTabKey = (e, idx) => {
    if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
    e.preventDefault();
    const dir = e.key === "ArrowRight" ? 1 : -1;
    const next = (idx + dir + INSIGHT_CATEGORIES.length) % INSIGHT_CATEGORIES.length;
    const btn = document.getElementById(`insight-tab-${INSIGHT_CATEGORIES[next].key}`);
    if (btn) btn.focus();
    setActiveCat(INSIGHT_CATEGORIES[next].key);
  };

  return (
    <main className="insights-page">
      <section className="page-hero insights-hero">
        <div className="eyebrow">{t("home.insightsEyebrow")}</div>
        <h1 className="page-title">{t("insights.title")}</h1>
        <p className="page-lede">{t("insights.lede")}</p>
      </section>

      <div
        className="insight-tabs"
        role="tablist"
        aria-label={t("insights.title")}
      >
        {INSIGHT_CATEGORIES.map((c, i) => {
          const selected = activeCat === c.key;
          return (
            <button
              key={c.key}
              id={`insight-tab-${c.key}`}
              type="button"
              role="tab"
              aria-selected={selected}
              tabIndex={selected ? 0 : -1}
              className={`insight-tab ${selected ? "is-active" : ""}`}
              onClick={() => setActiveCat(c.key)}
              onKeyDown={(e) => onTabKey(e, i)}
            >
              {t(c.labelKey)}
            </button>
          );
        })}
      </div>

      <section className="insights-grid-wrap" role="tabpanel">
        {filtered.length === 0 ? (
          <div className="insights-empty">{t("insights.empty")}</div>
        ) : (
          <>
            <div className="insights-grid">
              {shown.map(ins => (
                <InsightCard key={ins.slug} insight={ins}/>
              ))}
            </div>
            {visible < filtered.length && (
              <div className="insights-more">
                <button
                  type="button"
                  className="btn btn-ghost"
                  onClick={() => setVisible(v => v + _INSIGHTS_PAGE_SIZE)}
                >
                  {t("insights.showMore")}
                </button>
              </div>
            )}
          </>
        )}
      </section>
    </main>
  );
}

// ---------- Detail page -------------------------------------------------

function _insightCtaFor(category) {
  const map = {
    projects:    { titleKey: "insights.cta.projects.title",    btnKey: "insights.cta.projects.btn",    to: "/contacts" },
    materials:   { titleKey: "insights.cta.materials.title",   btnKey: "insights.cta.materials.btn",   to: "/contacts" },
    news:        { titleKey: "insights.cta.news.title",        btnKey: "insights.cta.news.btn",        to: "/contacts" },
    inspiration: { titleKey: "insights.cta.inspiration.title", btnKey: "insights.cta.inspiration.btn", to: "/catalog"  },
  };
  return map[category] || map.projects;
}

function _setMetaDescription(text) {
  if (typeof document === "undefined") return;
  let m = document.querySelector('meta[name="description"]');
  if (!m) {
    m = document.createElement("meta");
    m.setAttribute("name", "description");
    document.head.appendChild(m);
  }
  m.setAttribute("content", text || "");
}

function _setOgTag(prop, value) {
  if (typeof document === "undefined") return;
  if (!value) return;
  let m = document.querySelector(`meta[property="${prop}"]`);
  if (!m) {
    m = document.createElement("meta");
    m.setAttribute("property", prop);
    document.head.appendChild(m);
  }
  m.setAttribute("content", value);
}

function _injectArticleLD(insight) {
  if (typeof document === "undefined") return () => {};
  const id = "ld-insight";
  const data = {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": insight.title,
    "description": insight.excerpt,
    "image": insight.coverImage ? [insight.coverImage] : undefined,
    "datePublished": insight.date,
    "author": insight.author?.name
      ? { "@type": "Person", "name": insight.author.name }
      : { "@type": "Organization", "name": COMPANY.name || "Artifex De Color" },
    "publisher": {
      "@type": "Organization",
      "name": COMPANY.name || "Artifex De Color",
      "logo": COMPANY.logo ? { "@type": "ImageObject", "url": COMPANY.logo } : undefined
    }
  };
  if (insight.partnerProject && insight.externalCta?.primaryUrl) {
    data.mentions = {
      "@type": "Organization",
      "name": insight.partnerProject,
      "url": insight.externalCta.primaryUrl
    };
  }
  let s = document.getElementById(id);
  if (!s) {
    s = document.createElement("script");
    s.id = id;
    s.type = "application/ld+json";
    document.head.appendChild(s);
  }
  s.textContent = JSON.stringify(data);
  return () => { const cur = document.getElementById(id); if (cur) cur.remove(); };
}

function InsightDetailPage({ slug }) {
  useLang();
  const insight = INSIGHTS.find(x => x.slug === slug);

  React.useEffect(() => {
    if (!insight) return;
    const prevTitle = document.title;
    const seoTitle = insight.seo?.title || `${insight.title} — Artifex De Color`;
    document.title = seoTitle;
    _setMetaDescription(insight.seo?.description || insight.excerpt || "");
    _setOgTag("og:title", seoTitle);
    _setOgTag("og:description", insight.seo?.description || insight.excerpt || "");
    _setOgTag("og:type", "article");
    _setOgTag("og:image", insight.seo?.ogImage || insight.coverImage || "");
    const cleanupLD = _injectArticleLD(insight);
    return () => {
      document.title = prevTitle;
      cleanupLD();
    };
  }, [insight && insight.slug, getLang()]);

  if (!insight) {
    return (
      <main className="page-404">
        <h1>{t("insights.notFound.title")}</h1>
        <Link to="/insights" className="btn btn-primary">{t("insights.notFound.back")}</Link>
      </main>
    );
  }

  const cta = _insightCtaFor(insight.category);
  const minutes = _readingMinutes(insight);
  const dateStr = _formatInsightDate(insight.date);
  const tags = _normalizeTags(insight.tags);

  // related: same-category first, then fill from rest. Always exclude self.
  const sameCat = INSIGHTS.filter(x => x.slug !== insight.slug && x.category === insight.category);
  const others  = INSIGHTS.filter(x => x.slug !== insight.slug && x.category !== insight.category);
  const related = [...sameCat, ...others].slice(0, 3);

  return (
    <main className="insight-detail">
      <nav className="insight-crumbs" aria-label="Breadcrumbs">
        <Link to="/insights">{t("insights.crumbs")}</Link>
        <span aria-hidden="true">/</span>
        <Link to={`/insights?cat=${insight.category}`}>{_categoryLabel(insight.category)}</Link>
        <span aria-hidden="true">/</span>
        <span className="insight-crumb-current">{insight.title}</span>
      </nav>

      <header className="insight-header">
        <h1 className="insight-title">{insight.title}</h1>
        {insight.lede && <p className="insight-lede">{insight.lede}</p>}
      </header>

      {insight.coverImage && (
        <figure className="insight-cover">
          <img src={insight.coverImage} alt={insight.coverAlt || ""} width="1400" height="788"/>
        </figure>
      )}

      <article className="insight-body">
        {renderMarkdown(insight.body)}
      </article>

      {insight.externalCta && (insight.externalCta.title || insight.externalCta.primaryUrl) && (
        <section className="insight-external-cta">
          {insight.externalCta.logo && (
            <img
              src={insight.externalCta.logo}
              alt={insight.externalCta.title || ""}
              className="insight-external-cta-logo"
              loading="lazy"
            />
          )}
          {insight.externalCta.title && <h3>{insight.externalCta.title}</h3>}
          {insight.externalCta.subtitle && <p>{insight.externalCta.subtitle}</p>}
          <div className="insight-external-cta-row">
            {insight.externalCta.primaryUrl && (
              <a
                href={insight.externalCta.primaryUrl}
                target="_blank"
                rel="noopener noreferrer"
                className="btn btn-primary"
              >
                {insight.externalCta.primaryLabel || insight.externalCta.primaryUrl}
                <span className="ext-arrow" aria-hidden="true">↗</span>
              </a>
            )}
            {insight.externalCta.secondaryUrl && (() => {
              const url = insight.externalCta.secondaryUrl;
              const isAbs = /^(https?:)/i.test(url);
              const isScheme = /^(tel:|mailto:)/i.test(url);
              const href = (isAbs || isScheme) ? url : `#${url}`;
              return (
                <a
                  href={href}
                  className="btn btn-ghost"
                  target={isAbs ? "_blank" : undefined}
                  rel={isAbs ? "noopener noreferrer" : undefined}
                >
                  {insight.externalCta.secondaryLabel || url}
                </a>
              );
            })()}
          </div>
        </section>
      )}

      {tags.length > 0 && (
        <div className="insight-tags-row">
          {tags.map((tg, i) => <span key={i} className="insight-tag-chip">#{tg}</span>)}
        </div>
      )}

      {insight.author?.name && (
        <aside className="insight-author">
          {insight.author.photo && (
            <div className="insight-author-photo">
              <img src={insight.author.photo} alt={insight.author.name} loading="lazy"/>
            </div>
          )}
          <div className="insight-author-info">
            <div className="insight-author-label">{t("insights.author")}</div>
            <div className="insight-author-name">{insight.author.name}</div>
            {insight.author.role && <div className="insight-author-role">{insight.author.role}</div>}
          </div>
        </aside>
      )}

      {related.length > 0 && (
        <section className="insight-related">
          <div className="section-head">
            <h2 className="section-title">{t("insights.related")}</h2>
          </div>
          <div className="insights-grid">
            {related.map(ins => <InsightCard key={ins.slug} insight={ins}/>)}
          </div>
        </section>
      )}

      <section className="insight-cta">
        <h2 className="section-title">{t(cta.titleKey)}</h2>
        <div className="insight-cta-row">
          <Link to={cta.to} className="btn btn-primary">{t(cta.btnKey)}</Link>
          <Link to="/insights" className="btn btn-ghost">{t("insights.back")}</Link>
        </div>
      </section>
    </main>
  );
}

window.InsightsPage = InsightsPage;
window.InsightDetailPage = InsightDetailPage;
window.InsightCard = InsightCard;
window.renderMarkdown = renderMarkdown;
