import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { renderFixture, type RenderFixtureOptions } from './render-fixture-pure';

export { renderFixture, type RenderFixtureOptions };

/**
 * テスト専用ユーティリティ。
 *
 * __fixtures__ 配下の *.html は custom-field-maker（カスタムフィールドメーカー）が実際に出力する
 * ACMS テンプレート構文入りのソース（`{id}` 等の変数、`<!-- BEGIN xxx:loop -->...<!-- END xxx:loop -->`
 * のループ、`<!-- BEGIN_IF [...] -->...<!-- ELSE -->...<!-- END_IF -->` の分岐、`%{ARCHIVES_DIR}` の
 * グローバル変数を含む）で、無改変のまま収集している。
 *
 * 実際に組み込みJS（use-buildin-js.ts の apply()）が受け取るのは、サーバー側のテンプレートエンジン
 * （Template.php 等）がこれらを解決した後の HTML であり、本ヘルパーはそれを近似するもの。
 * 以下の点で「サーバーの実レンダリングの忠実な再現」ではないことに注意する。
 *
 * - `<!-- BEGIN_IF [...] -->` の分岐条件は評価しない。`BEGIN`/`ELSE`/`END` から始まるブロックコメント
 *   （`:loop` のループ、`_IF` の分岐、`veil` 等の他のブロックコメントも含む）はコメント自体だけを
 *   取り除き、囲まれた中身はすべて残す。そのため IF 節・ELSE 節の両方の中身が同時に残ることがある
 *   （データ0件を条件に持つループの複数件展開のようなことは行わない。ループも1回分の記述をそのまま
 *   展開するだけ）。
 * - `{variable}[filter]` のような ACMS のフィルタ記法は `{variable}` 部分だけを解決し、`[filter]` は
 *   解決されないテキストとしてそのまま残る（例: `{test_table}[raw]` → `[raw]`）。
 *
 * 目的は「テキストの厳密な再現」ではなく、「組み込みJSが要素を検出するために依存する class / data
 * 属性の構造を保った近似」を作ることにある。
 */

const FIXTURES_DIR = dirname(fileURLToPath(import.meta.url));

/**
 * `__fixtures__` 配下の HTML ファイルを無改変のまま文字列として読み込む。
 */
export function loadFixtureSource(fileName: string): string {
  return readFileSync(join(FIXTURES_DIR, fileName), 'utf-8');
}

/**
 * `__fixtures__` 配下の HTML ファイルを読み込み、変数解決済みの近似 HTML に変換する。
 */
export function renderFixtureFile(fileName: string, options: RenderFixtureOptions = {}): string {
  return renderFixture(loadFixtureSource(fileName), options);
}

/**
 * フィクスチャの近似 HTML を、DOM に未接続の `<div>` コンテナに流し込んで返す。
 * 組み込みJSの dispatch 関数はいずれも `Element | Document` を受け取るため、この
 * コンテナをそのまま渡せる。
 */
export function mountFixture(fileName: string, options: RenderFixtureOptions = {}): HTMLDivElement {
  const container = document.createElement('div');
  container.innerHTML = renderFixtureFile(fileName, options);
  return container;
}
