/* @vitest-environment happy-dom */

import type { FieldDataType } from '#/api/x/eln/fields';

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

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

import SourceWorkspace from '../SourceWorkspace.vue';

const apiMocks = vi.hoisted(() => ({
  createSourceDataItem: vi.fn(),
  createSourcePath: vi.fn(),
  getSourceDataItems: vi.fn(),
  getSourceObjectFields: vi.fn(),
  getSourceObjects: vi.fn(),
  getSourcePaths: vi.fn(),
  getSourcePreviewSamples: vi.fn(),
  previewSourceDataItem: vi.fn(),
  publishSourceDataItem: vi.fn(),
  saveSourceDataItem: vi.fn(),
  setSourcePropertyEnabled: vi.fn(),
  suggestFieldCode: vi.fn(),
}));
const navigationMocks = vi.hoisted(() => ({ route: vi.fn() }));

vi.mock('#/api/x/eln/fields', () => ({ suggestFieldCode: apiMocks.suggestFieldCode }));
vi.mock('#/api/x/eln/sources', () => apiMocks);
vi.mock('#/utils/jnpf', () => ({ onlineUtils: navigationMocks }));
vi.mock('@jnpf/ui/drawer', () => ({
  BasicDrawer: { name: 'BasicDrawer', template: '<aside><slot /><slot name="footer" /></aside>' },
  useDrawer: () => [vi.fn(), { closeDrawer: vi.fn(), openDrawer: vi.fn() }],
}));
vi.mock('@jnpf/ui/form', () => ({
  BasicForm: { name: 'BasicForm', template: '<form />' },
  useForm: () => [vi.fn(), { getFieldsValue: vi.fn(() => ({})), resetFields: vi.fn() }],
}));
vi.mock('@jnpf/ui/modal', () => ({
  BasicModal: { name: 'BasicModal', template: '<section><slot /></section>' },
  useModal: () => [vi.fn(), { openModal: vi.fn() }],
}));
vi.mock('@jnpf/ui/vxeTable', () => ({
  BasicVxeTable: {
    name: 'BasicVxeTable',
    props: { dataSource: { default: () => [], type: Array } },
    template:
      '<div><slot name="tableTitle" /><div v-for="record in dataSource" :key="record.id"><span>{{ record.displayName }}</span><span>{{ record.sourcePath || record.path }}</span><slot name="dataType" :record="record" /><slot name="status" :record="record" /><slot name="action" :record="record" /></div></div>',
  },
  TableAction: {
    name: 'TableAction',
    props: ['actions'],
    template: '<span><button v-for="action in actions" :key="action.label" @click="action.onClick">{{ action.label }}</button></span>',
  },
  useVxeTable: () => [vi.fn(), { setLoading: vi.fn() }],
}));

const publishedItem = {
  dataType: 'DATE',
  displayName: '生产日期',
  editVersion: 1,
  enabled: true,
  id: 'item-1',
  outputKey: 'production_date',
  outputName: '生产日期',
  propertyCode: 'yangpin_shengchan_riqi',
  returnContent: '原值',
  revision: 1,
  scope: '全部检验项',
  sourcePath: '检验项 > 请验单 > 生产日期',
  sourcePathId: 'path-1',
  status: 'PUBLISHED',
};

function mountWorkspace(fieldDataType?: FieldDataType) {
  return mount(SourceWorkspace, {
    props: fieldDataType ? { fieldDataType } : {},
    global: {
      stubs: {
        'a-alert': { template: '<div><slot /><slot name="action" /></div>' },
        'a-button': {
          emits: ['click'],
          props: ['disabled'],
          template: '<button :disabled="disabled" @click="$emit(\'click\')"><slot name="icon" /><slot /></button>',
        },
        'a-input': {
          emits: ['blur', 'update:value'],
          props: ['disabled', 'value'],
          template: '<input :disabled="disabled" :value="value" @blur="$emit(\'blur\')" @input="$emit(\'update:value\', $event.target.value)" />',
        },
        'a-select': {
          emits: ['change', 'update:value'],
          props: ['options', 'value'],
          template:
            '<select :value="value" @change="$emit(\'update:value\', $event.target.value); $emit(\'change\', $event.target.value)"><option value=""></option><option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}</option></select>',
        },
        'a-tag': { template: '<span><slot /></span>' },
        'a-tooltip': { template: '<span><slot /></span>' },
      },
    },
  });
}

describe('lIMS 数据项工作台', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    apiMocks.getSourceDataItems.mockResolvedValue({ data: [publishedItem] });
    apiMocks.getSourcePaths.mockResolvedValue({
      data: [
        {
          code: 'request',
          displayName: '所属请验单',
          editVersion: 1,
          id: 'path-1',
          path: '检验项 > 请验单 > 生产日期',
          outputs: [{ dataType: 'DATE', displayName: '生产日期', key: 'production_date', path: '检验项 > 请验单 > 生产日期', returnContent: '原值' }],
          revision: 1,
          scope: '全部检验项',
          status: 'PUBLISHED',
        },
      ],
    });
    apiMocks.getSourcePreviewSamples.mockResolvedValue({ data: [{ batchNo: 'B001', id: 'task-1', sampleName: '样品 A', testName: '含量', testNo: 'JY001' }] });
    apiMocks.suggestFieldCode.mockResolvedValue({ data: { code: 'yangpin_shengchan_riqi' } });
    apiMocks.createSourceDataItem.mockResolvedValue({ data: { ...publishedItem, editVersion: 0, id: 'draft-1', status: 'DRAFT' } });
    apiMocks.saveSourceDataItem.mockResolvedValue({ data: { ...publishedItem, editVersion: 0, id: 'draft-2', revision: 2, status: 'DRAFT' } });
    apiMocks.previewSourceDataItem.mockResolvedValue({ data: { value: '2026-09-07' } });
    apiMocks.publishSourceDataItem.mockResolvedValue({ data: publishedItem });
    apiMocks.setSourcePropertyEnabled.mockResolvedValue({ data: false });
  });

  it('按业务语言展示并可选用已发布数据项', async () => {
    const wrapper = mountWorkspace('DATE');
    await flushPromises();

    expect(wrapper.text()).toContain('新增数据项');
    expect(wrapper.text()).toContain('检验项 > 请验单 > 生产日期');
    expect(wrapper.text()).not.toContain('创建新版本');
    await wrapper
      .findAll('button')
      .find((button) => button.text() === '选用')!
      .trigger('click');
    expect(wrapper.emitted('select')?.[0]?.[0]).toMatchObject({ property: 'yangpin_shengchan_riqi', propertyRevision: 1 });
  });

  it('输入中文业务名称后自动生成只读标识并保存草稿', async () => {
    const wrapper = mountWorkspace();
    await flushPromises();
    await wrapper.get('[data-test="new-source-property"]').trigger('click');
    const displayName = wrapper.get('[data-test="source-display-name"]');
    await displayName.setValue('样品生产日期');
    await displayName.trigger('blur');
    await flushPromises();

    expect(apiMocks.suggestFieldCode).toHaveBeenCalledWith('样品生产日期');
    const propertyCode = wrapper.get('[data-test="source-property-code"]');
    expect(propertyCode.attributes('disabled')).toBeDefined();
    expect((propertyCode.element as HTMLInputElement).value).toBe('yangpin_shengchan_riqi');
    await wrapper.get('[data-test="source-path-select"]').setValue('path-1:production_date');
    await wrapper
      .findAll('button')
      .find((button) => button.text() === '保存草稿')!
      .trigger('click');
    await flushPromises();
    expect(apiMocks.createSourceDataItem).toHaveBeenCalledWith(
      expect.objectContaining({
        displayName: '样品生产日期',
        propertyCode: 'yangpin_shengchan_riqi',
        sourcePathId: 'path-1',
        outputKey: 'production_date',
      }),
    );
  });

  it('编辑已发布项时由后端自动创建草稿版本', async () => {
    const wrapper = mountWorkspace();
    await flushPromises();
    await wrapper
      .findAll('button')
      .find((button) => button.text() === '编辑')!
      .trigger('click');
    await wrapper
      .findAll('button')
      .find((button) => button.text() === '保存草稿')!
      .trigger('click');
    await flushPromises();
    expect(apiMocks.saveSourceDataItem).toHaveBeenCalledWith('item-1', expect.objectContaining({ expectedEditVersion: 1 }));
  });

  it('通过业务选择器预览，不要求手填检验项 ID', async () => {
    const wrapper = mountWorkspace();
    await flushPromises();
    await wrapper
      .findAll('button')
      .find((button) => button.text() === '编辑')!
      .trigger('click');
    await flushPromises();
    await wrapper.get('[data-test="source-sample-select"]').setValue('task-1');
    await wrapper
      .findAll('button')
      .find((button) => button.text() === '预览')!
      .trigger('click');
    await flushPromises();
    expect(apiMocks.previewSourceDataItem).toHaveBeenCalledWith('draft-2', 'task-1');
    expect(wrapper.text()).toContain('2026-09-07');
  });

  it('从方案 A 主界面进入来源路径管理', async () => {
    const wrapper = mountWorkspace();
    await flushPromises();
    await wrapper.get('[data-test="manage-source-paths"]').trigger('click');
    expect(navigationMocks.route).toHaveBeenCalledWith('/eln/source-paths', { title: '来源路径管理' });
  });

  it('重新打开数据项编辑时刷新可用来源路径', async () => {
    const wrapper = mountWorkspace();
    await flushPromises();
    apiMocks.getSourcePaths.mockResolvedValueOnce({
      data: [
        {
          code: 'sample',
          displayName: '所属样品',
          editVersion: 1,
          id: 'path-2',
          outputs: [{ dataType: 'TEXT', displayName: '批号', key: 'batch_no', path: '检验项 > 样品 > 批号', returnContent: '原值' }],
          revision: 1,
          scope: '全部检验项',
          status: 'PUBLISHED',
        },
      ],
    });

    await wrapper.get('[data-test="new-source-property"]').trigger('click');
    await flushPromises();

    expect(apiMocks.getSourcePaths).toHaveBeenCalledTimes(2);
    expect(wrapper.text()).toContain('检验项 > 样品 > 批号');
  });

  it('预览样例加载失败时仍显示数据项和来源路径', async () => {
    apiMocks.getSourcePreviewSamples.mockRejectedValue(new Error('preview unavailable'));
    const wrapper = mountWorkspace();
    await flushPromises();

    expect(wrapper.text()).toContain('生产日期');
    expect(wrapper.text()).toContain('检验项 > 请验单 > 生产日期');
    expect(wrapper.text()).not.toContain('LIMS 数据项加载失败');
    expect(apiMocks.getSourcePreviewSamples).not.toHaveBeenCalled();

    await wrapper.get('[data-test="new-source-property"]').trigger('click');
    await flushPromises();
    expect(apiMocks.getSourcePreviewSamples).toHaveBeenCalledTimes(1);
  });
});
