import { McpIntegrationModule } from '../mcp.module';

describe('McpIntegrationModule', () => {
  let module: McpIntegrationModule;
  let mockTypesenseClient: {
    getClient: ReturnType<typeof vi.fn>;
  };
  let mockMcpRegistry: {
    setTypesenseClient: ReturnType<typeof vi.fn>;
    onModuleInit: ReturnType<typeof vi.fn>;
    getServerNames: ReturnType<typeof vi.fn>;
  };
  let mockLogger: {
    log: ReturnType<typeof vi.fn>;
    warn: ReturnType<typeof vi.fn>;
    error: ReturnType<typeof vi.fn>;
    debug: ReturnType<typeof vi.fn>;
    verbose: ReturnType<typeof vi.fn>;
  };

  beforeEach(() => {
    mockTypesenseClient = {
      getClient: vi.fn().mockReturnValue({ collections: () => ({}) }),
    };
    mockMcpRegistry = {
      setTypesenseClient: vi.fn(),
      onModuleInit: vi.fn().mockResolvedValue(undefined),
      getServerNames: vi.fn().mockReturnValue(['search', 'listings']),
    };
    mockLogger = {
      log: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
      debug: vi.fn(),
      verbose: vi.fn(),
    };

    module = new McpIntegrationModule(
      mockTypesenseClient as any,
      mockMcpRegistry as any,
      mockLogger as any,
    );
  });

  describe('onModuleInit', () => {
    it('sets the Typesense client on the MCP registry', async () => {
      const fakeClient = { collections: () => ({}) };
      mockTypesenseClient.getClient.mockReturnValue(fakeClient);

      await module.onModuleInit();

      expect(mockTypesenseClient.getClient).toHaveBeenCalledOnce();
      expect(mockMcpRegistry.setTypesenseClient).toHaveBeenCalledWith(fakeClient);
    });

    it('re-initializes MCP registry after setting Typesense client', async () => {
      await module.onModuleInit();

      expect(mockMcpRegistry.onModuleInit).toHaveBeenCalledOnce();
      // setTypesenseClient should be called BEFORE onModuleInit
      const setClientOrder =
        mockMcpRegistry.setTypesenseClient.mock.invocationCallOrder[0];
      const initOrder =
        mockMcpRegistry.onModuleInit.mock.invocationCallOrder[0];
      expect(setClientOrder).toBeLessThan(initOrder!);
    });

    it('logs the initialized server names', async () => {
      mockMcpRegistry.getServerNames.mockReturnValue(['search', 'listings']);

      await module.onModuleInit();

      expect(mockLogger.log).toHaveBeenCalledWith(
        'MCP servers initialized: search, listings',
        'McpIntegrationModule',
      );
    });

    it('logs empty list when no servers are registered', async () => {
      mockMcpRegistry.getServerNames.mockReturnValue([]);

      await module.onModuleInit();

      expect(mockLogger.log).toHaveBeenCalledWith(
        'MCP servers initialized: ',
        'McpIntegrationModule',
      );
    });

    it('logs single server name correctly', async () => {
      mockMcpRegistry.getServerNames.mockReturnValue(['search']);

      await module.onModuleInit();

      expect(mockLogger.log).toHaveBeenCalledWith(
        'MCP servers initialized: search',
        'McpIntegrationModule',
      );
    });
  });

  describe('module metadata', () => {
    it('has controllers metadata defined', () => {
      const controllers = Reflect.getMetadata('controllers', McpIntegrationModule);
      expect(controllers).toBeDefined();
      expect(controllers).toHaveLength(1);
    });

    it('has imports metadata defined', () => {
      const imports = Reflect.getMetadata('imports', McpIntegrationModule);
      expect(imports).toBeDefined();
      expect(imports.length).toBeGreaterThanOrEqual(2);
    });
  });
});
