/**
 * plugin store 单元测试
 *
 * 覆盖 usePluginStore 的状态管理逻辑：
 * - 初始状态值
 * - fetchPlugins：加载列表、并行 schema、去重
 * - refreshMenuItems：菜单生成、分组、各页面类型路由
 * - schema 缓存
 * - 错误处理（schema 加载失败、API 失败）
 */
import { describe, it, expect, vi, beforeEach } from 'vitest';

// ---------------------------------------------------------------------------
// Mock 外部依赖
// ---------------------------------------------------------------------------

const mockListPlugins = vi.fn();
const mockGetPluginSchema = vi.fn();

vi.mock('../api/plugins', () => ({
  listPlugins: (...args: unknown[]) => mockListPlugins(...args),
  getPluginSchema: (...args: unknown[]) => mockGetPluginSchema(...args),
}));

import { usePluginStore } from './plugin';
import type { PluginInfo, PluginSchemaResponse } from '../api/plugins';

// ---------------------------------------------------------------------------
// 测试辅助函数
// ---------------------------------------------------------------------------

function createFakePlugin(overrides: Partial<PluginInfo> = {}): PluginInfo {
  return {
    id: 'plugin-001',
    name: '测试插件',
    version: '1.0.0',
    description: '用于单元测试',
    status: 'running',
    config: {},
    entities: [
      { name: 'customer', display_name: '客户', table_name: 'plugin_001_customer' },
    ],
    record_version: 1,
    ...overrides,
  };
}

function createFakeSchema(
  overrides: Partial<PluginSchemaResponse> = {},
): PluginSchemaResponse {
  return {
    entities: [
      {
        name: 'customer',
        display_name: '客户',
        fields: [
          { name: 'id', field_type: 'uuid', required: true },
          { name: 'name', field_type: 'text', required: true, display_name: '名称' },
        ],
      },
    ],
    ...overrides,
  };
}

// ---------------------------------------------------------------------------
// 重置 fetchPluginsPromise（模块级变量）
// 因为 fetchPluginsPromise 是模块作用域变量，无法直接重置，
// 但在 fetchPlugins finally 块中会被设为 null。
// 只要每次 fetchPlugins 正常结束，就不会残留。
// ---------------------------------------------------------------------------

describe('usePluginStore', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    usePluginStore.setState({
      plugins: [],
      loading: false,
      pluginMenuItems: [],
      pluginMenuGroups: [],
      schemaCache: {},
    });
  });

  // =========================================================================
  // 初始状态
  // =========================================================================
  describe('初始状态', () => {
    it('应有正确的默认状态字段', () => {
      const state = usePluginStore.getState();
      expect(state).toHaveProperty('plugins');
      expect(state).toHaveProperty('loading');
      expect(state).toHaveProperty('pluginMenuItems');
      expect(state).toHaveProperty('pluginMenuGroups');
      expect(state).toHaveProperty('schemaCache');
      expect(state).toHaveProperty('fetchPlugins');
      expect(state).toHaveProperty('refreshMenuItems');
    });

    it('默认值应为空', () => {
      const state = usePluginStore.getState();
      expect(state.plugins).toEqual([]);
      expect(state.loading).toBe(false);
      expect(state.pluginMenuItems).toEqual([]);
      expect(state.pluginMenuGroups).toEqual([]);
      expect(state.schemaCache).toEqual({});
    });
  });

  // =========================================================================
  // fetchPlugins
  // =========================================================================
  describe('fetchPlugins', () => {
    it('成功时应更新 plugins 和 loading 状态', async () => {
      const fakePlugin = createFakePlugin();
      mockListPlugins.mockResolvedValueOnce({ data: [fakePlugin] });
      mockGetPluginSchema.mockResolvedValueOnce(createFakeSchema());

      await usePluginStore.getState().fetchPlugins();

      const state = usePluginStore.getState();
      expect(state.plugins).toEqual([fakePlugin]);
      expect(state.loading).toBe(false);
      expect(mockListPlugins).toHaveBeenCalledWith(1, 100, undefined);
    });

    it('加载中 loading 应为 true，完成后恢复为 false', async () => {
      let resolveList: (value: unknown) => void;
      const listPromise = new Promise((resolve) => {
        resolveList = resolve;
      });
      mockListPlugins.mockReturnValue(listPromise);

      const fetchPromise = usePluginStore.getState().fetchPlugins();

      expect(usePluginStore.getState().loading).toBe(true);

      resolveList!({ data: [] });
      await fetchPromise;

      expect(usePluginStore.getState().loading).toBe(false);
    });

    it('应并行加载所有运行中/启用插件的 schema', async () => {
      const plugin1 = createFakePlugin({ id: 'p1', status: 'running' });
      const plugin2 = createFakePlugin({ id: 'p2', status: 'enabled' });
      const plugin3 = createFakePlugin({ id: 'p3', status: 'disabled' });

      mockListPlugins.mockResolvedValueOnce({ data: [plugin1, plugin2, plugin3] });
      mockGetPluginSchema
        .mockResolvedValueOnce(createFakeSchema())
        .mockResolvedValueOnce(createFakeSchema());

      await usePluginStore.getState().fetchPlugins();

      // 只有 running 和 enabled 的插件加载了 schema
      expect(mockGetPluginSchema).toHaveBeenCalledWith('p1');
      expect(mockGetPluginSchema).toHaveBeenCalledWith('p2');
      expect(mockGetPluginSchema).not.toHaveBeenCalledWith('p3');
    });

    it('schema 加载应写入 schemaCache', async () => {
      const fakePlugin = createFakePlugin({ id: 'p1', status: 'running' });
      const fakeSchema = createFakeSchema();

      mockListPlugins.mockResolvedValueOnce({ data: [fakePlugin] });
      mockGetPluginSchema.mockResolvedValueOnce(fakeSchema);

      await usePluginStore.getState().fetchPlugins();

      expect(usePluginStore.getState().schemaCache['p1']).toEqual(fakeSchema);
    });

    it('单个 schema 加载失败不应阻止其他 schema', async () => {
      const plugin1 = createFakePlugin({ id: 'p1', status: 'running' });
      const plugin2 = createFakePlugin({ id: 'p2', status: 'running' });

      mockListPlugins.mockResolvedValueOnce({ data: [plugin1, plugin2] });
      mockGetPluginSchema
        .mockRejectedValueOnce(new Error('schema fail'))
        .mockResolvedValueOnce(createFakeSchema());

      await usePluginStore.getState().fetchPlugins();

      // p1 失败不应出现在 cache 中
      expect(usePluginStore.getState().schemaCache['p1']).toBeUndefined();
      // p2 应该成功
      expect(usePluginStore.getState().schemaCache['p2']).toBeDefined();
    });

    it('无运行中插件时不应调用 getPluginSchema', async () => {
      mockListPlugins.mockResolvedValueOnce({ data: [] });

      await usePluginStore.getState().fetchPlugins();

      expect(mockGetPluginSchema).not.toHaveBeenCalled();
    });

    it('API 失败时应恢复 loading 为 false', async () => {
      mockListPlugins.mockRejectedValueOnce(new Error('network error'));

      // fetchPlugins 使用 try/finally，不捕获异常，错误会向上传播
      await expect(usePluginStore.getState().fetchPlugins()).rejects.toThrow('network error');

      expect(usePluginStore.getState().loading).toBe(false);
    });

    it('应传递 page 和 status 参数', async () => {
      mockListPlugins.mockResolvedValueOnce({ data: [] });

      await usePluginStore.getState().fetchPlugins(2, 'enabled' as any);

      expect(mockListPlugins).toHaveBeenCalledWith(2, 100, 'enabled');
    });
  });

  // =========================================================================
  // refreshMenuItems — 无 schema 回退模式
  // =========================================================================
  describe('refreshMenuItems（无 schema 回退）', () => {
    it('无运行中插件时菜单应为空', () => {
      usePluginStore.setState({
        plugins: [createFakePlugin({ status: 'disabled' })],
      });
      usePluginStore.getState().refreshMenuItems();

      expect(usePluginStore.getState().pluginMenuItems).toEqual([]);
    });

    it('无 schema 时应从 entities 生成回退菜单', () => {
      const plugin = createFakePlugin({
        id: 'p1',
        status: 'running',
        entities: [
          { name: 'customer', display_name: '客户', table_name: 't1' },
          { name: 'order', display_name: '订单', table_name: 't2' },
        ],
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: {},
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items).toHaveLength(2);
      expect(items[0]).toEqual({
        key: '/plugins/p1/customer',
        icon: 'AppstoreOutlined',
        label: '客户',
        pluginId: 'p1',
        entity: 'customer',
        pageType: 'crud',
      });
      expect(items[1]).toEqual({
        key: '/plugins/p1/order',
        icon: 'AppstoreOutlined',
        label: '订单',
        pluginId: 'p1',
        entity: 'order',
        pageType: 'crud',
      });
    });

    it('entity 无 display_name 时应使用 name 作为 label', () => {
      const plugin = createFakePlugin({
        id: 'p2',
        status: 'enabled',
        entities: [
          { name: 'product', display_name: '', table_name: 't3' },
        ],
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: {},
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items[0].label).toBe('product');
    });
  });

  // =========================================================================
  // refreshMenuItems — 有 schema 的各页面类型
  // =========================================================================
  describe('refreshMenuItems（有 schema 页面类型）', () => {
    it('crud 类型应生成正确的菜单项', () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      const schema = createFakeSchema({
        ui: {
          pages: [
            { type: 'crud' as const, entity: 'customer', label: '客户管理', icon: 'UserOutlined' },
          ],
        },
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: { p1: schema },
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items).toHaveLength(1);
      expect(items[0]).toEqual({
        key: '/plugins/p1/customer',
        icon: 'UserOutlined',
        label: '客户管理',
        pluginId: 'p1',
        entity: 'customer',
        pageType: 'crud',
      });
    });

    it('tree 类型应生成正确的菜单项', () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      const schema = createFakeSchema({
        ui: {
          pages: [
            {
              type: 'tree' as const,
              entity: 'department',
              label: '部门树',
              id_field: 'id',
              parent_field: 'parent_id',
              label_field: 'name',
            },
          ],
        },
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: { p1: schema },
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items).toHaveLength(1);
      expect(items[0]).toEqual({
        key: '/plugins/p1/tree/department',
        icon: 'ApartmentOutlined',
        label: '部门树',
        pluginId: 'p1',
        entity: 'department',
        pageType: 'tree',
      });
    });

    it('tabs 类型应生成正确的菜单项', () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      const schema = createFakeSchema({
        ui: {
          pages: [
            { type: 'tabs' as const, label: '综合视图', icon: 'LayoutOutlined', tabs: [] },
          ],
        },
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: { p1: schema },
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items).toHaveLength(1);
      expect(items[0].pageType).toBe('tabs');
      expect(items[0].key).toContain('/plugins/p1/tabs/');
      expect(items[0].key).toContain(encodeURIComponent('综合视图'));
    });

    it('graph 类型应生成正确的菜单项', () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      const schema = createFakeSchema({
        ui: {
          pages: [
            {
              type: 'graph' as const,
              entity: 'relation',
              label: '关系图',
              relationship_entity: 'rel',
              source_field: 'src',
              target_field: 'tgt',
              edge_label_field: 'label',
              node_label_field: 'name',
            },
          ],
        },
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: { p1: schema },
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items).toHaveLength(1);
      expect(items[0]).toEqual({
        key: '/plugins/p1/graph/relation',
        icon: 'ApartmentOutlined',
        label: '关系图',
        pluginId: 'p1',
        entity: 'relation',
        pageType: 'graph',
      });
    });

    it('dashboard 类型应生成正确的菜单项', () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      const schema = createFakeSchema({
        ui: {
          pages: [
            { type: 'dashboard' as const, label: '仪表盘', widgets: [] },
          ],
        },
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: { p1: schema },
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items).toHaveLength(1);
      expect(items[0]).toEqual({
        key: '/plugins/p1/dashboard',
        icon: 'DashboardOutlined',
        label: '仪表盘',
        pluginId: 'p1',
        pageType: 'dashboard',
      });
    });

    it('kanban 类型应生成正确的菜单项', () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      const schema = createFakeSchema({
        ui: {
          pages: [
            {
              type: 'kanban' as const,
              entity: 'task',
              label: '看板',
              lane_field: 'status',
              card_title_field: 'title',
            },
          ],
        },
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: { p1: schema },
      });
      usePluginStore.getState().refreshMenuItems();

      const items = usePluginStore.getState().pluginMenuItems;
      expect(items).toHaveLength(1);
      expect(items[0]).toEqual({
        key: '/plugins/p1/kanban/task',
        icon: 'UnorderedListOutlined',
        label: '看板',
        pluginId: 'p1',
        entity: 'task',
        pageType: 'kanban',
      });
    });

    it('detail 类型不应生成菜单项', () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      const schema = createFakeSchema({
        ui: {
          pages: [
            {
              type: 'detail' as const,
              entity: 'customer',
              label: '客户详情',
              sections: [{ type: 'fields', label: '基本信息', fields: ['name'] }],
            },
          ],
        },
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: { p1: schema },
      });
      usePluginStore.getState().refreshMenuItems();

      expect(usePluginStore.getState().pluginMenuItems).toEqual([]);
    });
  });

  // =========================================================================
  // pluginMenuGroups 分组
  // =========================================================================
  describe('pluginMenuGroups 分组', () => {
    it('应按 pluginId 正确分组', () => {
      const plugin1 = createFakePlugin({
        id: 'p1',
        name: '插件一',
        status: 'running',
        entities: [
          { name: 'a', display_name: '实体A', table_name: 't1' },
          { name: 'b', display_name: '实体B', table_name: 't2' },
        ],
      });
      const plugin2 = createFakePlugin({
        id: 'p2',
        name: '插件二',
        status: 'enabled',
        entities: [
          { name: 'c', display_name: '实体C', table_name: 't3' },
        ],
      });

      usePluginStore.setState({
        plugins: [plugin1, plugin2],
        schemaCache: {},
      });
      usePluginStore.getState().refreshMenuItems();

      const groups = usePluginStore.getState().pluginMenuGroups;
      expect(groups).toHaveLength(2);

      const g1 = groups.find((g) => g.pluginId === 'p1');
      expect(g1).toBeDefined();
      expect(g1!.pluginName).toBe('插件一');
      expect(g1!.items).toHaveLength(2);

      const g2 = groups.find((g) => g.pluginId === 'p2');
      expect(g2).toBeDefined();
      expect(g2!.pluginName).toBe('插件二');
      expect(g2!.items).toHaveLength(1);
    });

    it('无运行中插件时 groups 应为空', () => {
      usePluginStore.setState({
        plugins: [createFakePlugin({ status: 'disabled' })],
      });
      usePluginStore.getState().refreshMenuItems();

      expect(usePluginStore.getState().pluginMenuGroups).toEqual([]);
    });

    it('pluginName 应从 plugins 列表查找', () => {
      const plugin = createFakePlugin({
        id: 'px',
        name: '名称查找插件',
        status: 'running',
        entities: [
          { name: 'x', display_name: 'X', table_name: 't1' },
        ],
      });

      usePluginStore.setState({
        plugins: [plugin],
        schemaCache: {},
      });
      usePluginStore.getState().refreshMenuItems();

      const groups = usePluginStore.getState().pluginMenuGroups;
      expect(groups[0].pluginName).toBe('名称查找插件');
    });
  });

  // =========================================================================
  // 集成：fetchPlugins 后菜单自动生成
  // =========================================================================
  describe('fetchPlugins 集成', () => {
    it('fetchPlugins 完成后应自动生成菜单', async () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      mockListPlugins.mockResolvedValueOnce({ data: [plugin] });
      mockGetPluginSchema.mockResolvedValueOnce(createFakeSchema());

      await usePluginStore.getState().fetchPlugins();

      // 应该有菜单项（来自回退或 schema）
      const items = usePluginStore.getState().pluginMenuItems;
      expect(items.length).toBeGreaterThan(0);
    });

    it('多次 fetchPlugins 不应导致菜单重复', async () => {
      const plugin = createFakePlugin({ id: 'p1', status: 'running' });
      mockListPlugins.mockResolvedValue({ data: [plugin] });
      mockGetPluginSchema.mockResolvedValue(createFakeSchema());

      await usePluginStore.getState().fetchPlugins();
      await usePluginStore.getState().fetchPlugins();

      // 每次都是完全替换，不会累积
      const items = usePluginStore.getState().pluginMenuItems;
      const keys = items.map((i) => i.key);
      const uniqueKeys = [...new Set(keys)];
      expect(keys.length).toBe(uniqueKeys.length);
    });
  });
});
