/**
 * 报告上下文（Context）
 * <p>
 * 模板里的 {{path}} 全部相对这个结构解析：report 是报告级字段，inspectionItems 是检验项数组。
 * 业务单据 → 上下文的映射由上下文构建器负责，模板本身只认这个标准结构，
 * 换行业、换单据来源都不需要改模板与组件。
 */

/** 判定结果取值，组件与规则引擎共用 */
export const QUALITY_RESULT = {
  PASS: 'PASS',
  FAIL: 'FAIL',
} as const;

export type QualityResult = (typeof QUALITY_RESULT)[keyof typeof QUALITY_RESULT];

/** 判定结果对应的展示文字 */
export const QUALITY_RESULT_TEXT: Record<QualityResult, string> = {
  PASS: '合格',
  FAIL: '不合格',
};

/** 报告级字段 */
export interface ReportContextReport {
  reportNo: string;
  reportName: string;
  sampleNo: string;
  productCode: string;
  productName: string;
  spec: string;
  batchNo: string;
  workOrderNo: string;
  inspectType: string;
  inspector: string;
  inspectDate: string;
  department: string;
  customerName: string;
  supplierName: string;
  /** PASS / FAIL */
  result: string;
  /** 合格 / 不合格 / 待判定 */
  resultText: string;
  conclusion: string;
  passRate: string;
  total: number;
  passCount: number;
  failCount: number;
  /**
   * 质检单上的原判定（数字字符串 "1"~"4"，来自 MES 单据）。
   * <p>
   * 与引擎算出的 {@link result} 并存、互不覆盖：单据判定是人工拍的板
   * （合格 / 特采 / 不合格退货 / 不合格报废，四态），而引擎只有 PASS/FAIL/待判定 三态装不下它。
   * 仅「由质检单出件」时有值，通用出件的上下文里恒为空串。
   */
  qcResult: string;
  /** {@link qcResult} 的中文，如「合格」「特采」「不合格退货」「不合格报废」 */
  qcResultText: string;
}

/**
 * 检验项的分组信息。
 * <p>
 * 检验单里的父子层级（分组头 + 组内子项）只有两项数据能表达：组名合并几行、
 * 子项行要不要让出合并格。渲染期无法自己算出这两件事 —— 绑定的路径在数组上继续取属性
 * 会按元素 pluck，`group.children.length` 取不到值 —— 所以必须由上下文构建器逐项下发。
 * <p>
 * 这几项在**每一个**检验项上都必须有值（哪怕是空串）：绑定的字段缺席时属性会被跳过，
 * 合并格反而会露出来。没有分组时整个 {@link ReportContextItemGroup} 可以不给。
 */
export interface ReportContextItemGroup {
  /** 「子项」列文字：组内子项 = 自己的名字，组头行与独立项 = 空串 */
  childName: string;
  /** 组名格的纵向合并行数：组头行 = 1 + 组内子项数，组内子项行与独立项 = 1 */
  span: number;
  /**
   * 组名格的隐藏值：组内子项行 = 'hidden'（该格让位给上方合并格），组头行与独立项 = 空串。
   * <p>
   * 字符串而不是布尔：`hidden` 属性靠「在不在」起作用（`hidden="false"` 照样隐藏），
   * 只有空串能表达「不输出该属性 = 这一格可见」。
   */
  hidden: string;
}

/** 单个检验项 */
export interface ReportContextItem {
  index: number;
  itemCode: string;
  itemName: string;
  standardValue: string;
  actualValue: string;
  unit: string;
  /**
   * 「检测要求」文本：分组父项 = 指标上的计算公式，其余 = 单据上的标准要求。
   * <p>
   * 与 {@link standardValue} 并存而不合并，是因为两者的来源不同：标准要求是检验单行上的值，
   * 公式是检验项主数据上的值；分组父子项压根没有规格，只有公式。
   */
  requirement?: string;
  /**
   * 「检测方法」文本，来自单据行（如 GB 5009.3-2016）。
   * <p>
   * 与 {@link requirement} 并存而不合并：检测要求说的是「这一项要达到什么」，
   * 检测方法说的是「用什么办法测的」，原件里常各占一列。
   */
  checkMethod?: string;
  /** 分组信息，仅在检验单存在分组结构时逐项下发 */
  group?: ReportContextItemGroup;
  /** 规格上限，规则引擎做区间判定时使用 */
  upperLimit?: number;
  /** 规格下限 */
  lowerLimit?: number;
  /** PASS / FAIL */
  result: string;
  /**
   * 合格 / 不合格 / 待判定 / 无判定规则。
   * <p>
   * 后两者都不是结论：待判定是规则跑挂了（仍计入合格率分母），无判定规则是这项没有判定依据（不计入）。
   */
  resultText: string;
  remark: string;
}

/** 报告上下文 */
export interface ReportContext {
  report: ReportContextReport;
  inspectionItems: ReportContextItem[];
}

function emptyReport(): ReportContextReport {
  return {
    reportNo: '',
    reportName: '',
    sampleNo: '',
    productCode: '',
    productName: '',
    spec: '',
    batchNo: '',
    workOrderNo: '',
    inspectType: '',
    inspector: '',
    inspectDate: '',
    department: '',
    customerName: '',
    supplierName: '',
    result: '',
    resultText: '',
    conclusion: '',
    passRate: '',
    total: 0,
    passCount: 0,
    failCount: 0,
    qcResult: '',
    qcResultText: '',
  };
}

/** 空白上下文，字段齐全但没有值，便于逐项填充 */
export function createEmptyContext(): ReportContext {
  return { report: emptyReport(), inspectionItems: [] };
}

/** 用传入值覆盖空白上下文，未传的字段保留空值 */
export function createContext(input?: Partial<ReportContext>): ReportContext {
  const base = createEmptyContext();
  return {
    report: { ...base.report, ...input?.report },
    inspectionItems: input?.inspectionItems ?? base.inspectionItems,
  };
}