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

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

import SkillToolBindingDialog from './SkillToolBindingDialog.vue';

const apiMocks = vi.hoisted(() => ({
  getSkillMcpTools: vi.fn(),
  getSkillToolOptions: vi.fn(),
  updateSkillToolBindings: vi.fn(),
}));

vi.mock('./api', () => apiMocks);
vi.mock('@easyflow/common-ui', () => ({
  EasyFlowPanelModal: defineComponent({
    name: 'EasyFlowPanelModal',
    props: {
      open: Boolean,
      title: { default: '', type: String },
    },
    emits: ['update:open'],
    setup(props, { slots }) {
      return () =>
        props.open
          ? h('section', { 'data-testid': 'modal' }, [
              h('h2', props.title),
              slots.default?.(),
            ])
          : null;
    },
  }),
}));

function optionResponse(toolType = 'WORKFLOW') {
  return {
    data: {
      pageNum: 1,
      pageSize: 100,
      records: [
        {
          available: true,
          description: '用于执行验收流程',
          knownToolCount: 1,
          targetId: toolType === 'MCP' ? 20 : 10,
          title: toolType === 'MCP' ? '企业 MCP' : '合同审批流',
          toolType,
        },
      ],
      total: 1,
    },
    errorCode: 0,
  };
}

function mountDialog(props: Record<string, unknown>) {
  return mount(SkillToolBindingDialog, {
    attachTo: document.body,
    global: { directives: { loading: {} } },
    props: props as never,
  });
}

describe('skill tool binding dialog', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    apiMocks.getSkillToolOptions.mockResolvedValue(optionResponse());
    apiMocks.getSkillMcpTools.mockResolvedValue({
      data: {
        manifestHash: 'manifest-v2',
        toolCount: 2,
        tools: [
          { description: '查询订单', name: 'query_order' },
          { description: '创建订单', name: 'create_order' },
        ],
      },
      errorCode: 0,
    });
    apiMocks.updateSkillToolBindings.mockResolvedValue({
      data: [],
      errorCode: 0,
    });
  });

  it('selects a workflow and sends only binding fields', async () => {
    const wrapper = mountDialog({ modelValue: true, skillId: 101 });
    await flushPromises();

    await wrapper.get('.skill-tools__candidate-content').trigger('click');
    await wrapper
      .findAll('button')
      .find((button) => button.text().trim() === '保存绑定')
      ?.trigger('click');
    await flushPromises();

    expect(apiMocks.updateSkillToolBindings).toHaveBeenCalledWith(101, [
      expect.objectContaining({
        hitlEnabled: false,
        targetId: 10,
        toolType: 'WORKFLOW',
      }),
    ]);
    const submitted = apiMocks.updateSkillToolBindings.mock.calls[0]?.[1]?.[0];
    expect(submitted).not.toHaveProperty('resourceSnapshot');
    expect(wrapper.emitted('saved')).toHaveLength(1);
    wrapper.unmount();
  });

  it('binds one MCP as a whole service and exposes its child tools on demand', async () => {
    apiMocks.getSkillToolOptions.mockImplementation(
      ({ toolType }: { toolType: string }) =>
        Promise.resolve(optionResponse(toolType)),
    );
    const wrapper = mountDialog({ modelValue: true, skillId: 101 });
    await flushPromises();

    const mcpTab = wrapper
      .findAll('[role="tab"]')
      .find((tab) => tab.text().trim() === 'MCP');
    await mcpTab?.trigger('click');
    await flushPromises();
    await wrapper.get('.skill-tools__candidate-content').trigger('click');
    await flushPromises();

    expect(apiMocks.getSkillMcpTools).toHaveBeenCalledWith(20);
    expect(wrapper.text()).toContain('2 个工具');
    const expand = wrapper
      .findAll('button')
      .find((button) => button.text().includes('查看工具'));
    await expand?.trigger('click');
    await flushPromises();
    expect(wrapper.text()).toContain('query_order');
    expect(wrapper.text()).toContain('create_order');
    wrapper.unmount();
  });

  it('requires explicit confirmation when a bound MCP manifest changes', async () => {
    const wrapper = mountDialog({
      bindings: [
        {
          hitlEnabled: true,
          mcpToolCount: 1,
          mcpToolManifestHash: 'manifest-v1',
          resourceSummary: {
            approvalRequired: true,
            available: true,
            title: '企业 MCP',
            toolCount: 1,
          },
          targetId: 20,
          toolType: 'MCP',
        },
      ],
      modelValue: true,
      skillId: 101,
    });
    await flushPromises();

    expect(wrapper.text()).toContain('清单变化');
    const saveButton = wrapper
      .findAll('button')
      .find((button) => button.text().trim() === '保存绑定');
    expect((saveButton?.element as HTMLButtonElement).disabled).toBe(true);
    const accept = wrapper
      .findAll('button')
      .find((button) => button.text().trim() === '确认最新清单');
    await accept?.trigger('click');
    await flushPromises();
    expect(wrapper.text()).not.toContain('清单变化');
    expect(wrapper.text()).toContain('保存后随下一次发布生效');
    expect((saveButton?.element as HTMLButtonElement).disabled).toBe(false);
    wrapper.unmount();
  });

  it('keeps invalid bindings visible and removable', async () => {
    const wrapper = mountDialog({
      bindings: [
        {
          resourceSummary: {
            available: false,
            title: '已删除工作流',
          },
          targetId: 99,
          toolType: 'WORKFLOW',
        },
      ],
      modelValue: true,
      skillId: 101,
    });
    await flushPromises();

    expect(wrapper.text()).toContain('已删除工作流');
    expect(wrapper.text()).toContain('请移除已失效的绑定');
    const remove = wrapper
      .findAll('button')
      .find((button) => button.text().trim() === '移除');
    await remove?.trigger('click');
    expect(wrapper.text()).toContain('从左侧选择工具');
    wrapper.unmount();
  });
});
