const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

const docDir = __dirname;
const tmpDir = path.join(docDir, '_eid_docx_tmp');
const docxPath = path.join(docDir, 'E证通小程序申请与配置指南.docx');

function escapeXml(text) {
  return String(text)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

function p(text, style, bold) {
  const styleXml = style ? `<w:pPr><w:pStyle w:val="${style}"/></w:pPr>` : '';
  const boldXml = bold ? '<w:b/>' : '';
  return `<w:p>${styleXml}<w:r><w:rPr>${boldXml}<w:rFonts w:ascii="Microsoft YaHei" w:hAnsi="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/><w:sz w:val="24"/></w:rPr><w:t xml:space="preserve">${escapeXml(text)}</w:t></w:r></w:p>`;
}

function h1(text) { return p(text, 'Heading1', true); }
function h2(text) { return p(text, 'Heading2', true); }
function body(text) { return p(text); }
function bullet(text) {
  return `<w:p><w:pPr><w:pStyle w:val="ListParagraph"/><w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="Microsoft YaHei" w:hAnsi="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/><w:sz w:val="24"/></w:rPr><w:t xml:space="preserve">${escapeXml(text)}</w:t></w:r></w:p>`;
}

const sections = [
  h1('E证通小程序申请与配置指南'),
  body('适用项目：灵活用工微信小程序（uni-app）'),
  body('文档版本：v1.0'),
  body('编写日期：2026年7月8日'),
  body(''),

  h2('一、概述'),
  body('E证通（腾讯云慧眼人脸核身）在微信小程序中的接入方式，官方称为「E证通小程序 SDK」，并非微信公众平台「插件管理」中搜索添加的普通插件。'),
  body('整体接入分为两部分：'),
  bullet('腾讯云侧：申请商户 ID、下载 SDK、后端对接 GetEidToken / GetEidResult'),
  bullet('微信小程序后台：配置服务器域名、可选半屏小程序等'),
  body('官方文档：'),
  bullet('接入流程说明：https://cloud.tencent.com/document/product/1007/108009'),
  bullet('小程序集成：https://cloud.tencent.com/document/product/1007/108011'),
  body(''),

  h2('二、整体流程'),
  body('1. 腾讯云申请商户 ID（约 3 个工作日）'),
  body('2. 下载 E证通小程序 SDK（mp_ecard_sdk 文件夹）'),
  body('3. 后端接入 GetEidToken / GetEidResult 接口'),
  body('4. 微信小程序后台配置域名、半屏（可选）'),
  body('5. 小程序内集成 SDK 并调用 startEid 发起核身'),
  body(''),

  h2('三、腾讯云侧：申请商户 ID（必做）'),
  body('步骤 1：登录腾讯云慧眼 · 人脸核身控制台'),
  bullet('控制台地址：https://console.cloud.tencent.com/faceid'),
  body('步骤 2：进入「自助接入 → E证通服务 → 申请商户 ID」'),
  body('步骤 3：填写并提交申请资料'),
  bullet('小程序 AppID（manifest.json 中 mp-weixin.appid，不能为空）'),
  bullet('小程序名称'),
  bullet('业务场景说明（例如：临时工/企业注册实人认证）'),
  bullet('按提示上传业务域名校验文件（若需要返回身份信息等）'),
  body('步骤 4：等待审核，一般约 3 个工作日'),
  body('步骤 5：审核通过后'),
  bullet('获得商户 ID（MerchantId）'),
  bullet('可下载 E证通小程序 SDK（mp_ecard_sdk 文件夹）'),
  body('后端使用商户 ID 调用 GetEidToken 生成核身 Token，再传给前端使用。'),
  body(''),

  h2('四、微信小程序后台配置'),
  body('登录微信公众平台：https://mp.weixin.qq.com/ ，进入对应小程序。'),
  body(''),
  body('4.1 服务器域名（必配）'),
  body('路径：开发 → 开发管理 → 开发设置 → 服务器域名 → request 合法域名'),
  body('添加域名：https://eid.faceid.qq.com'),
  body('说明：开发阶段可在微信开发者工具勾选「不校验合法域名」，但上线前必须配置。'),
  body(''),
  body('4.2 业务域名（视 SDK 版本而定）'),
  bullet('SDK V1.0.7 及以上：一般不需要配置业务域名'),
  bullet('更低版本：可能需要将 eid.faceid.qq.com 加入业务域名，校验文件在申请商户 ID 时一并提交审核'),
  body(''),
  body('4.3 半屏打开 eID 小程序（可选，体验更好）'),
  body('E证通核身时会跳转到「eID 数字身份」小程序，AppID：wx0e2cb0b052a91c92'),
  body('配置路径：设置 → 第三方设置 → 半屏小程序管理 → 添加'),
  bullet('每个小程序最多可添加 10 个半屏小程序'),
  bullet('还需向 E证通侧申请半屏权限，审核通过后才会生效'),
  bullet('不支持半屏时会自动降级为全屏跳转'),
  body('半屏模式还需在 app.json 中配置（uni-app 对应 pages.json / manifest.json）：'),
  body('"embeddedAppIdList": ["wx0e2cb0b052a91c92"]'),
  body(''),
  body('4.4 关于「插件」的说明'),
  body('E证通不需要在「设置 → 第三方设置 → 插件管理」中搜索添加插件。正确做法是：下载 SDK 放入项目，在 App.onLaunch 中调用 initEid，页面中通过按钮点击调用 startEid 并传入 token。'),
  body(''),

  h2('五、小程序 SDK 集成要点'),
  body('5.1 下载并放置 SDK'),
  body('在腾讯云控制台下载 E证通小程序 SDK，将 mp_ecard_sdk 文件夹放在小程序根目录下（uni-app 项目建议放在 app/ 目录下）。'),
  body(''),
  body('5.2 初始化 SDK（App.onLaunch）'),
  body('在 App.js / App.vue 的 onLaunch 中引入并调用 initEid。'),
  body('在 pages.json 中注册 SDK 自带页面路径（以 SDK 版本说明为准）。'),
  body(''),
  body('5.3 调用核身（必须通过按钮点击触发）'),
  body('SDK v1.0.5 及以上版本，startEid 必须通过按钮点击形式发起（内部使用 wx.navigateToMiniProgram 拉起 eID 数字身份小程序）。'),
  body('主要参数说明：'),
  bullet('data.token：后端返回的 EidToken（必填）'),
  bullet('data.enableEmbedded：是否半屏打开（SDK v1.0.9+，默认 false）'),
  bullet('data.allowFullScreen：半屏后是否允许全屏（SDK v1.0.9+，默认 true）'),
  bullet('verifyDoneCallback：核身完成回调，含 token 与 verifyDone 标志'),
  body(''),
  body('5.4 从 eID 小程序返回时的 onShow 处理'),
  body('用户从 eID 数字身份小程序返回时，会同时触发接入方 App.onShow。若 onShow 中有登录跳转等逻辑，需排除此场景：'),
  body('判断条件：scene === 1038 且 referrerInfo.appId === "wx0e2cb0b052a91c92" 时直接 return，不执行原有逻辑。'),
  body(''),

  h2('六、后端接口要求'),
  body('商户 ID 审核通过后，后端需至少提供以下能力：'),
  bullet('GetEidToken：根据姓名、身份证号等信息向腾讯云换取 EidToken，返回给前端'),
  bullet('GetEidResult：核身结束后查询核验结果，以前端回调为辅、后端查询为准'),
  body('默认情况下不会返回姓名、身份证等明文信息；如需加密返回身份信息，需在控制台单独申请开通。'),
  body('建议后端封装接口示例：'),
  bullet('POST /api/v1/mp/face/init — 登记提交后获取 EidToken'),
  bullet('GET  /api/v1/mp/face/result — 核身完成后查询结果并更新 faceVerified 状态'),
  body(''),

  h2('七、与本项目的对应关系'),
  bullet('小程序 AppID：manifest.json → mp-weixin.appid（当前需填写）'),
  bullet('SDK 目录：app/mp_ecard_sdk/'),
  bullet('初始化：App.vue 的 onLaunch 中 initEid'),
  bullet('页面注册：pages.json 增加 SDK 页面路径'),
  bullet('业务串联：企业/临时工登记页提交 → 人脸校验页 → startEid → 后端确认 → 进入业务首页'),
  body('建议业务流程：'),
  body('登记页提交 → 后端保存资料并 GetEidToken → 跳转人脸校验页 → 用户点击按钮 startEid(token) → 进入 eID 数字身份小程序 → 返回本小程序 → 后端 GetEidResult 确认 → faceVerified = true → 进入首页'),
  body(''),

  h2('八、调试与注意事项'),
  bullet('请在微信开发者工具中使用手机「预览」模式调试，官方建议不要使用「真机调试」'),
  bullet('E证通 SDK 通过 wx.onAppShow 监听从 eID 小程序返回的事件，触发 verifyDoneCallback'),
  bullet('半屏接入是较新能力，部分机型存在兼容性问题，上线前需充分测试'),
  body(''),

  h2('九、申请前 Checklist'),
  bullet('小程序已注册，AppID 已填入 manifest.json'),
  bullet('腾讯云账号已实名认证'),
  bullet('人脸核身 / E证通 服务已开通'),
  bullet('商户 ID 已申请（约 3 工作日）'),
  bullet('E证通小程序 SDK 已下载'),
  bullet('微信后台 request 域名已添加 eid.faceid.qq.com'),
  bullet('（可选）半屏小程序已配置 wx0e2cb0b052a91c92'),
  bullet('后端 GetEidToken / GetEidResult 接口已联调通过'),
  body(''),

  h2('十、常见问题'),
  body('Q1：找不到「E证通插件」怎么办？'),
  body('A：E证通不是插件市场插件，需在腾讯云下载 SDK 集成，并在微信后台配置域名。'),
  body(''),
  body('Q2：调起核身失败？'),
  body('A：检查 AppID 是否为空、商户 ID 是否审核通过、是否已 initEid、Token 是否有效。'),
  body(''),
  body('Q3：域名请求报错？'),
  body('A：确认已在微信后台添加 https://eid.faceid.qq.com 到 request 合法域名。'),
  body(''),
  body('Q4：真机调试异常？'),
  body('A：改用开发者工具「预览」扫码测试，不要使用「真机调试」。'),
  body(''),
  body('Q5：返回小程序后页面乱跳？'),
  body('A：在 App.onShow 中排除从 eID 小程序返回的场景（scene=1038）。'),
];

const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:body>
    ${sections.join('\n    ')}
    <w:sectPr>
      <w:pgSz w:w="11906" w:h="16838"/>
      <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440"/>
    </w:sectPr>
  </w:body>
</w:document>`;

const files = {
  '[Content_Types].xml': `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  <Default Extension="xml" ContentType="application/xml"/>
  <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
  <Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>
  <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
</Types>`,
  '_rels/.rels': `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`,
  'word/document.xml': documentXml,
  'word/_rels/document.xml.rels': `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/>
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
</Relationships>`,
  'word/numbering.xml': `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:abstractNum w:abstractNumId="0">
    <w:multiLevelType w:val="hybridMultilevel"/>
    <w:lvl w:ilvl="0">
      <w:start w:val="1"/>
      <w:numFmt w:val="bullet"/>
      <w:lvlText w:val="•"/>
      <w:lvlJc w:val="left"/>
      <w:pPr><w:ind w:left="720" w:hanging="360"/></w:pPr>
    </w:lvl>
  </w:abstractNum>
  <w:num w:numId="1"><w:abstractNumId w:val="0"/></w:num>
</w:numbering>`,
  'word/styles.xml': `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:style w:type="paragraph" w:default="1" w:styleId="Normal">
    <w:name w:val="Normal"/>
    <w:rPr><w:rFonts w:ascii="Microsoft YaHei" w:hAnsi="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/><w:sz w:val="24"/></w:rPr>
  </w:style>
  <w:style w:type="paragraph" w:styleId="Heading1">
    <w:name w:val="heading 1"/>
    <w:pPr><w:spacing w:before="240" w:after="120"/></w:pPr>
    <w:rPr><w:b/><w:sz w:val="32"/><w:rFonts w:ascii="Microsoft YaHei" w:hAnsi="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/></w:rPr>
  </w:style>
  <w:style w:type="paragraph" w:styleId="Heading2">
    <w:name w:val="heading 2"/>
    <w:pPr><w:spacing w:before="200" w:after="100"/></w:pPr>
    <w:rPr><w:b/><w:sz w:val="28"/><w:rFonts w:ascii="Microsoft YaHei" w:hAnsi="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/></w:rPr>
  </w:style>
  <w:style w:type="paragraph" w:styleId="ListParagraph">
    <w:name w:val="List Paragraph"/>
    <w:pPr><w:ind w:left="720"/></w:pPr>
  </w:style>
</w:styles>`,
};

if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true, force: true });
fs.mkdirSync(tmpDir, { recursive: true });

for (const [relPath, content] of Object.entries(files)) {
  const fullPath = path.join(tmpDir, relPath);
  fs.mkdirSync(path.dirname(fullPath), { recursive: true });
  fs.writeFileSync(fullPath, content, 'utf8');
}

if (fs.existsSync(docxPath)) fs.unlinkSync(docxPath);

const ps1Path = path.join(docDir, '_zip-eid-doc.ps1');
const ps1 = `
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.IO.Compression.FileSystem
$tmp = '${tmpDir.replace(/'/g, "''")}'
$out = '${docxPath.replace(/'/g, "''")}'
if (Test-Path -LiteralPath $out) { Remove-Item -LiteralPath $out -Force }
[System.IO.Compression.ZipFile]::CreateFromDirectory($tmp, $out)
Write-Host "OK"
`;
fs.writeFileSync(ps1Path, '\ufeff' + ps1, 'utf8');
execSync(`powershell -NoProfile -ExecutionPolicy Bypass -File "${ps1Path}"`, { stdio: 'inherit' });
fs.unlinkSync(ps1Path);

fs.rmSync(tmpDir, { recursive: true, force: true });

if (!fs.existsSync(docxPath)) {
  console.error('Failed to create docx');
  process.exit(1);
}
console.log('Generated:', docxPath);
console.log('Size:', fs.statSync(docxPath).size, 'bytes');
