/*
 * Tencent is pleased to support the open source community by making TMagicEditor available.
 *
 * Copyright (C) 2025 Tencent.
 */
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { defineComponent, h, nextTick } from 'vue';
import { mount } from '@vue/test-utils';

import CodeParams from '@editor/components/CodeParams.vue';
import * as utilsMod from '@editor/utils';

const submitMock = vi.fn();
let lastConfig: any;
let lastProps: any;

vi.mock('@tmagic/form', () => ({
  MForm: defineComponent({
    name: 'MFormStub',
    props: ['config', 'initValues', 'disabled', 'size', 'watchProps', 'lastValues', 'isCompare'],
    emits: ['change'],
    setup(props, { expose, emit }) {
      lastConfig = props.config;
      lastProps = props;
      expose({ submitForm: submitMock });
      return () =>
        h('div', {
          class: 'form-stub',
          onClick: () => emit('change', { ok: true }, { changeRecords: [] }),
        });
    },
  }),
}));

vi.mock('@editor/utils', () => ({
  error: vi.fn(),
}));

describe('CodeParams.vue', () => {
  beforeEach(() => {
    submitMock.mockReset();
    lastConfig = null;
    lastProps = null;
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  test('config 中包含 vs-code 类型时直接保留', () => {
    mount(CodeParams as any, {
      props: {
        model: { p: {} },
        name: 'p',
        paramsConfig: [{ name: 'a', text: 'A', type: 'vs-code' }] as any,
      },
    });
    expect(lastConfig[0].items[0].type).toBe('vs-code');
  });

  test('config 中其它类型会包装成 data-source-field-select', () => {
    mount(CodeParams as any, {
      props: {
        model: { p: {} },
        name: 'p',
        paramsConfig: [{ name: 'a', text: 'A', type: 'text' }] as any,
      },
    });
    expect(lastConfig[0].items[0].type).toBe('data-source-field-select');
    expect(lastConfig[0].items[0].fieldConfig.type).toBe('text');
  });

  test('config.type 为函数时执行函数判断类型', () => {
    const typeFn = vi.fn(() => 'vs-code');
    mount(CodeParams as any, {
      props: {
        model: { p: { x: 1 } },
        name: 'p',
        paramsConfig: [{ name: 'a', text: 'A', type: typeFn }] as any,
      },
    });
    expect(typeFn).toHaveBeenCalledWith(undefined, { model: { x: 1 } });
    expect(lastConfig[0].items[0].name).toBe('a');
  });

  test('change 事件成功时 emit change 携带值', async () => {
    submitMock.mockResolvedValueOnce({ p: { a: 1 } });
    const wrapper = mount(CodeParams as any, {
      props: {
        model: { p: {} },
        name: 'p',
        paramsConfig: [{ name: 'a', text: 'A', type: 'vs-code' }] as any,
      },
    });
    await wrapper.find('.form-stub').trigger('click');
    await nextTick();
    await new Promise((r) => setTimeout(r, 0));
    const events = wrapper.emitted('change') as any[];
    expect(events?.[0]?.[0]).toEqual({ p: { a: 1 } });
  });

  test('对比模式 isCompare/lastValues 透传给内部 MForm', () => {
    mount(CodeParams as any, {
      props: {
        model: { p: { a: 'new' } },
        name: 'p',
        isCompare: true,
        lastValues: { p: { a: 'old' } },
        paramsConfig: [{ name: 'a', text: 'A', type: 'vs-code' }] as any,
      },
    });
    expect(lastProps.isCompare).toBe(true);
    expect(lastProps.lastValues).toEqual({ p: { a: 'old' } });
  });

  test('submitForm 抛错时调用 error 不抛出', async () => {
    submitMock.mockRejectedValueOnce(new Error('bad'));
    const wrapper = mount(CodeParams as any, {
      props: {
        model: { p: {} },
        name: 'p',
        paramsConfig: [{ name: 'a', text: 'A', type: 'vs-code' }] as any,
      },
    });
    await wrapper.find('.form-stub').trigger('click');
    await nextTick();
    await new Promise((r) => setTimeout(r, 0));
    expect((utilsMod as any).error).toHaveBeenCalled();
  });
});
