/**
 * ActionExecutor 单元测试
 * 覆盖全部 ActionStep 变体，使用假输入控制器进行隔离测试
 */

import { ActionExecutor } from '../../../../src/modules/desktop_op/runtime/action_executor.js';
import type { InputControllerLike } from '../../../../src/modules/desktop_op/runtime/action_executor.js';

/** 构造假的输入控制器 */
function makeMockInput(): jest.Mocked<InputControllerLike> {
  return {
    click: jest.fn().mockResolvedValue(undefined),
    hotkey: jest.fn().mockResolvedValue(undefined),
    writeClipboard: jest.fn(),
    typeViaClipboard: jest.fn().mockResolvedValue(undefined),
  };
}

describe('ActionExecutor', () => {
  let executor: ActionExecutor;
  let input: jest.Mocked<InputControllerLike>;

  beforeEach(() => {
    executor = new ActionExecutor();
    input = makeMockInput();
    executor.setInput(input);
  });

  // 测试1: click 步骤 → 调用 input.click(x, y)
  it('click 步骤调用 input.click(x, y)', async () => {
    await executor.execute({ type: 'click', x: 100, y: 200 });
    expect(input.click).toHaveBeenCalledTimes(1);
    expect(input.click).toHaveBeenCalledWith(100, 200);
  });

  // 测试2: hotkey 步骤 → 调用 input.hotkey(key)
  it('hotkey 步骤调用 input.hotkey(key)', async () => {
    await executor.execute({ type: 'hotkey', key: 'ctrl+c' });
    expect(input.hotkey).toHaveBeenCalledTimes(1);
    expect(input.hotkey).toHaveBeenCalledWith('ctrl+c');
  });

  // 测试3: clipboard-write 步骤 → 同步调用 input.writeClipboard(text)
  it('clipboard-write 步骤调用 input.writeClipboard(text)', async () => {
    await executor.execute({ type: 'clipboard-write', text: '你好世界' });
    expect(input.writeClipboard).toHaveBeenCalledTimes(1);
    expect(input.writeClipboard).toHaveBeenCalledWith('你好世界');
  });

  // 测试4: type 步骤 → 调用 input.typeViaClipboard(text)
  it('type 步骤调用 input.typeViaClipboard(text)', async () => {
    await executor.execute({ type: 'type', text: '测试文本' });
    expect(input.typeViaClipboard).toHaveBeenCalledTimes(1);
    expect(input.typeViaClipboard).toHaveBeenCalledWith('测试文本');
  });

  // 测试5: wait 步骤 → 等待指定毫秒后解决
  it('wait 步骤等待指定毫秒后解决', async () => {
    jest.useFakeTimers();
    const exec = new ActionExecutor();
    const p = exec.execute({ type: 'wait', ms: 500 });
    jest.advanceTimersByTime(499);
    // 尚未解决
    let resolved = false;
    p.then(() => { resolved = true; });
    await Promise.resolve();
    expect(resolved).toBe(false);
    jest.advanceTimersByTime(1);
    await p;
    jest.useRealTimers();
  });

  // 测试6: finished 步骤 → 无副作用，不调用任何输入方法
  it('finished 步骤无副作用直接返回', async () => {
    await executor.execute({ type: 'finished' });
    expect(input.click).not.toHaveBeenCalled();
    expect(input.hotkey).not.toHaveBeenCalled();
    expect(input.writeClipboard).not.toHaveBeenCalled();
    expect(input.typeViaClipboard).not.toHaveBeenCalled();
  });

  // 测试7: failed 步骤 → 无副作用，直接返回
  it('failed 步骤无副作用直接返回', async () => {
    await executor.execute({ type: 'failed', reason: '任务失败' });
    expect(input.click).not.toHaveBeenCalled();
    expect(input.hotkey).not.toHaveBeenCalled();
    expect(input.writeClipboard).not.toHaveBeenCalled();
    expect(input.typeViaClipboard).not.toHaveBeenCalled();
  });

  // 测试8: mention 步骤 → 抛出 'mention-not-implemented'
  it('mention 步骤抛出 mention-not-implemented', async () => {
    await expect(
      executor.execute({ type: 'mention', wxid: 'wxid_abc' })
    ).rejects.toThrow('mention-not-implemented');
  });

  // 测试9: 未调用 setInput 时执行 click 抛出 'action-executor-input-not-set'
  it('未设置 input 时执行 click 抛出 action-executor-input-not-set', async () => {
    const bare = new ActionExecutor();
    await expect(
      bare.execute({ type: 'click', x: 0, y: 0 })
    ).rejects.toThrow('action-executor-input-not-set');
  });

  // 测试10: wait / finished / failed 不需要 input 也能正常执行
  it('wait / finished / failed 在未设置 input 时也能正常执行', async () => {
    const bare = new ActionExecutor();
    // wait: 需要计时器，用真实定时器但 ms=0 即可
    await expect(bare.execute({ type: 'wait', ms: 0 })).resolves.toBeUndefined();
    await expect(bare.execute({ type: 'finished' })).resolves.toBeUndefined();
    await expect(bare.execute({ type: 'failed', reason: '测试' })).resolves.toBeUndefined();
  });
});
