// 記事の全文ページ（/record/{slug}.md。2026-11-01）
// AI が記事を丸ごと読めるように、1本の記事を飾りのない Markdown 1枚にする。
// - 長いコード・ログを外に分けた記事（tools/split_files.py）は、分ける前の全文（public/files/LOG{N}/LOG{N}_full.md・.html）を使う
// - Studio から移した記事（本文が HTML）は Markdown に直す（turndown）
// - 画像・図解・動画・このサイトの住所は、ページの外でも開けるよう https://pixelstomotors.com から始まる住所にする
// 人が見るのは今の記事ページのまま。全文ページは worker/index.js が canonical（正しいページは記事ページ）を付けて返す
import { existsSync, readFileSync } from 'node:fs';
import TurndownService from 'turndown';
import { SITE, SITE_NAME } from './seo';
import { ymd } from './format';

export const fullTextPath = (slug: string) => `/record/${slug}.md`;

type Meta = { title: string; slug: string; day: string; series: string; seriesName: string; published: string; description: string; lang: 'ja' | 'en' };

const absUrl = (u: string) => (/^https?:|^mailto:|^#/.test(u) ? u : u.startsWith('/') ? SITE + u : u);

// 記事の Markdown の印（【画像】など。src/lib/remark-markers.mjs）を、普通の Markdown に直す
function markersToMarkdown(md: string, title: string): string {
  const media = (f: string) => `${SITE}/media/${encodeURIComponent(f)}`;
  return md.split('\n').map((line) => {
    const t = line.trim();
    let m = t.match(/^【画像】\s*(\S+)(?:\s+(.+))?$/);
    if (m) return `![${(m[2] || title).trim()}](${media(m[1])})`;
    m = t.match(/^【動画】\s*(\S+\.(?:mp4|webm|mov))(?:\s+(.+))?$/);
    if (m) return `[動画: ${(m[2] || m[1]).trim()}](${media(m[1])})`;
    m = t.match(/^【図解】\s*(\S+\.html)$/);
    if (m) return `[図解: ${m[1]}](${SITE}/embeds/${encodeURIComponent(m[1])})`;
    m = t.match(/^【バナー】\s*(\S+)$/);
    if (m) return `[${m[1] === 'gpu-buy-or-rent' ? 'ロボットAIの学習用GPU、買うか借りるか' : m[1] === 'serving-robot-one-less-staff' ? '配膳ロボットの導入前シミュレーション' : m[1]}](${SITE}/${m[1]})`;
    // 分けたファイルの印は、全文には本来残らない。残っていたらファイルへのリンクにする
    m = t.match(/^【ファイル】\s*(LOG\d+\/\S+)\s+(\S+)\s+(\S+)$/);
    if (m) return `[ファイル: ${m[1]}（${m[2]}・${m[3]}）](${SITE}/files/${m[1]})`;
    m = t.match(/^【ファイル一式】\s*(LOG\d+)$/);
    if (m) return `[ファイル一式: ${SITE}/files/${m[1]}/index.json](${SITE}/files/${m[1]}/index.json)`;
    return line;
  }).join('\n')
    // このサイトの中の住所（](/record/… ）を、外でも開ける住所に
    .replace(/\]\((\/[^)\s]*)\)/g, (_, p) => `](${SITE}${p})`);
}

const stripFrontmatter = (md: string) => md.replace(/^---\n[\s\S]*?\n---\n+/, '');

let td: TurndownService | null = null;
function htmlToMarkdown(html: string): string {
  if (!td) {
    td = new TurndownService({ headingStyle: 'atx', codeBlockStyle: 'fenced', bulletListMarker: '-', emDelimiter: '*' });
    td.remove(['script', 'style', 'noscript']);
    // 図解・YouTube の枠は、中身のページへのリンクにする
    td.addRule('iframe', {
      filter: 'iframe',
      replacement: (_c, node: any) => {
        const src = node.getAttribute('src') || '';
        if (!src) return '';
        const yt = /youtube(?:-nocookie)?\.com\/embed\/([\w-]+)/.exec(src);
        return yt ? `\n\n[動画（YouTube）](https://www.youtube.com/watch?v=${yt[1]})\n\n` : `\n\n[図解](${absUrl(src)})\n\n`;
      },
    });
    td.addRule('video', {
      filter: 'video',
      replacement: (_c, node: any) => {
        const src = (node.getAttribute('src') || node.querySelector?.('source')?.getAttribute('src') || '').replace(/#.*$/, '');
        return src ? `\n\n[動画](${absUrl(src)})\n\n` : '';
      },
    });
    // コードの言語（class="language-python"）を残す
    td.addRule('pre', {
      filter: (node: any) => node.nodeName === 'PRE',
      replacement: (_c, node: any) => {
        const code = node.querySelector?.('code');
        const lang = /language-([\w+-]+)/.exec((code || node).getAttribute?.('class') || '')?.[1] || '';
        const text = (code || node).textContent || '';
        const fence = text.includes('```') ? '````' : '```';
        return `\n\n${fence}${lang}\n${text.replace(/\n$/, '')}\n${fence}\n\n`;
      },
    });
  }
  return td.turndown(html.replace(/<!--TOC-->/g, ''))
    .replace(/\]\((\/[^)\s]*)\)/g, (_, p) => `](${SITE}${p})`)
    .replace(/\n{3,}/g, '\n\n');
}

function header(m: Meta): string {
  const url = `${SITE}/record/${m.slug}`;
  const en = m.lang === 'en';
  return [
    `# ${m.title}`,
    '',
    en
      ? `- Record: ${m.day} (${m.seriesName})\n- Published: ${ymd(m.published)}\n- Page: ${url}\n- Site: ${SITE_NAME} (ADAPT DESIGN / Eiichi Shimazu) ${SITE}/`
      : `- 記録: ${m.day}（${m.seriesName}）\n- 公開: ${ymd(m.published)}\n- 記事のページ: ${url}\n- サイト: ${SITE_NAME}（ADAPT DESIGN / 島津 栄一） ${SITE}/`,
    m.description ? (en ? `- Summary: ${m.description}` : `- 要約: ${m.description}`) : '',
    '',
    en
      ? '> This is the full text of the article for AI and text readers, including code and logs. Please cite the article page above.'
      : '> AI が読むための記事の全文です（コード・実行ログを含む）。引用するときは上の「記事のページ」の住所を使ってください。',
    '',
    '---',
    '',
  ].filter((l, i, a) => !(l === '' && a[i - 1] === '')).join('\n') + '\n';
}

// 分けた記事の全文ファイル（public/files/LOG{N}/index.json の article_full）
function splitFull(day: string): { body: string; type: 'md' | 'html' } | null {
  const idx = `public/files/${day}/index.json`;
  if (!existsSync(idx)) return null;
  try {
    const j = JSON.parse(readFileSync(idx, 'utf-8'));
    const type = j.article_full_type === 'text/html' ? 'html' : 'md';
    const p = type === 'html' ? `public/files/${day}/${day}_full.html` : `public/files/${day}/${day}_full.md`;
    return existsSync(p) ? { body: readFileSync(p, 'utf-8'), type } : null;
  } catch { return null; }
}

export function markdownRecord(m: Meta, src: { kind: 'md'; body: string } | { kind: 'studio'; html: string }): string {
  const split = splitFull(m.day);
  let body: string;
  if (split?.type === 'md') body = markersToMarkdown(stripFrontmatter(split.body), m.title);
  else if (split?.type === 'html') body = htmlToMarkdown(split.body);
  else if (src.kind === 'md') body = markersToMarkdown(src.body, m.title);
  else body = htmlToMarkdown(src.html);
  return header(m) + body.trim() + '\n';
}
