/**
 * 문서 본문 markdown 렌더 (Phase 1C + Phase 1B.5 — MarkdownDoc 컴포넌트 전용).
 *
 * mathMarkdown.ts (study 의 문제·해설용) 와 별도 인스턴스를 둬서 study 측 동작에 영향 없음.
 *
 * 차이점:
 *   - GFM heading id (anchor 용 id 자동 부여, prefix=doc-)
 *   - 이미지는 placeholder card 로 렌더. `data-md-image-internal="1"` 인 경우 (href 가
 *     `docimg:img_NNN`) MarkdownDoc 의 selector 가 mount 후 실제 <img> 로 교체.
 *     `data-md-image-internal="0"` (외부 URL) 은 placeholder 유지 — 외부 이미지 자동
 *     fetch 회피 (privacy + dependency).
 *
 * KaTeX / DOMPurify 정책은 mathMarkdown.ts 의 정책과 동일.
 */

import DOMPurify from 'dompurify';
import { Marked } from 'marked';
import katex from 'katex';
// @ts-ignore — 타입 정의 누락 시 무시
import markedKatex from 'marked-katex-extension';
// @ts-ignore — 타입 정의 누락 시 무시
import { gfmHeadingId } from 'marked-gfm-heading-id';
import 'katex/dist/katex.min.css';

function escAttr(s: string): string {
  return s.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function escText(s: string): string {
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

const docMarked = new Marked();
docMarked.use({ mangle: false } as any);
docMarked.use(gfmHeadingId({ prefix: 'doc-' }));
docMarked.use(
  markedKatex({
    throwOnError: false,
    nonStandard: false,
    output: 'html',
  } as any),
);

// 이미지 → placeholder card. raw <img> 를 emit 하면 asset 미서빙 상태(1B.5 전)에선
// 깨진 아이콘이 보임 → 1D pilot 평가가 markdown 품질이 아닌 viewer 미완성 문제로 오염됨.
// 따라서 1C 에선 placeholder 로 렌더하되 원본 href 는 data-md-image-src 에 보존
// (1B.5 에서 ImgAuth selector 로 잡아 실제 <img> 로 교체 예정).
docMarked.use({
  renderer: {
    image(token: any): string {
      const rawHref = (token?.href ?? '') as string;
      const rawAlt = (token?.text ?? '') as string;
      const isInternal = rawHref.startsWith('docimg:');
      const basename = rawHref.split('/').pop() ?? rawHref;
      const labelSrc = rawAlt || basename || '이미지';
      const safeHref = escAttr(rawHref);
      const safeAlt = escAttr(rawAlt);
      const safeLabel = escText(`[이미지: ${labelSrc} — 아직 표시되지 않음]`);
      const internalFlag = isInternal ? '1' : '0';
      return (
        `<figure class="md-image-placeholder" data-md-img="1" data-md-image-src="${safeHref}" data-md-image-internal="${internalFlag}" data-md-image-alt="${safeAlt}">` +
        `<div class="md-image-placeholder-card">` +
        `<span class="md-image-placeholder-icon" aria-hidden="true">🖼️</span>` +
        `<span class="md-image-placeholder-label">${safeLabel}</span>` +
        `</div>` +
        `</figure>`
      );
    },
    // 외부 링크(http/https) → 새 탭 + rel=noopener noreferrer (탭내빙 차단). 521건 실재.
    // 내부/프래그먼트/상대 링크는 손대지 않음 — `#` anchor 는 gfmHeadingId/outline 경로 유지
    // (클릭 인터셉터 없음 → 충돌 0), 상대 .md(코퍼스 0건)는 기본 동작(inert). marked 15 토큰객체 시그니처.
    link(token: any): string {
      const href = (token?.href ?? '') as string;
      const text = this.parser.parseInline(token?.tokens ?? []);
      const titleAttr = token?.title ? ` title="${escAttr(token.title as string)}"` : '';
      const safeHref = escAttr(href);
      if (/^https?:\/\//i.test(href)) {
        return `<a href="${safeHref}"${titleAttr} target="_blank" rel="noopener noreferrer">${text}</a>`;
      }
      return `<a href="${safeHref}"${titleAttr}>${text}</a>`;
    },
  },
});

const SANITIZE_OPTS = {
  USE_PROFILES: { html: true },
  // KaTeX (style + aria-hidden), heading anchor (id), 이미지 마킹 (data-md-img,
  // data-md-image-src + data-md-image-internal + data-md-image-alt — 1B.5 ImgAuth
  // selector 용), figure caption (figure/figcaption)
  ADD_ATTR: [
    'style',
    'aria-hidden',
    'id',
    'data-md-img',
    'data-md-image-src',
    'data-md-image-internal',
    'data-md-image-alt',
    'loading',
    'target',
    'rel',
  ],
  ADD_TAGS: ['figure', 'figcaption'],
  FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'link', 'meta'],
  FORBID_ATTR: ['onerror', 'onclick', 'onload', 'onmouseover', 'onfocus'],
  ALLOW_UNKNOWN_PROTOCOLS: false,
} as const;

// ── 수식 pre-render ──────────────────────────────────────────────────────────
// marked-katex-extension 의 토크나이저는 `$$` 가 블록 선두에 있어야 발화하는데,
// (1) 개요 anchor splice 가 `$$` 직전에 <span id="sec-N"> 를 끼우면 `$$` 가 문단 중간으로
// 밀려 블록 규칙이 깨지고, (2) 빌드/런타임 환경에 따라 확장 토크나이저가 발화하지 않으면
// `$$` 가 평문으로 새어 marked 의 백슬래시 이스케이프(\% → %, \, → ,)에 망가진다.
// → marked 가 손대기 *전에* 수식을 katex 로 직접 렌더해 placeholder 로 보호한 뒤 복원한다.
//   위치·인접 상황과 무관(전역 정규식)하므로 위 두 경우를 모두 우회한다.
const _MATH_SLOT = (i: number) => `KX0MATHSLOT${i}MATHKX0`; // marked-안전(영숫자) + 충돌 불가
const _MATH_SLOT_RE = /KX0MATHSLOT(\d+)MATHKX0/g;
const _BLOCK_MATH_RE = /\$\$([\s\S]+?)\$\$/g;
// 인라인 $...$ — 통화($5)·이스케이프(\$)·`$$` 회피. $ 직후 비공백, $ 직전 비공백.
const _INLINE_MATH_RE = /(?<![\\$\d])\$(?!\s)([^$\n]*?[^$\n\s])\$(?!\d)/g;

function _protectMath(text: string, slots: string[]): string {
  const render = (tex: string, displayMode: boolean): string => {
    slots.push(
      katex.renderToString(tex.trim(), { displayMode, throwOnError: false, output: 'html' }),
    );
    return _MATH_SLOT(slots.length - 1);
  };
  return text
    .replace(_BLOCK_MATH_RE, (m, tex) => {
      try {
        return render(String(tex), true);
      } catch {
        return m;
      }
    })
    .replace(_INLINE_MATH_RE, (m, tex) => {
      try {
        return render(String(tex), false);
      } catch {
        return m;
      }
    });
}

export function renderDocMarkdown(text: string | null | undefined): string {
  if (!text) return '';
  try {
    const slots: string[] = [];
    const protectedText = _protectMath(text, slots);
    let html = docMarked.parse(protectedText) as string;
    if (slots.length) {
      // 블록 수식이 단독 문단이면 marked 가 <p> 로 감싸므로 그 <p> 를 벗겨 블록 수식이 문단에
      // 매몰되지 않게 한다. (katex-display 는 block 이라 <p> 안에 두면 브라우저가 자동 분리.)
      html = html
        .replace(
          new RegExp(`<p>\\s*KX0MATHSLOT(\\d+)MATHKX0\\s*</p>`, 'g'),
          (m, i) => slots[Number(i)] ?? m,
        )
        .replace(_MATH_SLOT_RE, (m, i) => slots[Number(i)] ?? m);
    }
    return DOMPurify.sanitize(html, SANITIZE_OPTS);
  } catch {
    // 마지막 안전망: 모든 태그 제거 후 escape
    return DOMPurify.sanitize(text, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
  }
}
