import { Command } from '../src/command';

describe('Command', () => {
  let commandInstance;
  let mockHandler;

  beforeEach(() => {
    commandInstance = new Command();
    mockHandler = vi.fn();
  });

  describe('registerCommand', () => {
    it('should register a command successfully', () => {
      const command = {
        name: 'testCommand',
        handler: mockHandler,
      };
      commandInstance.registerCommand(command, { commandScope: 'testScope' });

      const registeredCommand = commandInstance.listCommands().find(c => c.name === 'testScope:testCommand');
      expect(registeredCommand).toBeDefined();
      expect(registeredCommand.name).toBe('testScope:testCommand');
    });

    it('should throw an error if commandScope is not provided', () => {
      const command = {
        name: 'testCommand',
        handler: mockHandler,
      };

      expect(() => {
        commandInstance.registerCommand(command);
      }).toThrow('plugin meta.commandScope is required.');
    });

    it('should throw an error if command is already registered', () => {
      const command = {
        name: 'testCommand',
        handler: mockHandler,
      };
      commandInstance.registerCommand(command, { commandScope: 'testScope' });

      expect(() => {
        commandInstance.registerCommand(command, { commandScope: 'testScope' });
      }).toThrow(`Command 'testCommand' is already registered.`);
    });
  });

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

describe('unregisterCommand', () => {
  let commandInstance;
  let mockHandler;

  beforeEach(() => {
    commandInstance = new Command();
    mockHandler = vi.fn();
    // 先注册一个命令以便之后注销
    const command = {
      name: 'testCommand',
      handler: mockHandler,
    };
    commandInstance.registerCommand(command, { commandScope: 'testScope' });
  });

  it('should unregister a command successfully', () => {
    const commandName = 'testScope:testCommand';
    expect(commandInstance.listCommands().find(c => c.name === commandName)).toBeDefined();

    commandInstance.unregisterCommand(commandName);

    expect(commandInstance.listCommands().find(c => c.name === commandName)).toBeUndefined();
  });

  it('should throw an error if the command is not registered', () => {
    const nonExistingCommandName = 'testScope:nonExistingCommand';
    expect(() => {
      commandInstance.unregisterCommand(nonExistingCommandName);
    }).toThrow(`Command '${nonExistingCommandName}' is not registered.`);
  });

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

describe('executeCommand', () => {
  let commandInstance;
  let mockHandler;

  beforeEach(() => {
    commandInstance = new Command();
    mockHandler = vi.fn();
    // 注册一个带参数校验的命令
    const command = {
      name: 'testCommand',
      handler: mockHandler,
      parameters: [
        { name: 'param1', propType: 'string' },
        { name: 'param2', propType: 'number' }
      ],
    };
    commandInstance.registerCommand(command, { commandScope: 'testScope' });
  });

  it('should execute a command successfully', () => {
    const commandName = 'testScope:testCommand';
    const args = { param1: 'test', param2: 42 };

    commandInstance.executeCommand(commandName, args);

    expect(mockHandler).toHaveBeenCalledWith(args);
  });

  it('should throw an error if the command is not registered', () => {
    const nonExistingCommandName = 'testScope:nonExistingCommand';
    expect(() => {
      commandInstance.executeCommand(nonExistingCommandName, {});
    }).toThrow(`Command '${nonExistingCommandName}' is not registered.`);
  });

  it('should throw an error if arguments are invalid', () => {
    const commandName = 'testScope:testCommand';
    const invalidArgs = { param1: 'test', param2: 'not-a-number' }; // param2 should be a number

    expect(() => {
      commandInstance.executeCommand(commandName, invalidArgs);
    }).toThrow(`Command '${commandName}' arguments param2 is invalid.`);
  });

  it('should handle errors thrown by the command handler', () => {
    const commandName = 'testScope:testCommand';
    const args = { param1: 'test', param2: 42 };
    const errorMessage = 'Command handler error';
    mockHandler.mockImplementation(() => {
      throw new Error(errorMessage);
    });

    expect(() => {
      commandInstance.executeCommand(commandName, args);
    }).toThrow(errorMessage);
  });

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

describe('batchExecuteCommand', () => {
  let commandInstance;
  let mockHandler;
  let mockExecuteTransaction;
  let mockPluginContext;

  beforeEach(() => {
    commandInstance = new Command();
    mockHandler = vi.fn();
    mockExecuteTransaction = vi.fn(callback => callback());
    mockPluginContext = {
      common: {
        utils: {
          executeTransaction: mockExecuteTransaction
        }
      }
    };

    // 注册几个命令
    const command1 = {
      name: 'testCommand1',
      handler: mockHandler,
    };
    const command2 = {
      name: 'testCommand2',
      handler: mockHandler,
    };
    commandInstance.registerCommand(command1, { commandScope: 'testScope' });
    commandInstance.registerCommand(command2, { commandScope: 'testScope' });
  });

  it('should execute a batch of commands', () => {
    const commands = [
      { name: 'testScope:testCommand1', args: { param: 'value1' } },
      { name: 'testScope:testCommand2', args: { param: 'value2' } },
    ];

    commandInstance.batchExecuteCommand(commands, mockPluginContext);

    expect(mockExecuteTransaction).toHaveBeenCalledTimes(1);
    expect(mockHandler).toHaveBeenCalledWith({ param: 'value1' });
    expect(mockHandler).toHaveBeenCalledWith({ param: 'value2' });
  });

  it('should not execute anything if commands array is empty', () => {
    commandInstance.batchExecuteCommand([], mockPluginContext);

    expect(mockExecuteTransaction).not.toHaveBeenCalled();
    expect(mockHandler).not.toHaveBeenCalled();
  });

  it('should handle errors thrown during command execution', () => {
    const errorMessage = 'Command handler error';
    mockHandler.mockImplementation(() => {
      throw new Error(errorMessage);
    });

    const commands = [
      { name: 'testScope:testCommand1', args: { param: 'value1' } },
      { name: 'testScope:testCommand2', args: { param: 'value2' } },
    ];

    expect(() => {
      commandInstance.batchExecuteCommand(commands, mockPluginContext);
    }).toThrow(errorMessage);

    expect(mockExecuteTransaction).toHaveBeenCalledTimes(1); // Still called once
  });

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

describe('listCommands', () => {
  let commandInstance;
  let mockHandler;

  beforeEach(() => {
    commandInstance = new Command();
    mockHandler = vi.fn();
  });

  it('should list all registered commands', () => {
    // 注册几个命令
    const command1 = {
      name: 'testCommand1',
      handler: mockHandler,
      description: 'Test Command 1',
      parameters: [{ name: 'param1', propType: 'string' }]
    };
    const command2 = {
      name: 'testCommand2',
      handler: mockHandler,
      description: 'Test Command 2',
      parameters: [{ name: 'param2', propType: 'number' }]
    };
    commandInstance.registerCommand(command1, { commandScope: 'testScope' });
    commandInstance.registerCommand(command2, { commandScope: 'testScope' });

    const listedCommands = commandInstance.listCommands();

    expect(listedCommands.length).toBe(2);
    expect(listedCommands).toEqual(expect.arrayContaining([
      expect.objectContaining({
        name: 'testScope:testCommand1',
        description: 'Test Command 1',
        parameters: [{ name: 'param1', propType: 'string' }]
      }),
      expect.objectContaining({
        name: 'testScope:testCommand2',
        description: 'Test Command 2',
        parameters: [{ name: 'param2', propType: 'number' }]
      })
    ]));
  });

  it('should return an empty array if no commands are registered', () => {
    const listedCommands = commandInstance.listCommands();
    expect(listedCommands).toEqual([]);
  });

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

describe('onCommandError', () => {
  let commandInstance;
  let mockHandler;
  let mockErrorHandler1;
  let mockErrorHandler2;

  beforeEach(() => {
    commandInstance = new Command();
    mockHandler = vi.fn();
    mockErrorHandler1 = vi.fn();
    mockErrorHandler2 = vi.fn();

    // 注册一个命令，该命令会抛出错误
    const command = {
      name: 'testCommand',
      handler: () => {
        throw new Error('Command execution failed');
      },
    };
    commandInstance.registerCommand(command, { commandScope: 'testScope' });
  });

  it('should call all registered error handlers when a command throws an error', () => {
    const commandName = 'testScope:testCommand';
    commandInstance.onCommandError(mockErrorHandler1);
    commandInstance.onCommandError(mockErrorHandler2);

    expect(() => {
      commandInstance.executeCommand(commandName, {});
    }).not.toThrow();

    // 确保所有错误处理函数都被调用，并且传递了正确的参数
    expect(mockErrorHandler1).toHaveBeenCalledWith(commandName, expect.any(Error));
    expect(mockErrorHandler2).toHaveBeenCalledWith(commandName, expect.any(Error));
  });

  it('should throw the error if no error handlers are registered', () => {
    const commandName = 'testScope:testCommand';

    expect(() => {
      commandInstance.executeCommand(commandName, {});
    }).toThrow('Command execution failed');
  });

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