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

import { HookType } from '@tmagic/core';

import CompareForm from '@editor/components/CompareForm.vue';

const propsService = {
  getPropsConfig: vi.fn(async () => [
    {
      type: 'tab',
      items: [{ title: '样式', items: [{ type: 'text', name: 'color', display: false }] }],
    },
    { type: 'text', name: 'name' },
  ]),
};
const dataSourceService = {
  getFormConfig: vi.fn(() => [{ type: 'text', name: 'title' }]),
};
const codeBlockService = {
  getParamsColConfig: vi.fn(() => null),
};
const editorService = {
  get: vi.fn(() => ({ select: vi.fn() })),
};

const services = {
  propsService,
  dataSourceService,
  codeBlockService,
  editorService,
} as any;

let capturedShowDiff: ((args: any) => boolean) | undefined;
let capturedFormProps: Record<string, any> = {};

vi.mock('@editor/utils/code-block', () => ({
  getCodeBlockFormConfig: vi.fn(() => [{ type: 'text', name: 'content' }]),
}));

vi.mock('@tmagic/form', () => ({
  MForm: defineComponent({
    name: 'MForm',
    props: ['config', 'initValues', 'lastValues', 'isCompare', 'disabled', 'labelWidth', 'extendState', 'showDiff'],
    setup(props, { expose }) {
      capturedShowDiff = props.showDiff as (args: any) => boolean;
      capturedFormProps = props as Record<string, any>;
      expose({ formState: {} });
      return () => h('div', { class: 'fake-mform' });
    },
  }),
}));

beforeEach(() => {
  vi.clearAllMocks();
  capturedShowDiff = undefined;
  capturedFormProps = {};
});

describe('CompareForm.vue', () => {
  test('node 类别按 type 加载 props 配置并展示 MForm', async () => {
    const wrapper = mount(CompareForm, {
      props: {
        category: 'node',
        type: 'text',
        value: { id: 'n1', name: 'new' },
        lastValue: { id: 'n1', name: 'old' },
        services,
      },
      global: {
        provide: {
          codeOptions: { theme: 'vs-dark' },
        },
      },
    });
    await nextTick();
    await nextTick();

    expect(propsService.getPropsConfig).toHaveBeenCalledWith('text', { node: { id: 'n1', name: 'new' } });
    expect(wrapper.find('.fake-mform').exists()).toBe(true);
    expect(capturedFormProps.initValues).toEqual({ id: 'n1', name: 'new' });
    expect(capturedFormProps.lastValues).toEqual({ id: 'n1', name: 'old' });
  });

  test('node 类别缺少 type 时不渲染 MForm', async () => {
    const wrapper = mount(CompareForm, {
      props: {
        category: 'node',
        value: { id: 'n1' },
        services,
      },
    });
    await nextTick();
    expect(wrapper.find('.fake-mform').exists()).toBe(false);
  });

  test('data-source 类别加载数据源表单配置', async () => {
    mount(CompareForm, {
      props: {
        category: 'data-source',
        type: 'http',
        value: { id: 'ds_1', title: 'A' },
        lastValue: { id: 'ds_1', title: 'B' },
        services,
      },
    });
    await nextTick();
    await nextTick();
    expect(dataSourceService.getFormConfig).toHaveBeenCalledWith('http');
  });

  test('code-block 类别会把 content 非字符串值 normalize 成字符串', async () => {
    mount(CompareForm, {
      props: {
        category: 'code-block',
        value: { id: 'cb_1', content: { toString: () => 'fn-body' } },
        lastValue: { id: 'cb_1', content: '' },
        services,
      },
    });
    await nextTick();
    await nextTick();
    expect(capturedFormProps.initValues.content).toBe('fn-body');
    expect(capturedFormProps.lastValues.content).toBe('');
  });

  test('传入 height 时外层容器启用内部滚动样式', () => {
    const wrapper = mount(CompareForm, {
      props: {
        category: 'node',
        type: 'text',
        value: { id: 'n1' },
        height: '400px',
        services,
      },
    });
    const style = wrapper.find('.m-editor-compare-form-wrapper').attributes('style') || '';
    expect(style).toContain('height: 400px');
    expect(style).toContain('overflow: auto');
  });

  test('自定义 loadConfig 可接管配置加载', async () => {
    const loadConfig = vi.fn(async ({ defaultLoadConfig }) => {
      await defaultLoadConfig();
      return [{ type: 'text', name: 'custom' }];
    });
    const wrapper = mount(CompareForm, {
      props: {
        category: 'node',
        type: 'text',
        value: { id: 'n1' },
        loadConfig,
        services,
      },
    });
    await nextTick();
    await nextTick();
    await nextTick();
    expect(loadConfig).toHaveBeenCalled();
    expect((wrapper.vm as any).config).toEqual([{ type: 'text', name: 'custom' }]);
  });

  test('showDiff 对 code-select 的空形态视为相等', async () => {
    mount(CompareForm, {
      props: {
        category: 'node',
        type: 'text',
        value: { id: 'n1' },
        services,
      },
    });
    await nextTick();
    await nextTick();
    expect(capturedShowDiff).toBeTypeOf('function');
    expect(
      capturedShowDiff!({
        curValue: '',
        lastValue: { hookType: HookType.CODE, hookData: [] },
        config: { type: 'code-select' },
      }),
    ).toBe(false);
    expect(
      capturedShowDiff!({
        curValue: { hookType: HookType.CODE, hookData: [] },
        lastValue: '',
        config: { type: 'code-select' },
      }),
    ).toBe(false);
    expect(
      capturedShowDiff!({
        curValue: 'a',
        lastValue: 'b',
        config: { type: 'code-select' },
      }),
    ).toBe(true);
  });

  test('reload 暴露方法会重新加载配置', async () => {
    const wrapper = mount(CompareForm, {
      props: {
        category: 'data-source',
        type: 'base',
        value: { id: 'ds_1' },
        services,
      },
    });
    await nextTick();
    await nextTick();
    dataSourceService.getFormConfig.mockClear();
    await (wrapper.vm as any).reload();
    expect(dataSourceService.getFormConfig).toHaveBeenCalled();
  });

  test('watchEffect 会把 stage / services 写入 MForm.formState', async () => {
    const stage = ref({ select: vi.fn() });
    editorService.get.mockReturnValue(stage.value);
    mount(CompareForm, {
      props: {
        category: 'node',
        type: 'text',
        value: { id: 'n1' },
        services,
      },
    });
    await nextTick();
    await nextTick();
    stage.value = { select: vi.fn() };
    await nextTick();
    expect(editorService.get).toHaveBeenCalledWith('stage');
  });
});
