import path from 'path';
import fs from 'fs-extra';
import chalk from 'chalk';
import type { ITiddlerFields } from 'tiddlywiki';

import { tiddlywiki, waitForFile, getTmpDir } from '../utils/index';
import { getRuntimePluginTiddlers } from '../runtime-plugins';
import { buildCPLPlugin } from './cpl-plugin';

/** 项目路径 */
const bypassTiddlers = new Set([
  '$:/core',
  'IfEditorMode',
  '$:/UpgradeLibrary',
  '$:/UpgradeLibrary/List',
  '$:/plugins/telmiger/EditorCounter',
  '$:/plugins/tiddlywiki/pluginlibrary',
  '$:/plugins/flibbles/relink-titles',
]);

const headerMetadataTiddler: ITiddlerFields = {
  title: '$:/plugins/Gk0Wk/CPL-Repo/website/no-cache-html',
  tags: ['$:/tags/RawMarkupWikified/TopHead'],
  text: [
    '`',
    '<meta http-equiv="cache-control" content="no-cache">',
    '<meta http-equiv="expires" content="0">',
    '<meta http-equiv="pragma" content="no-cache">',
    '`',
  ].join('\n'),
} as any;

/**
 * 构建在线HTML版本：核心JS和资源文件不包括在HTML中， 下载后不能使用
 * @param {string} [wikiPath='.'] wiki 路径
 * @param {string} [dist='dist'] 构建产物路径
 * @param {string} [htmlName='index.html'] 构建产生的 index 文件名
 * @param {string} [excludeFilter='-[is[draft]]'] 要排除的tiddler的过滤表达式，默认为'-[is[draft]]'
 */
export const buildOnlineHTML = async (
  wikiPath = '.',
  dist = 'dist',
  htmlName = 'index.html',
  excludeFilter = '-[is[draft]]',
) => {
  // 构建插件库，导出插件
  const wikiFolder = path.resolve(wikiPath);
  const distDir = path.resolve(dist);
  const publicDir = path.resolve('public');

  // Remove any CPL config tiddlers that leaked from CI test runs.
  // CI E2E tests write config tiddlers (current-server, current-repo, etc.)
  // through the browser to the test TW server. TW's filesystem sync saves
  // these to wiki/tiddlers/ (sometimes under subdirs like website/$_/plugins/...
  // due to FileSystemPaths rules). These files are gitignored but persist on
  // the runner, causing `tiddlywiki([], wikiFolder)` below to load them as user
  // tiddlers that override the plugin's defaults.multids.
  const tiddlersDir = path.join(wikiFolder, 'tiddlers');
  const cplConfigDir = path.join(
    tiddlersDir,
    'website',
    '$_',
    'plugins',
    'Gk0Wk',
    'CPL-Repo',
    'config',
  );
  function cleanLeakedConfig(dir: string): void {
    for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
      const entryPath = path.join(dir, entry.name);
      if (entry.isDirectory()) {
        cleanLeakedConfig(entryPath);
      } else if (
        entry.isFile() &&
        entry.name.startsWith('$__plugins_Gk0Wk_CPL-Repo_config_')
      ) {
        fs.removeSync(entryPath);
        console.log(
          chalk.gray(
            `  Cleaned up leaked config: ${path.relative(
              tiddlersDir,
              entryPath,
            )}`,
          ),
        );
      }
    }
  }
  if (fs.existsSync(tiddlersDir)) {
    cleanLeakedConfig(tiddlersDir);
  }
  if (fs.existsSync(cplConfigDir)) {
    fs.removeSync(cplConfigDir);
    console.log(
      chalk.gray(
        `  Cleaned up leaked config directory: ${path.relative(
          tiddlersDir,
          cplConfigDir,
        )}`,
      ),
    );
  }

  // 读取、导出外置资源、处理 tiddler
  console.log(chalk.bgCyan.black.bold('\nExporting media tiddlers...'));
  const $tw = tiddlywiki([], wikiFolder);
  const tiddlers: Map<string, ITiddlerFields> = new Map();
  const sourceRepoPluginTiddlers = getRuntimePluginTiddlers('repo');
  const assetsPath = path.resolve(distDir, 'assets');
  fs.ensureFileSync(path.resolve(assetsPath, '1'));
  $tw.wiki.each(({ fields }, title: string) => {
    if (
      bypassTiddlers.has(title) ||
      title.startsWith('$:/boot/') ||
      title.startsWith('$:/temp/') ||
      title.startsWith('$:/plugins/tiddlywiki/codemirror')
    ) {
      return;
    }
    if ($tw.wiki.isBinaryTiddler(title) || $tw.wiki.isImageTiddler(title)) {
      const { extension, encoding } = $tw.config.contentTypeInfo[
        fields.type || 'text/vnd.tiddlywiki'
      ] ?? { extension: '.bin', encoding: 'base64' };
      const fileName = encodeURIComponent(
        title.endsWith(extension) ? title : `${title}${extension}`,
      );
      fs.writeFileSync(
        path.resolve(assetsPath, fileName),
        fields.text,
        encoding as any,
      );
      tiddlers.set(title, {
        ...fields,
        text: '',
        _canonical_uri: `./assets/${encodeURIComponent(fileName)}`,
      });
      console.log(chalk.gray(`  ${title} => assets/${fileName}`));
    } else {
      tiddlers.set(title, { ...fields });
    }
  });

  // Overlay the current src/CPLPlugin runtime tiddlers so the website uses the
  // same UI/templates as the built plugin instead of legacy wiki-side copies.
  Object.values(sourceRepoPluginTiddlers).forEach(tiddler => {
    tiddlers.set(tiddler.title, { ...tiddler } as ITiddlerFields);
  });

  // Remove legacy wiki-side title cascade that overrides the newer detail page.
  tiddlers.delete('Plugin Info Title Cascade');
  tiddlers.delete('Plugin Info Title');

  // 拷贝公共资源
  console.log(chalk.bgCyan.black.bold('\nCopying public assets...'));
  for (const entry of fs.readdirSync(publicDir)) {
    fs.copySync(path.resolve(publicDir, entry), path.resolve(distDir, entry), {
      overwrite: true,
    });
  }

  // 缓存策略
  tiddlers.set(headerMetadataTiddler.title, headerMetadataTiddler);

  // CPL 插件
  console.log(chalk.bgCyan.black.bold('\nGenerating CPL plugin...'));
  const cplPlugin = buildCPLPlugin($tw)[0];
  tiddlers.set(cplPlugin.title, cplPlugin as any);

  // Disable auto update and popup for the deployed static site.
  tiddlers.set(
    '$:/plugins/Gk0Wk/CPL-Repo/config/auto-update-intervals-minutes',
    {
      title: '$:/plugins/Gk0Wk/CPL-Repo/config/auto-update-intervals-minutes',
      text: '-1',
    } as any,
  );
  // 不要弹窗
  tiddlers.set('$:/plugins/Gk0Wk/CPL-Repo/config/popup-readme-at-startup', {
    title: '$:/plugins/Gk0Wk/CPL-Repo/config/popup-readme-at-startup',
    text: '1',
  } as any);

  // 构建
  console.log(
    chalk.bgCyan.black.bold('\nBuilding up TiddlyWiki online website...'),
  );
  const tmpFolder = getTmpDir();
  try {
    fs.cpSync(
      path.resolve(wikiFolder, 'tiddlywiki.info'),
      path.resolve(tmpFolder, 'tiddlywiki.info'),
    );
    tiddlywiki(Array.from(tiddlers.values()), tmpFolder, [
      ...['--output', distDir] /* 指定输出路径 */,
      ...[
        '--rendertiddler',
        '$:/core/save/offline-external-js',
        htmlName,
        'text/plain',
        '',
        'publishFilter',
        excludeFilter,
      ] /* 导出无核心的HTML文件 */,
      ...[
        '--rendertiddler',
        '$:/core/templates/tiddlywiki5.js',
        `tiddlywikicore-${$tw.version}.js`,
        'text/plain',
      ] /* 导出核心 */,
    ]);
    await waitForFile(
      path.resolve(distDir, `tiddlywikicore-${$tw.version}.js`),
    );
  } finally {
    fs.rmSync(tmpFolder, { recursive: true, force: true });
  }
};
