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

import {
  formatAuditDiffValue,
  getAuditDiffDisplayValue,
  getSafeAuditDiffFieldLabel,
  isSensitiveAuditDiff,
  parseAuditEventFieldDiffs,
  parseAuditFieldDiffs,
} from '#/components/FormExtraPanel/auditDiff';

import { getAuditActionLabel, groupTimelineOperations, normalizeAuditPageQuery, normalizeOperation } from '../auditOperation';

describe('audit operation normalization', () => {
  it('uses the BUSINESS row as the operation list row', () => {
    const operation = normalizeOperation({
      id: '100000000000000001',
      operationId: 'op-1',
      sourceLayer: 2,
      diffCount: 0,
    });

    expect(operation.diffCount).toBe(0);
    expect(operation.eventIds).toEqual(['100000000000000001']);
    expect(operation.layers).toEqual([2]);
    expect(operation.sourceSummary).toBe('业务动作');
    expect(operation.subEvents).toEqual([]);
  });

  it('retains a layer 0 event without an operation id', () => {
    const operation = normalizeOperation({
      id: 7,
      operationId: null,
      clientEventId: 'visual-log-7',
      sourceLayer: 0,
      diffCount: 0,
    });

    expect(operation.correlationKey).toBe('visual-log-7');
    expect(operation.sourceSummary).toBe('在线表单');
    expect(operation.eventIds).toEqual([7]);
  });

  it('labels read events as queries', () => {
    expect(getAuditActionLabel({ actionCode: 'READ', actionLabel: '查询列表' })).toBe('查询');
  });

  it('associates signature rows without adding their diffs to the business summary', () => {
    const operation = normalizeOperation({
      id: 8,
      operationId: 'op-sign-used',
      sourceLayer: 2,
      diffCount: 2,
      subEvents: [
        { id: 9, operationId: 'op-sign-used', sourceLayer: 3, eventType: 'E_SIGNATURE', diffCount: 5 },
      ],
    });

    expect(operation.diffCount).toBe(2);
    expect(operation.eventIds).toEqual([8, 9]);
    expect(operation.layers).toEqual([2, 3]);
    expect(operation.sourceSummary).toBe('业务动作 + 电子签名 + 字段变更');
    expect(operation.subEvents).toHaveLength(1);
  });

  it('labels a standalone signature event as electronic signature evidence', () => {
    const operation = normalizeOperation({
      id: 10,
      sourceLayer: 3,
      diffCount: 0,
      actionCode: 'SIGN',
    });

    expect(operation.sourceSummary).toBe('电子签名');
  });

  it('keeps the id-based order supplied by the timeline endpoint', () => {
    const operations = groupTimelineOperations([
      {
        id: 11,
        operationId: 'op-2',
        sourceLayer: 2,
        eventTime: '2026-07-28 10:01:00',
        diffCount: 2,
        subEvents: [{ id: 12, operationId: 'op-2', sourceLayer: 3, eventType: 'E_SIGNATURE' }],
      },
      { id: 21, operationId: null, clientEventId: 'client-21', sourceLayer: 0, eventTime: '2026-07-28 10:00:00', diffCount: 1 },
    ]);

    expect(operations).toHaveLength(2);
    const firstOperation = operations[0]!;
    const secondOperation = operations[1]!;
    expect(firstOperation.id).toBe(11);
    expect(firstOperation.eventIds).toEqual([11, 12]);
    expect(secondOperation.id).toBe(21);
  });

  it('keeps one BUSINESS row with primary and review signatures as switchable details', () => {
    const operations = groupTimelineOperations([
      {
        id: 31,
        operationId: 'BS-839875756384423365',
        eventType: 'BIZ_ACTION',
        sourceLayer: 2,
        diffCount: 0,
        subEvents: [
          {
            id: 33,
            operationId: 'BS-839875756384423365',
            eventType: 'E_SIGNATURE',
            signatureRole: 'PRIMARY',
            sourceLayer: 0,
          },
          {
            id: 34,
            operationId: 'BS-839875756384423365',
            eventType: 'E_SIGNATURE',
            signatureRole: 'REVIEW',
            sourceLayer: 0,
          },
        ],
      },
    ]);

    expect(operations).toHaveLength(1);
    expect(operations[0]?.correlationKey).toBe('BS-839875756384423365');
    expect(operations[0]?.eventIds).toEqual([31, 33, 34]);
    expect(operations[0]?.subEvents.map((event) => event.signatureRole)).toEqual(['PRIMARY', 'REVIEW']);
  });

  it('trims optional filters without discarding pagination', () => {
    expect(normalizeAuditPageQuery({ currentPage: 2, pageSize: 20, operatorName: '  张三  ', bizCode: ' ' })).toEqual({
      currentPage: 2,
      pageSize: 20,
      operatorName: '张三',
    });
  });
});

describe('audit field diff safety', () => {
  it('keeps original values when nameModified is true', () => {
    const result = parseAuditFieldDiffs('[{"field":"display_name","nameModified":true,"oldData":"old-name","newData":"new-name"}]');

    expect(result.parseError).toBe(false);
    const diff = result.diffs[0]!;
    expect(diff.nameModified).toBe(true);
    expect(diff.oldData).toBe('old-name');
    expect(diff.newData).toBe('new-name');
  });

  it('prefers structured display semantics over legacy raw values', () => {
    const result = parseAuditEventFieldDiffs(
      [
        {
          componentType: 'select',
          field: 'biz_status',
          fieldName: '请验状态',
          masked: false,
          newData: 'confirmed',
          newDisplay: '已提交',
          oldData: 'created',
          oldDisplay: '已创建',
          technical: false,
          valueType: 'enum',
        },
      ],
      '[{"field":"legacy_only","newData":"legacy"}]',
    );

    expect(result.parseError).toBe(false);
    expect(result.diffs).toHaveLength(1);
    const diff = result.diffs[0]!;
    expect(getAuditDiffDisplayValue(diff, 'old')).toBe('已创建');
    expect(getAuditDiffDisplayValue(diff, 'new')).toBe('已提交');
    expect(diff.componentType).toBe('select');
    expect(diff.valueType).toBe('enum');
    expect(diff.technical).toBe(false);
  });

  it('falls back to raw values when display names are unavailable', () => {
    const diff = {
      newData: 'new-value',
      newDisplay: ' 名称暂不可用 ',
      oldData: 'old-value',
      oldDisplay: '名称暂不可用',
    };

    expect(getAuditDiffDisplayValue(diff, 'old')).toBe('old-value');
    expect(getAuditDiffDisplayValue(diff, 'new')).toBe('new-value');
  });

  it('treats an empty structured list as authoritative', () => {
    expect(parseAuditEventFieldDiffs([], '[{"field":"legacy_only","newData":"legacy"}]')).toEqual({ diffs: [], parseError: false });
  });

  it('falls back to legacy fieldDiffs when the structured field is absent', () => {
    const result = parseAuditEventFieldDiffs(undefined, '[{"field":"qingyan_beizhu","fieldName":"请验备注","newData":"已更新"}]');

    expect(result.parseError).toBe(false);
    expect(result.diffs[0]?.fieldName).toBe('请验备注');
  });

  it('keeps child-table rows and columns in the structured audit contract', () => {
    const result = parseAuditEventFieldDiffs(
      [
        {
          chidData: [{ jnpf_old_name: '旧项目', jnpf_type: 1, name: '新项目' }],
          chidField: [{ jnpfKey: 'input', label: '项目', prop: 'name' }],
          field: 'tableField1',
          fieldName: '检测项目',
          jnpfKey: 'table',
        },
      ],
      undefined,
    );

    expect(result.parseError).toBe(false);
    expect(result.diffs[0]?.chidData).toHaveLength(1);
    expect(result.diffs[0]?.chidField?.[0]?.label).toBe('项目');
  });

  it('drops raw and display values from the structured masked contract', () => {
    const result = parseAuditEventFieldDiffs(
      [
        {
          componentType: 'sign',
          field: 'biz_sign',
          fieldName: '电子签名',
          masked: true,
          newData: 'must-not-render',
          newDisplay: 'must-not-render',
          oldData: 'must-not-render',
          oldDisplay: 'must-not-render',
          valueType: 'masked',
        },
      ],
      undefined,
    );

    const diff = result.diffs[0]!;
    expect(diff.masked).toBe(true);
    expect(diff).not.toHaveProperty('oldData');
    expect(diff).not.toHaveProperty('newData');
    expect(diff).not.toHaveProperty('oldDisplay');
    expect(diff).not.toHaveProperty('newDisplay');
    expect(isSensitiveAuditDiff(diff)).toBe(true);
  });

  it('degrades malformed JSON without throwing', () => {
    expect(parseAuditFieldDiffs('{bad json')).toEqual({ diffs: [], parseError: true });
  });

  it('keeps untrusted markup as plain text for Vue interpolation', () => {
    expect(formatAuditDiffValue('<img src=x onerror=alert(1)>')).toBe('<img src=x onerror=alert(1)>');
    expect(formatAuditDiffValue({ enabled: false, count: 0 })).toBe('{"enabled":false,"count":0}');
  });

  it('detects sensitive fields from technical and display names', () => {
    expect(isSensitiveAuditDiff({ field: 'mobilePhone' })).toBe(true);
    expect(isSensitiveAuditDiff({ field: 'bankCardNo' })).toBe(true);
    expect(isSensitiveAuditDiff({ fieldName: '身份证号码' })).toBe(true);
    expect(isSensitiveAuditDiff({ field: 'contact', fieldName: '联系电话' })).toBe(true);
    expect(isSensitiveAuditDiff({ field: 'sampleName', fieldName: '样品名称' })).toBe(false);
    expect(isSensitiveAuditDiff({ field: 'hotelName' })).toBe(false);
    expect(isSensitiveAuditDiff({ field: 'biz_sign' })).toBe(true);
    expect(isSensitiveAuditDiff({ fieldName: '签名图片', jnpfKey: 'sign' })).toBe(true);
    expect(isSensitiveAuditDiff({ fieldName: '电子签章', jnpfKey: 'signature' })).toBe(true);
  });

  it('does not expose database identifiers as business field labels', () => {
    expect(getSafeAuditDiffFieldLabel({ field: 'chanpin_mingcheng', fieldName: '产品名称' })).toBe('产品名称');
    expect(getSafeAuditDiffFieldLabel({ field: 'biz_status', fieldName: 'biz_status' }, 1)).toBe('未配置字段名称 2');
    expect(getSafeAuditDiffFieldLabel({ field: 'biz_sign', fieldName: 'biz_sign' })).toBe('电子签名');
  });
});
