/* @vitest-environment happy-dom */

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

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

import FieldForm from '../FieldForm.vue';

config.global.stubs = {
  AButton: { template: '<button><slot /></button>' },
  AInput: { template: '<div><slot /><slot name="suffix" /></div>' },
  ATooltip: { template: '<div><slot /></div>' },
};

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

const apiMocks = vi.hoisted(() => ({ createField: vi.fn(), getProviderProperties: vi.fn(), suggestFieldCode: vi.fn() }));
const navigationMocks = vi.hoisted(() => ({ route: vi.fn() }));
const formState = vi.hoisted(() => ({
  config: undefined as any,
  resetFields: vi.fn(),
  setFieldsValue: vi.fn(),
  updateSchema: vi.fn(),
  validate: vi.fn(),
}));
const modalState = vi.hoisted(() => ({ closeModal: vi.fn(), init: undefined as any, openCodeAssistModal: vi.fn() }));

vi.mock('#/api/x/eln/fields', () => ({
  createField: apiMocks.createField,
  getProviderProperties: apiMocks.getProviderProperties,
  suggestFieldCode: apiMocks.suggestFieldCode,
}));

vi.mock('#/utils/jnpf', () => ({ getJnpfAppEnCode: () => '', onlineUtils: navigationMocks }));

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

vi.mock('@jnpf/ui/form', () => ({
  BasicForm: {
    name: 'BasicForm',
    template: `<div><slot name="recommendedCheckboxGroupCode"
      :model="{ recommendedCheckboxGroupCode: 'original_group' }"
      field="recommendedCheckboxGroupCode" /></div>`,
  },
  useForm: (config: any) => {
    formState.config = config;
    return [vi.fn(), formState];
  },
}));

vi.mock('../CodeAssistModal.vue', () => ({
  default: {
    emits: ['confirm', 'register'],
    name: 'CodeAssistModal',
    template: '<div data-test="mock-code-assist-modal" />',
  },
}));

vi.mock('@jnpf/ui/modal', () => ({
  BasicModal: { name: 'BasicModal', template: '<div><slot /></div>' },
  useModal: () => [vi.fn(), { openModal: modalState.openCodeAssistModal }],
  useModalInner: (init: any) => {
    modalState.init = init;
    return [vi.fn(), { changeOkLoading: vi.fn(), closeModal: modalState.closeModal }];
  },
}));

describe('eln global field form', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    apiMocks.createField.mockResolvedValue({ data: {}, msg: '创建成功' });
    apiMocks.getProviderProperties.mockResolvedValue({
      data: [{ dataType: 'TEXT', label: '检验依据', property: 'test.basis', readOnly: true, source: 'LIMS_TASK' }],
    });
    apiMocks.suggestFieldCode.mockResolvedValue({ data: { code: 'fuhe_jieguo' } });
    formState.validate.mockResolvedValue({
      danwei: '',
      controlDateFormat: '',
      controlDefaultChecked: false,
      controlDefaultDate: '',
      controlDefaultOption: undefined,
      controlDefaultValue: '标准品',
      controlOptionsText: '',
      controlPlaceholder: '请输入供试品名称',
      piciMoshi: 'RECORD_PRIVATE',
      providerProperty: undefined,
      readSource: 'NONE',
      shifouBitian: true,
      shujuLeixing: 'TEXT',
      ziduanMingcheng: '样品编号',
    });
  });

  it('does not expose editable identity code or scope fields', () => {
    shallowMount(FieldForm);
    const fields = formState.config.schemas.map((schema: any) => schema.field);

    expect(fields).not.toContain('ziduanBianma');
    expect(fields).not.toContain('zuoyongFanwei');
  });

  it('reserves enough label width for selection group recommendation labels', () => {
    shallowMount(FieldForm);

    expect(formState.config.labelWidth).toBe(130);
  });

  it('uses business fields instead of a raw JSON control configuration editor', () => {
    shallowMount(FieldForm);
    const fields = formState.config.schemas.map((schema: any) => schema.field);

    expect(fields).not.toContain('kongjianPeizhiText');
    expect(fields).toContain('controlPlaceholder');
    expect(fields).toContain('controlDefaultValue');
    expect(fields).toContain('controlOptionsText');
  });

  it('shows and submits a unit only for decimal fields', async () => {
    formState.validate.mockResolvedValue({
      danwei: 'mg',
      piciMoshi: 'RECORD_PRIVATE',
      readSource: 'NONE',
      shifouBitian: false,
      shujuLeixing: 'TEXT',
      ziduanMingcheng: '样品名称',
    });
    const wrapper = shallowMount(FieldForm);
    const unitSchema = formState.config.schemas.find((schema: any) => schema.field === 'danwei');

    expect(unitSchema.ifShow({ model: { shujuLeixing: 'DECIMAL' } })).toBe(true);
    expect(unitSchema.ifShow({ model: { shujuLeixing: 'TEXT' } })).toBe(false);

    modalState.init({});
    wrapper.findComponent({ name: 'BasicModal' }).vm.$emit('ok');
    await flushPromises();

    expect(apiMocks.createField).toHaveBeenCalledTimes(1);
    expect(apiMocks.createField.mock.calls[0]?.[0]?.danwei).toBeUndefined();
  });

  it('shows only the control settings owned by the selected data type', () => {
    shallowMount(FieldForm);
    const typeOwnedFields = [
      'danwei',
      'controlPlaceholder',
      'controlDefaultValue',
      'controlOptionsText',
      'controlDefaultOption',
      'controlDateFormat',
      'controlDefaultDate',
      'controlDefaultChecked',
      'recommendedCheckboxGroupCode',
      'recommendedCheckboxMinSelected',
      'checkboxGroupUnlimited',
      'recommendedCheckboxMaxSelected',
    ];
    const expectedVisibleFields: Record<string, string[]> = {
      CHECKBOX: [
        'controlDefaultChecked',
        'recommendedCheckboxGroupCode',
        'recommendedCheckboxMinSelected',
        'checkboxGroupUnlimited',
        'recommendedCheckboxMaxSelected',
      ],
      DATE: ['controlDateFormat', 'controlDefaultDate'],
      DATETIME: ['controlDateFormat', 'controlDefaultDate'],
      DECIMAL: ['danwei', 'controlPlaceholder', 'controlDefaultValue'],
      SELECT: ['controlPlaceholder', 'controlOptionsText', 'controlDefaultOption'],
      TEXT: ['controlPlaceholder', 'controlDefaultValue'],
      TEXTAREA: ['controlPlaceholder', 'controlDefaultValue'],
    };

    for (const [dataType, expectedFields] of Object.entries(expectedVisibleFields)) {
      const model = {
        checkboxGroupUnlimited: false,
        readSource: 'NONE',
        recommendedCheckboxGroupCode: 'review_result',
        shujuLeixing: dataType,
      };
      const visibleFields = typeOwnedFields.filter((field) => {
        const schema = formState.config.schemas.find((item: any) => item.field === field);
        return schema.ifShow?.({ model }) !== false;
      });

      expect(visibleFields, dataType).toEqual(expectedFields);
    }
  });

  it('hides provider property without an external source and validates it only on submit', async () => {
    formState.validate.mockResolvedValue(false);
    const wrapper = shallowMount(FieldForm);
    const providerProperty = formState.config.schemas.find((schema: any) => schema.field === 'providerProperty');

    expect(providerProperty.ifShow({ model: { readSource: 'NONE' } })).toBe(false);
    expect(providerProperty.ifShow({ model: { readSource: 'LIMS_TASK' } })).toBe(true);
    expect(providerProperty.dynamicRules({ model: { readSource: 'LIMS_TASK' } })).toEqual([]);

    modalState.init({});
    wrapper.findComponent({ name: 'BasicModal' }).vm.$emit('ok');
    await flushPromises();

    expect(providerProperty.dynamicRules({ model: { readSource: 'LIMS_TASK' } })).toEqual([
      expect.objectContaining({ message: '请选择来源属性', required: true }),
    ]);
  });

  it('uses type-specific date pickers and Chinese-friendly display format options', () => {
    shallowMount(FieldForm);
    const dateFormat = formState.config.schemas.find((schema: any) => schema.field === 'controlDateFormat');
    const defaultDate = formState.config.schemas.find((schema: any) => schema.field === 'controlDefaultDate');

    expect(dateFormat.component).toBe('Select');
    expect(dateFormat.componentProps({ formModel: { shujuLeixing: 'DATE' } }).options).toEqual([
      expect.objectContaining({ id: 'yyyy-MM-dd' }),
      expect.objectContaining({ id: 'yyyy/MM/dd' }),
      expect.objectContaining({ id: 'yyyy年MM月dd日' }),
    ]);
    expect(dateFormat.componentProps({ formModel: { shujuLeixing: 'DATETIME' } }).options).toEqual([
      expect.objectContaining({ id: 'yyyy-MM-dd HH:mm' }),
      expect.objectContaining({ id: 'yyyy-MM-dd HH:mm:ss' }),
      expect.objectContaining({ id: 'yyyy/MM/dd HH:mm' }),
      expect.objectContaining({ id: 'yyyy/MM/dd HH:mm:ss' }),
      expect.objectContaining({ id: 'yyyy年MM月dd日 HH:mm:ss' }),
    ]);

    expect(defaultDate.component).toBe('DatePicker');
    expect(defaultDate.componentProps({ formModel: { controlDateFormat: 'yyyy-MM-dd', shujuLeixing: 'DATE' } })).toEqual(
      expect.objectContaining({ format: 'YYYY-MM-DD', showTime: false, valueFormat: 'YYYY-MM-DD' }),
    );
    expect(
      defaultDate.componentProps({
        formModel: { controlDateFormat: 'yyyy-MM-dd HH:mm:ss', shujuLeixing: 'DATETIME' },
      }),
    ).toEqual(
      expect.objectContaining({
        format: 'YYYY-MM-DD HH:mm:ss',
        showTime: { format: 'HH:mm:ss' },
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
      }),
    );
  });

  it('keeps checkbox selection limits visible and enables them after a group code is entered', () => {
    shallowMount(FieldForm);
    const schemas = formState.config.schemas;
    const minSelected = schemas.find((schema: any) => schema.field === 'recommendedCheckboxMinSelected');
    const unlimited = schemas.find((schema: any) => schema.field === 'checkboxGroupUnlimited');
    const maxSelected = schemas.find((schema: any) => schema.field === 'recommendedCheckboxMaxSelected');
    const withoutCode = { checkboxGroupUnlimited: false, readSource: 'NONE', recommendedCheckboxGroupCode: '', shujuLeixing: 'CHECKBOX' };
    const withCode = { ...withoutCode, recommendedCheckboxGroupCode: 'review_result' };

    expect(minSelected.ifShow({ model: withoutCode })).toBe(true);
    expect(unlimited.ifShow({ model: withoutCode })).toBe(true);
    expect(maxSelected.ifShow({ model: withoutCode })).toBe(true);
    expect(minSelected.dynamicDisabled({ model: withoutCode })).toBe(true);
    expect(maxSelected.dynamicDisabled({ model: withoutCode })).toBe(true);
    expect(minSelected.dynamicDisabled({ model: withCode })).toBe(false);
    expect(maxSelected.dynamicDisabled({ model: withCode })).toBe(false);
  });

  it('creates a global field with a fixed ELN record write target', async () => {
    const wrapper = shallowMount(FieldForm);
    modalState.init({});
    wrapper.findComponent({ name: 'BasicModal' }).vm.$emit('ok');
    await flushPromises();

    expect(apiMocks.createField).toHaveBeenCalledWith(
      expect.objectContaining({
        binding: { read: { source: 'NONE' }, write: { target: 'ELN_RECORD' } },
        kongjianPeizhi: { defaultValue: '标准品', placeholder: '请输入供试品名称' },
        shifouBitian: 1,
        zuoyongFanwei: 'GLOBAL',
      }),
    );
    expect(modalState.closeModal).toHaveBeenCalled();
  });

  it('configures an arbitrary selection group recommendation only for editable checkboxes', async () => {
    formState.validate.mockResolvedValue({
      binding: undefined,
      checkboxGroupUnlimited: false,
      controlDefaultChecked: false,
      piciMoshi: 'RECORD_PRIVATE',
      readSource: 'NONE',
      recommendedCheckboxGroupCode: 'review_result',
      recommendedCheckboxMaxSelected: 5,
      recommendedCheckboxMinSelected: 2,
      shifouBitian: false,
      shujuLeixing: 'CHECKBOX',
      ziduanMingcheng: '复核结论',
    });
    const wrapper = shallowMount(FieldForm);
    const schemas = formState.config.schemas;

    expect(
      schemas
        .find((schema: any) => schema.field === 'recommendedCheckboxGroupCode')
        .ifShow({
          model: { readSource: 'NONE', shujuLeixing: 'CHECKBOX' },
        }),
    ).toBe(true);
    expect(
      schemas
        .find((schema: any) => schema.field === 'recommendedCheckboxGroupCode')
        .ifShow({
          model: { readSource: 'NONE', shujuLeixing: 'TEXT' },
        }),
    ).toBe(false);

    modalState.init({});
    wrapper.findComponent({ name: 'BasicModal' }).vm.$emit('ok');
    await flushPromises();

    expect(apiMocks.createField).toHaveBeenCalledWith(
      expect.objectContaining({
        recommendedCheckboxGroupCode: 'review_result',
        recommendedCheckboxMaxSelected: 5,
        recommendedCheckboxMinSelected: 2,
      }),
    );
  });

  it('offers code assistance for checkbox group recommendations and fills the confirmed code', async () => {
    const wrapper = mount(FieldForm);
    const groupCodeSchema = formState.config.schemas.find((schema: any) => schema.field === 'recommendedCheckboxGroupCode');

    expect(groupCodeSchema.slot).toBe('recommendedCheckboxGroupCode');
    expect(wrapper.get('[data-test="recommended-checkbox-code-assist"]').attributes('aria-label')).toBe('中文转拼音');

    await wrapper.get('[data-test="recommended-checkbox-code-assist"]').trigger('click');
    expect(modalState.openCodeAssistModal).toHaveBeenCalledWith(true);

    wrapper.getComponent({ name: 'CodeAssistModal' }).vm.$emit('confirm', 'fuhe_jieguo');

    expect(formState.setFieldsValue).toHaveBeenCalledWith({ recommendedCheckboxGroupCode: 'fuhe_jieguo' });
  });

  it('loads provider properties and forces record-private mode for external sources', async () => {
    shallowMount(FieldForm);
    const readSource = formState.config.schemas.find((schema: any) => schema.field === 'readSource');
    await readSource.componentProps.onChange('LIMS_TASK');

    expect(apiMocks.getProviderProperties).toHaveBeenCalledWith('LIMS_TASK');
    expect(formState.setFieldsValue).toHaveBeenCalledWith({ piciMoshi: 'RECORD_PRIVATE', providerProperty: undefined });
    expect(formState.updateSchema).toHaveBeenCalledWith(
      expect.objectContaining({ field: 'providerProperty', componentProps: expect.objectContaining({ options: expect.any(Array) }) }),
    );
  });

  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(FieldForm);
    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(FieldForm);
    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('creates a LIMS-bound field with the selected fixed property revision', 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 = shallowMount(FieldForm);
    modalState.init({});
    const readSource = formState.config.schemas.find((schema: any) => schema.field === 'readSource');
    await readSource.componentProps.onChange('LIMS_TASK');
    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.findComponent({ name: 'BasicModal' }).vm.$emit('ok');
    await flushPromises();

    expect(apiMocks.createField).toHaveBeenCalledWith(
      expect.objectContaining({
        binding: {
          read: { property: 'verification.sample_name', propertyRevision: 3, source: 'LIMS_TASK' },
          write: { target: 'ELN_RECORD' },
        },
      }),
    );
  });

  it('opens the independent LIMS source page instead of embedding the maintenance workspace', async () => {
    const wrapper = mount(FieldForm);
    modalState.init({});
    const readSource = formState.config.schemas.find((schema: any) => schema.field === 'readSource');
    await readSource.componentProps.onChange('LIMS_TASK');
    await flushPromises();

    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);
  });
});
