/* @vitest-environment happy-dom */

import type { SharedFillPreview } from '#/api/x/eln/records';

import { flushPromises, mount } from '@vue/test-utils';

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

import SharedFillView from '../index.vue';
import { buildSharedFillCommand, ElnSharedFillParamError, resolveTestWorkItemIds } from '../sharedFillView';

const apiMocks = vi.hoisted(() => ({
  createRecordsBatch: vi.fn(),
  executeSharedFill: vi.fn(),
  previewSharedFill: vi.fn(),
}));
const onlineMocks = vi.hoisted(() => ({ getViewParam: vi.fn() }));

vi.mock('#/api/x/eln/records', () => ({
  createRecordsBatch: apiMocks.createRecordsBatch,
  executeSharedFill: apiMocks.executeSharedFill,
  previewSharedFill: apiMocks.previewSharedFill,
}));
vi.mock('#/utils/jnpf', () => ({ onlineUtils: onlineMocks }));

const PREVIEW: SharedFillPreview = {
  fields: [
    {
      conflictingValues: false,
      dataType: 'DECIMAL',
      defaultExcludedTestWorkItemIds: [],
      fieldCode: 'temperature',
      fieldName: '环境温度',
      fieldVersionId: 'version-temperature',
      targetTestWorkItemIds: ['item-1', 'item-2'],
      unit: 'C',
    },
    {
      conflictingValues: true,
      dataType: 'DECIMAL',
      defaultExcludedTestWorkItemIds: ['item-2'],
      fieldCode: 'pressure',
      fieldName: '环境压力',
      fieldVersionId: 'version-pressure',
      targetTestWorkItemIds: ['item-1', 'item-2'],
      unit: 'kPa',
    },
  ],
  previewToken: 'preview-1',
  testDefinitionCode: 'IGNITION_RESIDUE',
  testDefinitionId: 'definition-1',
  testDefinitionName: '炽灼残渣',
  targets: [
    {
      currentValues: { 'version-pressure': '', 'version-temperature': '' },
      eligible: true,
      recordId: 'record-1',
      recordVersion: 3,
      revisionId: 'revision-1',
      testWorkItemId: 'item-1',
      testWorkItemName: '炽灼残渣',
    },
    {
      currentValues: { 'version-pressure': '100', 'version-temperature': '' },
      eligible: true,
      recordId: 'record-2',
      recordVersion: 5,
      revisionId: 'revision-2',
      testWorkItemId: 'item-2',
      testWorkItemName: '炽灼残渣',
    },
  ],
};

describe('eLN shared fill view', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    onlineMocks.getViewParam.mockReturnValue(['item-1', 'item-2']);
    apiMocks.previewSharedFill.mockResolvedValue({ data: PREVIEW });
    apiMocks.createRecordsBatch.mockResolvedValue({ data: { items: [] } });
    apiMocks.executeSharedFill.mockResolvedValue({
      data: { fieldVersionIds: ['version-temperature'], operationCode: '', targets: [] },
    });
  });

  it('requires at least two unique test work items', () => {
    expect(resolveTestWorkItemIds([' item-1 ', 'item-2', 'item-1'])).toEqual(['item-1', 'item-2']);
    expect(() => resolveTestWorkItemIds(['item-1'])).toThrow(ElnSharedFillParamError);
  });

  it('builds one multi-field request and requires a reason for a reselected target', () => {
    expect(() =>
      buildSharedFillCommand(PREVIEW, {
        fieldValues: { 'version-pressure': '101.325', 'version-temperature': '20.5' },
        operationCode: 'shared-fill-1',
        overwriteReasons: {},
        selectedFieldIds: ['version-temperature', 'version-pressure'],
        selectedTargetIds: ['item-1', 'item-2'],
      }),
    ).toThrow('请填写 item-2 的覆盖原因');

    expect(
      buildSharedFillCommand(PREVIEW, {
        fieldValues: { 'version-pressure': '101.325', 'version-temperature': '20.5' },
        operationCode: 'shared-fill-1',
        overwriteReasons: { 'item-2': '复测后统一修正' },
        selectedFieldIds: ['version-temperature', 'version-pressure'],
        selectedTargetIds: ['item-1', 'item-2'],
      }),
    ).toEqual({
      operationCode: 'shared-fill-1',
      previewToken: 'preview-1',
      testWorkItemIds: ['item-1', 'item-2'],
      targets: [
        { expectedRecordVersion: 3, overwrite: false, recordId: 'record-1', testWorkItemId: 'item-1' },
        {
          expectedRecordVersion: 5,
          overwrite: true,
          overwriteReason: '复测后统一修正',
          recordId: 'record-2',
          testWorkItemId: 'item-2',
        },
      ],
      values: [
        { fieldVersionId: 'version-temperature', value: '20.5' },
        { fieldVersionId: 'version-pressure', value: '101.325' },
      ],
    });
  });

  it('does not require an overwrite reason when the requested decimal equals the target value', () => {
    const command = buildSharedFillCommand(PREVIEW, {
      fieldValues: { 'version-pressure': '100.0' },
      operationCode: 'shared-fill-same-value',
      overwriteReasons: {},
      selectedFieldIds: ['version-pressure'],
      selectedTargetIds: ['item-2'],
    });

    expect(command.targets).toEqual([{ expectedRecordVersion: 5, overwrite: false, recordId: 'record-2', testWorkItemId: 'item-2' }]);
  });

  it('previews the selected test work items on mount', async () => {
    const wrapper = mount(SharedFillView);
    await flushPromises();

    expect(apiMocks.previewSharedFill).toHaveBeenCalledWith({ testWorkItemIds: ['item-1', 'item-2'] });
    expect(wrapper.get('[data-test="shared-fill-fields"]').text()).toContain('环境温度');
    expect(wrapper.text()).toContain('检验项目 炽灼残渣');
    expect(wrapper.get('[data-test="shared-fill-targets"]').text()).toContain('炽灼残渣');
    expect(wrapper.text()).not.toContain('definition-1');
    expect(wrapper.get('[data-test="shared-fill-targets"]').text()).not.toContain('item-2');
  });

  it('uses the shared alert spacing when preview fails', async () => {
    apiMocks.previewSharedFill.mockRejectedValueOnce(new Error('批量填写只允许选择同一检验项目'));
    const wrapper = mount(SharedFillView);
    await flushPromises();

    expect(wrapper.get('[data-test="shared-fill-error"]').classes()).toContain('shared-fill__alert');
  });

  it('creates only missing records and refreshes the preview', async () => {
    const missingPreview: SharedFillPreview = {
      ...PREVIEW,
      fields: [],
      targets: [
        {
          currentValues: {},
          eligible: false,
          errorCode: 'RECORD_NOT_FOUND',
          errorMessage: '检验项电子记录不存在',
          testWorkItemId: 'item-1',
          testWorkItemName: '炽灼残渣',
        },
        PREVIEW.targets[1]!,
      ],
    };
    apiMocks.previewSharedFill.mockResolvedValueOnce({ data: missingPreview }).mockResolvedValueOnce({ data: PREVIEW });
    apiMocks.createRecordsBatch.mockResolvedValue({
      data: {
        items: [
          {
            created: { document: { fileId: 'file-1', name: 'record.docx' }, existing: false, recordId: 'record-1', revisionId: 'revision-1' },
            status: 'CREATED',
            testWorkItemId: 'item-1',
          },
        ],
      },
    });
    const randomUUID = vi.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue('00000000-0000-4000-8000-000000000003');
    const wrapper = mount(SharedFillView);
    await flushPromises();

    expect(wrapper.get('[data-test="shared-fill-missing-records"]').text()).toContain('1 条检验项尚未创建电子记录');
    await wrapper.get('[data-test="shared-fill-create-missing"]').trigger('click');
    await flushPromises();

    expect(apiMocks.createRecordsBatch).toHaveBeenCalledWith({
      operationCode: 'record-batch-00000000-0000-4000-8000-000000000003',
      testWorkItemIds: ['item-1'],
    });
    expect(apiMocks.previewSharedFill).toHaveBeenCalledTimes(2);
    expect(wrapper.find('[data-test="shared-fill-missing-records"]').exists()).toBe(false);
    randomUUID.mockRestore();
  });

  it('keeps per-record creation failures after refreshing successful records', async () => {
    const missingTarget = (testWorkItemId: string) => ({
      currentValues: {},
      eligible: false,
      errorCode: 'RECORD_NOT_FOUND',
      errorMessage: '检验项电子记录不存在',
      testWorkItemId,
      testWorkItemName: '炽灼残渣',
    });
    const missingPreview: SharedFillPreview = {
      ...PREVIEW,
      fields: [],
      targets: [missingTarget('item-1'), missingTarget('item-2')],
    };
    const refreshedPreview: SharedFillPreview = {
      ...PREVIEW,
      targets: [PREVIEW.targets[0]!, missingTarget('item-2')],
    };
    apiMocks.previewSharedFill.mockResolvedValueOnce({ data: missingPreview }).mockResolvedValueOnce({ data: refreshedPreview });
    apiMocks.createRecordsBatch.mockResolvedValue({
      data: {
        items: [
          {
            created: { document: { fileId: 'file-1', name: 'record.docx' }, existing: false, recordId: 'record-1', revisionId: 'revision-1' },
            status: 'CREATED',
            testWorkItemId: 'item-1',
          },
          { errorMessage: '未配置可用模板', status: 'FAILED', testWorkItemId: 'item-2' },
        ],
      },
    });
    const randomUUID = vi.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue('00000000-0000-4000-8000-000000000004');
    const wrapper = mount(SharedFillView);
    await flushPromises();

    await wrapper.get('[data-test="shared-fill-create-missing"]').trigger('click');
    await flushPromises();

    expect(apiMocks.previewSharedFill).toHaveBeenCalledTimes(2);
    expect(wrapper.get('[data-test="shared-fill-error"]').text()).toContain('炽灼残渣（item-2）：未配置可用模板');
    expect(wrapper.get('[data-test="shared-fill-missing-records"]').text()).toContain('1 条检验项尚未创建电子记录');
    randomUUID.mockRestore();
  });

  it('reuses an operation code for a retry and rotates it after a new preview', async () => {
    const randomUUID = vi
      .spyOn(globalThis.crypto, 'randomUUID')
      .mockReturnValueOnce('00000000-0000-4000-8000-000000000001')
      .mockReturnValueOnce('00000000-0000-4000-8000-000000000002');
    const wrapper = mount(SharedFillView);
    await flushPromises();

    const fieldCheckbox = wrapper.findAll('input[type="checkbox"]')[0]!;
    await fieldCheckbox.setValue(true);
    await wrapper.get('input[placeholder="填写统一值"]').setValue('20.5');
    const confirm = wrapper.findAll('button').find((button) => button.text().includes('确认填写'))!;

    await confirm.trigger('click');
    await flushPromises();
    await confirm.trigger('click');
    await flushPromises();

    const firstCode = apiMocks.executeSharedFill.mock.calls[0]![0].operationCode;
    expect(apiMocks.executeSharedFill.mock.calls[1]![0].operationCode).toBe(firstCode);

    const reload = wrapper.findAll('button').find((button) => button.text().includes('重新预览'))!;
    await reload.trigger('click');
    await flushPromises();
    await wrapper.findAll('input[type="checkbox"]')[0]!.setValue(true);
    await wrapper.get('input[placeholder="填写统一值"]').setValue('20.5');
    const nextConfirm = wrapper.findAll('button').find((button) => button.text().includes('确认填写'))!;
    await nextConfirm.trigger('click');
    await flushPromises();

    expect(apiMocks.executeSharedFill.mock.calls[2]![0].operationCode).not.toBe(firstCode);
    randomUUID.mockRestore();
  });
});
