/* @vitest-environment happy-dom */

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

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

import VersionDrawer from '../VersionDrawer.vue';
import drawerSource from '../VersionDrawer.vue?raw';

config.global.stubs = { 'a-button': { template: '<button><slot /></button>' } };

function deferred<T>() {
  let resolve!: (value: T) => void;
  const promise = new Promise<T>((promiseResolve) => {
    resolve = promiseResolve;
  });
  return { promise, resolve };
}

const apiMocks = vi.hoisted(() => ({ getFieldDetail: vi.fn(), getProviderProperties: vi.fn(), updateFieldVersion: vi.fn() }));
const sourceApiMocks = vi.hoisted(() => ({ getSourceReference: vi.fn() }));
const navigationMocks = vi.hoisted(() => ({ route: vi.fn() }));
const drawerState = vi.hoisted(() => ({ init: undefined as any }));
const formState = vi.hoisted(() => ({ config: undefined as any, setFieldsValue: vi.fn(), updateSchema: vi.fn(), validate: vi.fn() }));
const tableState = vi.hoisted(() => ({ config: undefined as any }));

vi.mock('#/api/x/eln/fields', () => ({
  cancelFieldVersion: vi.fn(),
  createFieldVersion: vi.fn(),
  getFieldDetail: apiMocks.getFieldDetail,
  getProviderProperties: apiMocks.getProviderProperties,
  preReleaseFieldVersion: vi.fn(),
  publishFieldVersion: vi.fn(),
  switchCurrentFieldVersion: vi.fn(),
  updateFieldVersion: apiMocks.updateFieldVersion,
}));

vi.mock('#/api/x/eln/sources', () => ({ getSourceReference: sourceApiMocks.getSourceReference }));
vi.mock('#/utils/jnpf', () => ({ getJnpfAppEnCode: () => '', onlineUtils: navigationMocks }));

vi.mock('@jnpf/hooks', () => ({
  useMessage: () => ({ createMessage: { success: vi.fn(), warning: vi.fn() } }),
}));

vi.mock('@jnpf/ui/drawer', () => ({
  BasicDrawer: { name: 'BasicDrawer', template: '<div><slot /></div>' },
  useDrawerInner: (init: any) => {
    drawerState.init = init;
    return [vi.fn(), { changeLoading: vi.fn(), closeDrawer: vi.fn() }];
  },
}));

vi.mock('@jnpf/ui/form', () => ({
  BasicForm: { name: 'BasicForm', template: '<div />' },
  useForm: (config: any) => {
    formState.config = config;
    return [vi.fn(), { resetFields: vi.fn(), setFieldsValue: formState.setFieldsValue, updateSchema: formState.updateSchema, validate: formState.validate }];
  },
}));

vi.mock('@jnpf/ui/modal', () => ({
  BasicModal: { name: 'BasicModal', props: ['title'], template: '<div :data-title="title"><slot /></div>' },
  useModal: () => [vi.fn(), { closeModal: vi.fn(), openModal: vi.fn() }],
}));

vi.mock('@jnpf/ui/vxeTable', () => ({
  BasicVxeTable: {
    name: 'BasicVxeTable',
    props: ['dataSource'],
    template:
      '<div><slot v-for="record in dataSource" :record="record" name="shujuLeixing" /><slot v-for="record in dataSource" :record="record" name="action" /></div>',
  },
  TableAction: { name: 'TableAction', props: ['actions'], template: '<div>{{ actions.map((action) => action.label).join(" ") }}</div>' },
  useVxeTable: (config: any) => {
    tableState.config = config;
    return [vi.fn()];
  },
}));

describe('eLN field version drawer', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    apiMocks.getProviderProperties.mockResolvedValue({ data: [] });
    sourceApiMocks.getSourceReference.mockResolvedValue({ data: undefined });
    apiMocks.updateFieldVersion.mockResolvedValue({ data: {} });
    apiMocks.getFieldDetail.mockResolvedValue({
      data: {
        bizEnabled: 'enabled',
        dangqianZiduanBanbenId: 'v1',
        id: 'field-1',
        versions: [
          {
            banbenHao: 1,
            binding: { read: { source: 'LIMS_TASK' } },
            bizStatus: 'PUBLISHED',
            id: 'v1',
            shujuLeixing: 'TEXT',
            ziduanDingyiId: 'field-1',
          },
          {
            banbenHao: 2,
            binding: { read: { source: 'NONE' } },
            bizStatus: 'DRAFT',
            id: 'v2',
            shujuLeixing: 'DECIMAL',
            ziduanDingyiId: 'field-1',
          },
        ],
        ziduanBianma: 'sample_code_ab12cd34',
        zuoyongFanwei: 'GLOBAL',
      },
    });
  });

  it('loads and saves arbitrary checkbox selection group recommendations', async () => {
    apiMocks.getFieldDetail.mockResolvedValue({
      data: {
        bizEnabled: 'enabled',
        id: 'field-1',
        versions: [
          {
            banbenHao: 1,
            binding: { read: { source: 'NONE' } },
            bizStatus: 'DRAFT',
            id: 'v1',
            recommendedCheckboxGroupCode: 'review_result',
            recommendedCheckboxMaxSelected: null,
            recommendedCheckboxMinSelected: 2,
            shujuLeixing: 'CHECKBOX',
            ziduanDingyiId: 'field-1',
            ziduanMingcheng: '复核结论',
          },
        ],
        ziduanBianma: 'review_result_ab12cd34',
        zuoyongFanwei: 'GLOBAL',
      },
    });
    formState.validate.mockResolvedValue({
      checkboxGroupUnlimited: false,
      piciMoshi: 'RECORD_PRIVATE',
      readSource: 'NONE',
      recommendedCheckboxGroupCode: 'review_result',
      recommendedCheckboxMaxSelected: 8,
      recommendedCheckboxMinSelected: 3,
      shifouBitian: false,
      shujuLeixing: 'CHECKBOX',
      ziduanMingcheng: '复核结论',
    });
    const wrapper = mount(VersionDrawer, {
      global: { stubs: { 'a-alert': true, 'a-descriptions': true, 'a-descriptions-item': true, 'a-form-item': true, 'a-tag': true, 'a-textarea': true } },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();
    const actions = wrapper.findComponent({ name: 'TableAction' }).props('actions') as any[];
    await actions.find((action) => action.label === '编辑').onClick();
    await flushPromises();

    expect(formState.setFieldsValue).toHaveBeenCalledWith(
      expect.objectContaining({
        checkboxGroupUnlimited: true,
        recommendedCheckboxGroupCode: 'review_result',
        recommendedCheckboxMinSelected: 2,
      }),
    );
    wrapper.findAllComponents({ name: 'BasicModal' })[0]!.vm.$emit('ok');
    await flushPromises();
    expect(apiMocks.updateFieldVersion).toHaveBeenCalledWith(
      'v1',
      expect.objectContaining({
        recommendedCheckboxGroupCode: 'review_result',
        recommendedCheckboxMaxSelected: 8,
        recommendedCheckboxMinSelected: 3,
      }),
    );
  });

  it('shows field data types in Chinese', async () => {
    const wrapper = mount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();

    expect(wrapper.text()).toContain('单行文本');
    expect(wrapper.text()).toContain('数值');
    expect(wrapper.text()).not.toContain('TEXT');
    expect(wrapper.text()).not.toContain('DECIMAL');
  });

  it('loads independent V1/V2 history into the platform table', async () => {
    shallowMount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();

    expect(apiMocks.getFieldDetail).toHaveBeenCalledWith('field-1');
    expect(tableState.config.dataSource.value).toHaveLength(2);
    expect(tableState.config.pagination).toBe(false);
  });

  it('allows published versions to be viewed without exposing a save action', async () => {
    const wrapper = mount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();

    expect(wrapper.text()).toContain('查看');
    expect(drawerSource).toContain(':disabled="isViewingVersion"');
    expect(drawerSource).toContain(':show-ok-btn="!isViewingVersion"');
  });

  it('keeps every field read-only and shows the version number when viewing a published version', async () => {
    const wrapper = mount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();

    const publishedVersion = tableState.config.dataSource.value[0];
    const actions = wrapper.findComponent({ name: 'TableAction' }).props('actions') as any[];
    await actions.find((action) => action.label === '查看').onClick();
    await flushPromises();

    const providerProperty = formState.config.schemas.find((schema: any) => schema.field === 'providerProperty');
    expect(providerProperty.dynamicDisabled({ model: { readSource: 'LIMS_TASK' } })).toBe(true);
    expect(wrapper.findAll('[data-title]').map((element) => element.attributes('data-title'))).toContain(`查看字段版本 - V${publishedVersion.banbenHao}`);
  });

  it('does not validate a source property before an external source is selected', () => {
    shallowMount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    const providerProperty = formState.config.schemas.find((schema: any) => schema.field === 'providerProperty');

    expect(providerProperty.dynamicRules({ model: { readSource: undefined } })).toEqual([]);
    expect(providerProperty.dynamicRules({ model: { readSource: 'NONE' } })).toEqual([]);
    expect(providerProperty.dynamicRules({ model: { readSource: 'LIMS_TASK' } })).toHaveLength(1);
    expect(providerProperty.dynamicRules({ model: { readSource: 'SYSTEM' } })).toHaveLength(1);
  });

  it('uses shared status actions and keeps template fields away from external sources', () => {
    expect(drawerSource).toContain('versionActions({');
    expect(drawerSource).toContain("field.value?.zuoyongFanwei === 'GLOBAL'");
    expect(drawerSource).toContain('READ_SOURCE_OPTIONS');
    expect(drawerSource).toContain('NONE_READ_SOURCE_OPTIONS');
  });

  it('presents field ownership and remark in the detail summary', () => {
    expect(drawerSource).toContain('字段详情');
    expect(drawerSource).toContain('field.mubanBianhao');
    expect(drawerSource).toContain('field.beizhu');
  });

  it('keeps the summary and version table away from the drawer edges', () => {
    expect(drawerSource).toContain('padding: 16px 24px;');
    expect(drawerSource).toContain('padding: 12px 24px 0;');
  });

  it('shows both current and target version numbers in switch confirmation', () => {
    expect(drawerSource).toContain(`V\${currentVersion.value.banbenHao}`);
    expect(drawerSource).toContain(`V\${version.banbenHao}`);
    expect(drawerSource).toContain('switchCurrentFieldVersion');
  });

  it('keeps the draft form mounted between openings so it can be reset before showing', () => {
    expect(drawerSource).not.toMatch(/registerVersionModal[^>]*destroy-on-close/);
    expect(drawerSource).toMatch(/registerVersionModal[^>]*force-render/);
  });

  it('fills the drawer body so the version table can use the remaining height', () => {
    expect(drawerSource).toMatch(/<BasicDrawer[^>]*class="full-drawer"/);
  });

  it('keeps the newest provider options when an older request finishes last', async () => {
    const limsResponse = deferred<any>();
    apiMocks.getProviderProperties.mockImplementation((source: string) => {
      if (source === 'LIMS_TASK') return limsResponse.promise;
      return Promise.resolve({
        data: [{ dataType: 'TEXT', label: '当前用户', property: 'user.name', readOnly: true, source: 'SYSTEM' }],
      });
    });
    shallowMount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    const readSource = formState.config.schemas.find((schema: any) => schema.field === 'readSource');

    const limsChange = readSource.componentProps.onChange('LIMS_TASK');
    await Promise.resolve();
    await Promise.resolve();
    const systemChange = readSource.componentProps.onChange('SYSTEM');
    await systemChange;
    limsResponse.resolve({
      data: [{ dataType: 'TEXT', label: '检验依据', property: 'test.basis', readOnly: true, source: 'LIMS_TASK' }],
    });
    await limsChange;

    const providerUpdates = formState.updateSchema.mock.calls.map(([schema]) => schema).filter((schema) => schema.field === 'providerProperty');
    expect(providerUpdates.at(-1).componentProps.options).toEqual([{ fullName: '当前用户', id: 'user.name' }]);
  });

  it('keeps safe empty provider options when loading fails', async () => {
    apiMocks.getProviderProperties.mockRejectedValue(new Error('network error'));
    shallowMount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    const readSource = formState.config.schemas.find((schema: any) => schema.field === 'readSource');

    await expect(readSource.componentProps.onChange('LIMS_TASK')).resolves.toBeUndefined();

    const providerUpdates = formState.updateSchema.mock.calls.map(([schema]) => schema).filter((schema) => schema.field === 'providerProperty');
    expect(providerUpdates.at(-1).componentProps.options).toEqual([]);
  });

  it('shows and saves the fixed revision of a configured LIMS property', async () => {
    apiMocks.getProviderProperties.mockResolvedValue({
      data: [
        {
          dataType: 'TEXT',
          enabled: true,
          label: '供试品名称',
          property: 'verification.sample_name',
          propertyRevision: 3,
          readOnly: true,
          source: 'LIMS_TASK',
        },
      ],
    });
    formState.validate.mockResolvedValue({
      piciMoshi: 'RECORD_PRIVATE',
      providerProperty: 'verification.sample_name',
      readSource: 'LIMS_TASK',
      shifouBitian: false,
      shujuLeixing: 'TEXT',
      ziduanMingcheng: '供试品名称',
    });
    const wrapper = mount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();
    const actions = wrapper.findAllComponents({ name: 'TableAction' })[1]!.props('actions') as any[];
    await actions.find((action) => action.label === '编辑').onClick();
    const readSource = formState.config.schemas.find((schema: any) => schema.field === 'readSource');
    await readSource.componentProps.onChange('LIMS_TASK');
    await flushPromises();

    const providerUpdates = formState.updateSchema.mock.calls.map(([schema]) => schema).filter((schema) => schema.field === 'providerProperty');
    expect(providerUpdates.at(-1).componentProps.options).toEqual([{ fullName: '供试品名称（版本 3）', id: 'verification.sample_name' }]);

    wrapper.findAllComponents({ name: 'BasicModal' })[0]!.vm.$emit('ok');
    await flushPromises();
    expect(apiMocks.updateFieldVersion).toHaveBeenCalledWith(
      'v2',
      expect.objectContaining({
        binding: {
          read: { property: 'verification.sample_name', propertyRevision: 3, source: 'LIMS_TASK' },
          write: { target: 'ELN_RECORD' },
        },
      }),
    );
  });

  it('opens the independent LIMS source page from draft field editing', async () => {
    const wrapper = mount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();
    const actions = wrapper.findAllComponents({ name: 'TableAction' })[1]!.props('actions') as any[];
    await actions.find((action) => action.label === '编辑').onClick();
    const readSource = formState.config.schemas.find((schema: any) => schema.field === 'readSource');

    expect(wrapper.find('[data-test="maintain-lims-source"]').exists()).toBe(false);
    await readSource.componentProps.onChange('LIMS_TASK');
    await flushPromises();
    expect(wrapper.get('[data-test="maintain-lims-source"]').text()).toContain('维护 LIMS 数据项');
    await wrapper.get('[data-test="maintain-lims-source"]').trigger('click');
    expect(navigationMocks.route).toHaveBeenCalledWith('/eln/sources', { title: 'LIMS 数据项' });
    expect(wrapper.find('[data-test="lims-source-workspace"]').exists()).toBe(false);
    expect(wrapper.findComponent({ name: 'SourceWorkspace' }).exists()).toBe(false);

    await readSource.componentProps.onChange('SYSTEM');
    await flushPromises();
    expect(wrapper.find('[data-test="maintain-lims-source"]').exists()).toBe(false);
  });

  it('preserves a pinned property revision when the current catalog has moved on', async () => {
    apiMocks.getFieldDetail.mockResolvedValue({
      data: {
        bizEnabled: 'enabled',
        id: 'field-1',
        versions: [
          {
            banbenHao: 2,
            binding: { read: { property: 'verification.sample_name', propertyRevision: 3, source: 'LIMS_TASK' } },
            bizStatus: 'DRAFT',
            id: 'v2',
            piciMoshi: 'RECORD_PRIVATE',
            shifouBitian: 0,
            shujuLeixing: 'TEXT',
            ziduanDingyiId: 'field-1',
            ziduanMingcheng: '供试品名称',
          },
        ],
        ziduanBianma: 'sample_name_ab12cd34',
        zuoyongFanwei: 'GLOBAL',
      },
    });
    apiMocks.getProviderProperties.mockResolvedValue({
      data: [
        {
          dataType: 'TEXT',
          enabled: true,
          label: '供试品名称（新规则）',
          property: 'verification.sample_name',
          propertyRevision: 4,
          readOnly: true,
          source: 'LIMS_TASK',
        },
      ],
    });
    sourceApiMocks.getSourceReference.mockResolvedValue({
      data: { dataType: 'TEXT', displayName: '供试品名称', enabled: false, propertyCode: 'verification.sample_name', revision: 3 },
    });
    formState.validate.mockResolvedValue({
      piciMoshi: 'RECORD_PRIVATE',
      providerProperty: 'verification.sample_name',
      readSource: 'LIMS_TASK',
      shifouBitian: false,
      shujuLeixing: 'TEXT',
      ziduanMingcheng: '供试品名称',
    });
    const wrapper = mount(VersionDrawer, {
      global: {
        stubs: {
          'a-alert': true,
          'a-button': true,
          'a-descriptions': true,
          'a-descriptions-item': true,
          'a-form-item': true,
          'a-tag': true,
          'a-textarea': true,
        },
      },
    });
    await drawerState.init({ fieldId: 'field-1' });
    await flushPromises();
    const actions = wrapper.findComponent({ name: 'TableAction' }).props('actions') as any[];
    await actions.find((action) => action.label === '编辑').onClick();
    await flushPromises();

    expect(sourceApiMocks.getSourceReference).toHaveBeenCalledWith('verification.sample_name', 3);
    const providerUpdates = formState.updateSchema.mock.calls.map(([schema]) => schema).filter((schema) => schema.field === 'providerProperty');
    expect(providerUpdates.at(-1).componentProps.options).toEqual([{ fullName: '供试品名称（版本 3，已停用）', id: 'verification.sample_name' }]);
    wrapper.findAllComponents({ name: 'BasicModal' })[0]!.vm.$emit('ok');
    await flushPromises();
    expect(apiMocks.updateFieldVersion).toHaveBeenCalledWith(
      'v2',
      expect.objectContaining({
        binding: expect.objectContaining({
          read: { property: 'verification.sample_name', propertyRevision: 3, source: 'LIMS_TASK' },
        }),
      }),
    );
  });
});
