/**
 * 探针：分组检验项（父项 + 子项）在现有渲染引擎下能表达成什么样。
 *
 * 目的只有一个：把「rowspan 合并单元格要不要动引擎」这件事从推断变成实测。
 * 只读，不改代码、不碰数据库。
 *
 * 关键前提（读 report-evaluator.ts:237 得到）：判定阶段会把上下文重建成
 * `{ report, inspectionItems }` 两个键，任何自定义的顶层数组都进不了渲染作用域。
 * 所以本探针同时验证「顶层数组不可用」与「挂在每个检验项上的字段可用」两条。
 *
 * 用法：npx tsx .qc-conformance/verify-group-repeat.ts
 */
import type { RenderNode, RenderSchema } from '../src/components/quality/engine/render';

import { writeFileSync } from 'node:fs';

import { createContext } from '../src/components/quality/engine/context';
import { renderReport } from '../src/components/quality/engine/render';

const REPEAT = 'data-qc-repeat';
const REPEAT_ROW = 'data-qc-repeat-row';

const HEADERS = ['检验项目', '检测要求', '标准值', '实测值', '单位', '判定'];

/**
 * 把「组 → 子项」摊平成一行一项的检验项数组，组信息只落在组头行上。
 * 这是后端要产出的形状：组头行有自己的 itemName（组名），明细行 groupName 为空。
 */
const items = [
  { itemName: '水分', standardValue: 'w = (m1 - m0) / m × 100%', actualValue: '', unit: '', groupSpan: 5 },
  { itemName: '试样质量 m', standardValue: '', actualValue: '50.2', unit: 'g', groupSpan: 1 },
  { itemName: '试样+称量瓶 m1', standardValue: '', actualValue: '78.9', unit: 'g', groupSpan: 1 },
  { itemName: '称量瓶 m0', standardValue: '', actualValue: '28.7', unit: 'g', groupSpan: 1 },
  { itemName: '水分结果 w', standardValue: '≤0.5', actualValue: '0.32', unit: '%', groupSpan: 1 },
  { itemName: '正丁醇含量', standardValue: '纯度 = 100% - 水分 - 酸含量 - 其它杂质', actualValue: '', unit: '', groupSpan: 2 },
  { itemName: '正丁醇含量 /%', standardValue: '≥99.5', actualValue: '99.7', unit: '%', groupSpan: 1 },
];

const BASE_CONTEXT = {
  report: { reportNo: 'QC-2026-0001', reportName: '来料检验报告', spec: 'M8×20', inspectType: 'IQC' },
  inspectionItems: items,
};

const cell = (text: string, attributes?: Record<string, string>): RenderNode => ({
  type: 'cell',
  attributes,
  content: text,
});

const row = (cells: RenderNode[], attributes?: Record<string, string>): RenderNode => ({
  type: 'row',
  attributes,
  components: cells,
});

const tbody = (rows: RenderNode[], attributes?: Record<string, string>): RenderNode => ({
  type: 'tbody',
  attributes,
  components: rows,
});

const table = (headers: string[], bodies: RenderNode[]): RenderNode => ({
  type: 'table',
  components: [{ type: 'thead', components: [row(headers.map((text) => cell(text)))] }, ...bodies],
});

function run(
  label: string,
  component: RenderNode,
  context: Record<string, unknown> = BASE_CONTEXT,
): { body: string; html: string; errors: string[] } {
  const schema = {
    grapes: { pages: [{ frames: [{ component: { components: [component] } }] }] },
  } as unknown as RenderSchema;
  const outcome = renderReport({ context: createContext(context), rules: [], schema });
  // eslint-disable-next-line no-console
  console.log(`\n========== ${label} ==========`);
  // eslint-disable-next-line no-console
  console.log(outcome.body.replace(/></g, '>\n<'));
  // eslint-disable-next-line no-console
  console.log(`── 未解析绑定：${JSON.stringify(outcome.errors)}`);
  return { body: outcome.body, html: outcome.html, errors: outcome.errors };
}

// ----------------------------- P1 扁平：repeat over inspectionItems，组头信息挂在行上

const p1 = run(
  'P1 扁平：repeat inspectionItems，组头行的「检验项目/检测要求」放组名与公式',
  table(HEADERS, [
    tbody(
      [
        row(
          [
            cell('{{item.itemName}}'),
            cell('{{item.standardValue}}'),
            cell('{{item.actualValue}}'),
            cell('{{item.unit}}'),
            cell('{{item.resultText}}'),
            cell('{{item.index}}'),
          ],
          { [REPEAT_ROW]: 'item' },
        ),
      ],
      { [REPEAT]: 'inspectionItems' },
    ),
  ]),
);

// ----------------------------- P2 同上，但给首列加 rowspan 绑定

const p2 = run(
  'P2 rowspan 绑定：首列 rowspan="{{item.groupSpan}}"，看属性里的绑定能否解析',
  table(HEADERS, [
    tbody(
      [
        row(
          [
            cell('{{item.itemName}}', { rowspan: '{{item.groupSpan}}' }),
            cell('{{item.standardValue}}'),
            cell('{{item.actualValue}}'),
            cell('{{item.unit}}'),
            cell('{{item.resultText}}'),
            cell('{{item.index}}'),
          ],
          { [REPEAT_ROW]: 'item' },
        ),
      ],
      { [REPEAT]: 'inspectionItems' },
    ),
  ]),
);

// ----------------------------- P3 顶层自定义数组（预期为空，证明上下文被收口）

const p3 = run(
  'P3 顶层自定义数组：repeat inspectionGroups（预期渲染不出任何行）',
  table(HEADERS, [
    tbody(
      [row([cell('{{g.groupName}}'), cell('{{g.formula}}')], { [REPEAT_ROW]: 'g' })],
      { [REPEAT]: 'inspectionGroups' },
    ),
  ]),
);

// ----------------------------- P4 嵌套：组作为一项、子项挂在 item.children 上

/** 每个「项」是一个组，子项挂在 children 上（仍走 inspectionItems 这一个顶层数组） */
const groupedItems = [
  {
    itemName: '水分',
    standardValue: 'w = (m1 - m0) / m × 100%',
    span: 5,
    children: items.slice(1, 5),
  },
  {
    itemName: '正丁醇含量',
    standardValue: '纯度 = 100% - 水分 - 酸含量 - 其它杂质',
    span: 2,
    children: items.slice(6),
  },
];

const p4 = run(
  'P4 嵌套 repeat：外层按组、组内 tbody 再按 item.children 展开，组名/公式带 rowspan',
  table([...HEADERS, '备注'], [
    tbody(
      [
        {
          type: 'tbody',
          attributes: { [REPEAT_ROW]: 'g' },
          components: [
            row([
              cell('{{g.itemName}}', { rowspan: '{{g.span}}' }),
              cell('{{g.standardValue}}', { rowspan: '{{g.span}}' }),
            ]),
            tbody(
              [
                row(
                  [
                    cell('{{c.itemName}}'),
                    cell('{{c.standardValue}}'),
                    cell('{{c.actualValue}}'),
                    cell('{{c.unit}}'),
                    cell('{{c.resultText}}'),
                    cell(''),
                  ],
                  { [REPEAT_ROW]: 'c' },
                ),
              ],
              { [REPEAT]: 'g.children' },
            ),
          ],
        },
      ],
      { [REPEAT]: 'inspectionItems' },
    ),
  ]),
  { ...BASE_CONTEXT, inspectionItems: groupedItems },
);

// --------- P5 扁平 + rowspan + 隐藏重复单元格：数组里只有真实检验项，组头行靠绑定样式"让位"

/** 组信息挂在每个真实检验项上，组头单元格只在组内第一行可见并向下合并 */
const groupedByChild = [
  { itemName: '试样质量 m', actualValue: '50.2', unit: 'g', group: { name: '水分', formula: 'w = (m1 - m0) / m × 100%', span: 4, cellStyle: '' } },
  { itemName: '试样+称量瓶 m1', actualValue: '78.9', unit: 'g', group: { name: '', formula: '', span: 1, cellStyle: 'display:none' } },
  { itemName: '称量瓶 m0', actualValue: '28.7', unit: 'g', group: { name: '', formula: '', span: 1, cellStyle: 'display:none' } },
  { itemName: '水分结果 w', standardValue: '≤0.5', actualValue: '0.32', unit: '%', group: { name: '', formula: '', span: 1, cellStyle: 'display:none' } },
  { itemName: '正丁醇含量 /%', standardValue: '≥99.5', actualValue: '99.7', unit: '%', group: { name: '正丁醇含量', formula: '纯度 = 100% - 水分 - 酸含量 - 其它杂质', span: 1, cellStyle: '' } },
];

const p5 = run(
  'P5 扁平 + rowspan + 隐藏单元格：数组只装真实检验项，组头两列由绑定样式控制显隐',
  table(['检验项目', '检测要求', '子项', '标准值', '实测值', '单位', '判定'], [
    tbody(
      [
        row(
          [
            {
              type: 'cell',
              content: '{{item.group.name}}',
              attributes: { rowspan: '{{item.group.span}}', style: '{{item.group.cellStyle}}' },
            },
            {
              type: 'cell',
              content: '{{item.group.formula}}',
              attributes: { rowspan: '{{item.group.span}}', style: '{{item.group.cellStyle}}' },
            },
            cell('{{item.itemName}}'),
            cell('{{item.standardValue}}'),
            cell('{{item.actualValue}}'),
            cell('{{item.unit}}'),
            cell('{{item.resultText}}'),
          ],
          { [REPEAT_ROW]: 'item' },
        ),
      ],
      { [REPEAT]: 'inspectionItems' },
    ),
  ]),
  { ...BASE_CONTEXT, inspectionItems: groupedByChild },
);

// 把探针产物写到磁盘，供浏览器校验 rowspan 有没有真的合并、隐藏单元格是否被表格布局忽略
writeFileSync('.qc-conformance/group-probe-p4.html', p4.html, 'utf8');
writeFileSync('.qc-conformance/group-probe-p5.html', p5.html, 'utf8');

// eslint-disable-next-line no-console
console.log('\n========== 结论速览 ==========');
(
  [
    ['P1 扁平 repeat inspectionItems', p1],
    ['P2 rowspan 绑定', p2],
    ['P3 顶层自定义数组', p3],
    ['P4 嵌套 repeat（组内再按 children 展开）', p4],
    ['P5 扁平+rowspan+隐藏单元格', p5],
  ] as Array<[string, { body: string; errors: string[] }]>
).forEach(([label, result]) => {
  // eslint-disable-next-line no-console
  console.log(
    `${label}: tr 数=${(result.body.match(/<tr/g) ?? []).length} `< +
      `td 数=${(result.body.match(/<td/g) ?? []).length} ` +
      `未解析绑定=${result.errors.length} rowspan 出现=${result.body.includes('rowspan=')}`,
  );
});
