import { describe, expect, it } from "vitest";

import { mergeProcurementListObjects } from "./procurement-merge.ts";
import type {
  ProcurementListExtractionFields,
  ProcurementListExtractionPayload,
  ProcurementListPurchaseObject,
} from "./procurement-extraction-contract.ts";

type PurchaseObjectTestInput = Omit<Partial<ProcurementListPurchaseObject>, "fields"> & {
  object_role: ProcurementListPurchaseObject["object_role"];
  original_name: string;
  recommended_name: string;
  fields?: Partial<ProcurementListExtractionFields>;
};

function makeObject(
  partial: PurchaseObjectTestInput,
): ProcurementListPurchaseObject {
  return {
    object_role: partial.object_role,
    original_name: partial.original_name,
    recommended_name: partial.recommended_name,
    fields: {
      specification: "",
      material_requirement: "",
      technical_requirement: "",
      test_requirement: "",
      technical_standard: "",
      applicable_conditions: "",
      main_dimensions: "",
      tolerance: "",
      performance_indicators: "",
      accessory_materials: "",
      supply_form: "",
      acceptance_documents: "",
      quantity: 0,
      unit: "",
      invoice_type: "",
      delivery_address: "",
      remarks: "",
      pending_confirmations: [],
      ...partial.fields,
    },
    evidence: partial.evidence ?? [],
    warnings: partial.warnings ?? [],
    confidence: partial.confidence,
  };
}

function makePayload(sourceFileName: string, purchaseObjects: ProcurementListPurchaseObject[]): ProcurementListExtractionPayload & { source_file_name: string } {
  return {
    source_file_name: sourceFileName,
    file_profile: {
      source_file_name: sourceFileName,
      file_type: sourceFileName.endsWith(".xlsx") ? "excel" : sourceFileName.endsWith(".jpeg") ? "image" : "cad_drawing_pdf",
      document_title: sourceFileName.endsWith(".pdf") ? "嵌缝与螺栓防水构造图" : "",
      drawing_no: sourceFileName.endsWith(".pdf") ? "005A" : "",
      page_count: 1,
      diagnostics: sourceFileName.endsWith(".pdf") ? ["文本层较少，进入视觉理解"] : [],
    },
    purchase_objects: purchaseObjects,
    warnings: [],
  };
}

describe("mergeProcurementListObjects", () => {
  it("merges the sample XLSX, PDF, and JPEG protection-cover facts into one canonical item", () => {
    const result = mergeProcurementListObjects({
      extractionPayloads: [
        makePayload("塑料保护罩覆盖.xlsx", [
          makeObject({
            object_role: "primary_item",
            original_name: "塑料保护罩覆盖",
            recommended_name: "塑料保护罩",
            fields: {
              specification: "B1 级，低烟无卤，GB8624-2012 5.2.3 检测要求",
              quantity: 130000,
              unit: "个",
              invoice_type: "13%专票",
              delivery_address: "江苏省南京市建邺区",
            },
            evidence: [
              {
                source_file_name: "塑料保护罩覆盖.xlsx",
                source_type: "excel",
                field: "quantity",
                quote: "130000 个",
                row_index: 1,
                confidence: 0.98,
              },
            ],
            confidence: 0.9,
          }),
        ]),
        makePayload("塑料保护罩覆盖.pdf", [
          makeObject({
            object_role: "primary_item",
            original_name: "嵌缝与螺栓防水构造图",
            recommended_name: "螺栓孔塑料保护罩",
            fields: {
              specification: "盾构区间螺栓保护罩，图号 005A，适配 M30 螺栓孔",
              material_requirement: "低烟无卤原生材料",
              technical_requirement: "燃烧性能不低于 B1 级",
              technical_standard: "GB8624-2012 5.2.3",
              applicable_conditions: "盾构区间螺栓孔覆盖",
              pending_confirmations: ["是否按组件供货"],
            },
            evidence: [
              {
                source_file_name: "塑料保护罩覆盖.pdf",
                source_type: "pdf_render",
                field: "specification",
                quote: "M30 / 005A",
                page_number: 1,
                confidence: 0.75,
              },
            ],
            warnings: ["PDF 局部尺寸需要复核"],
            confidence: 0.74,
          }),
        ]),
        makePayload("塑料保护罩覆盖.jpeg", [
          makeObject({
            object_role: "primary_item",
            original_name: "塑料保护罩性能指标",
            recommended_name: "塑料保护罩",
            fields: {
              performance_indicators: "密度 0.91~1.2；拉伸强度 >=16MPa；相对伸长率 90%；氧指数 >=23",
            },
            evidence: [
              {
                source_file_name: "塑料保护罩覆盖.jpeg",
                source_type: "image",
                field: "performance_indicators",
                quote: "密度 0.91~1.2、拉伸强度 >=16MPa、相对伸长率 90%、氧指数 >=23",
                confidence: 0.95,
              },
            ],
            confidence: 0.93,
          }),
        ]),
      ],
    });

    expect(result.canonical_items).toHaveLength(1);
    expect(result.purchase_cards).toHaveLength(1);

    const canonical = result.canonical_items[0]!;
    expect(canonical.recommended_name).toBe("塑料保护罩");
    expect(canonical.original_name).toContain("塑料保护罩覆盖");
    expect(canonical.quantity).toBe(130000);
    expect(canonical.unit).toBe("个");
    expect(canonical.fields.invoice_type).toBe("13%专票");
    expect(canonical.fields.delivery_address).toBe("江苏省南京市建邺区");
    expect(canonical.specification).toContain("005A");
    expect(canonical.fields.material_requirement).toContain("低烟无卤");
    expect(canonical.fields.technical_requirement).toContain("B1");
    expect(canonical.fields.technical_standard).toContain("GB8624-2012");
    expect(canonical.fields.performance_indicators).toContain("氧指数 >=23");
    expect(canonical.can_search_1688).toBe(true);
    expect(canonical.missing_fields).toEqual([]);
    expect(canonical.human_fix_hint).toContain("可进入 1688 搜索");

    expect(result.confirmations).toEqual(expect.arrayContaining([
      expect.objectContaining({
        field_name: "pending_confirmations",
        issue_type: "uncertain",
        message: expect.stringContaining("是否按组件供货"),
      }),
    ]));
    expect(result.field_evidence).toEqual(expect.arrayContaining([
      expect.objectContaining({
        field_name: "performance_indicators",
        source_file_name: "塑料保护罩覆盖.jpeg",
      }),
      expect.objectContaining({
        field_name: "quantity",
        source_file_name: "塑料保护罩覆盖.xlsx",
        field_value: "130000",
      }),
    ]));
    expect(result.purchase_cards[0]?.field_sources.performance_indicators).toContain("塑料保护罩覆盖.jpeg");
  });

  it("classifies accessory objects separately and ignores construction or installation-only objects", () => {
    const result = mergeProcurementListObjects({
      extractionPayloads: [
        makePayload("附件.pdf", [
          makeObject({
            object_role: "primary_item",
            original_name: "塑料保护罩",
            recommended_name: "塑料保护罩",
            fields: {
              specification: "主件",
              quantity: 20,
              unit: "个",
            },
          }),
          makeObject({
            object_role: "accessory",
            original_name: "密封胶",
            recommended_name: "配套密封胶",
            fields: {
              accessory_materials: "密封胶",
              pending_confirmations: ["密封胶是否单独采购"],
            },
          }),
          makeObject({
            object_role: "construction_method",
            original_name: "施工前清理基层",
            recommended_name: "",
            fields: {
              technical_requirement: "施工前清理基层",
            },
          }),
        ]),
      ],
    });

    expect(result.canonical_items).toHaveLength(1);
    expect(result.canonical_items[0]?.fields.accessory_materials).toContain("密封胶");
    expect(result.ignored_objects).toEqual([
      expect.objectContaining({
        object_role: "construction_method",
        original_name: "施工前清理基层",
      }),
    ]);
    expect(result.confirmations).toEqual(expect.arrayContaining([
      expect.objectContaining({
        field_name: "accessory_materials",
        message: expect.stringContaining("密封胶是否单独采购"),
      }),
    ]));
  });

  it("reports conflicts, missing fields, and human fix hints when required procurement facts are incomplete", () => {
    const result = mergeProcurementListObjects({
      extractionPayloads: [
        makePayload("清单.xlsx", [
          makeObject({
            object_role: "primary_item",
            original_name: "保护罩",
            recommended_name: "保护罩",
            fields: {
              quantity: 10,
              unit: "个",
              invoice_type: "13%专票",
            },
          }),
        ]),
        makePayload("复核.xlsx", [
          makeObject({
            object_role: "primary_item",
            original_name: "保护罩",
            recommended_name: "保护罩",
            fields: {
              quantity: 12,
              unit: "套",
            },
          }),
        ]),
      ],
    });

    expect(result.canonical_items).toHaveLength(1);
    expect(result.canonical_items[0]).toMatchObject({
      quantity: 10,
      unit: "个",
      can_search_1688: false,
    });
    expect(result.canonical_items[0]?.missing_fields).toEqual(expect.arrayContaining([
      "specification",
      "delivery_address",
    ]));
    expect(result.canonical_items[0]?.human_fix_hint).toContain("补充");
    expect(result.confirmations).toEqual(expect.arrayContaining([
      expect.objectContaining({
        field_name: "quantity",
        issue_type: "conflict",
        message: expect.stringContaining("10"),
      }),
      expect.objectContaining({
        field_name: "unit",
        issue_type: "conflict",
        message: expect.stringContaining("套"),
      }),
    ]));
  });

  it("uses Excel rows as the canonical procurement list when Excel is present and uses other attachments only for detail enrichment", () => {
    const result = mergeProcurementListObjects({
      extractionPayloads: [
        makePayload("采购清单.xlsx", [
          makeObject({
            object_role: "primary_item",
            original_name: "电缆桥架",
            recommended_name: "电缆桥架",
            fields: {
              specification: "200x100",
              quantity: 30,
              unit: "米",
              invoice_type: "13%专票",
              delivery_address: "上海市浦东新区",
            },
          }),
        ]),
        makePayload("技术附件.pdf", [
          makeObject({
            object_role: "primary_item",
            original_name: "桥架安装详图",
            recommended_name: "热镀锌电缆桥架",
            fields: {
              specification: "200x100，厚度 1.5mm",
              material_requirement: "热镀锌钢板",
              technical_standard: "JB/T 10216",
            },
          }),
          makeObject({
            object_role: "primary_item",
            original_name: "膨胀螺栓节点",
            recommended_name: "膨胀螺栓",
            fields: {
              specification: "M10",
              quantity: 100,
              unit: "套",
            },
          }),
        ]),
      ],
    });

    expect(result.canonical_items).toHaveLength(1);
    expect(result.canonical_items[0]).toMatchObject({
      recommended_name: "电缆桥架",
      quantity: 30,
      unit: "米",
    });
    expect(result.canonical_items[0]?.fields.invoice_type).toBe("13%专票");
    expect(result.canonical_items[0]?.fields.delivery_address).toBe("上海市浦东新区");
    expect(result.canonical_items[0]?.fields.material_requirement).toContain("热镀锌钢板");
    expect(result.canonical_items[0]?.fields.technical_standard).toContain("JB/T 10216");
    expect(result.canonical_items[0]?.original_name).not.toContain("膨胀螺栓节点");
  });
});
