import { createParagraphChangeThrottler, ParagraphChangeStore } from '../paragraphChangeThrottler';
import { Controller, Eventes } from '@tingwu/core';

describe('createParagraphChangeThrottler', () => {
  let mockController: jest.Mocked<Controller>;
  let mockCallback: jest.Mock<Promise<void>, [ParagraphChangeStore]>;

  beforeEach(() => {
    jest.useFakeTimers();
    mockController = {
      on: jest.fn(() => jest.fn()), // Return unsubscribe function
      getParagraph: jest.fn(),
      getRealTimeLivingMode: jest.fn(() => false),
    } as any;
    mockCallback = jest.fn().mockResolvedValue(undefined);
  });

  afterEach(() => {
    jest.clearAllTimers();
    jest.useRealTimers();
  });

  it('should create a throttler and register event listeners', () => {
    const unsubscribe = createParagraphChangeThrottler(mockController, mockCallback);

    expect(mockController.on).toHaveBeenCalledTimes(2);
    expect(mockController.on).toHaveBeenCalledWith('paragraphChangeSpecific', expect.any(Function));
    expect(mockController.on).toHaveBeenCalledWith('paragraphNeedTranslate', expect.any(Function));
    expect(typeof unsubscribe).toBe('function');
  });

  it('should unsubscribe event listeners when cleanup is called', () => {
    const offCallback1 = jest.fn();
    const offCallback2 = jest.fn();
    (mockController.on as jest.Mock) = jest.fn((event: string) => {
      if (event === 'paragraphChangeSpecific') return offCallback1;
      return offCallback2;
    }) as any;

    const unsubscribe = createParagraphChangeThrottler(mockController, mockCallback);
    unsubscribe();

    expect(offCallback1).toHaveBeenCalledTimes(1);
    expect(offCallback2).toHaveBeenCalledTimes(1);
  });

  it('should throttle paragraph change callbacks', async () => {
    mockController.getParagraph = jest.fn(() => ({
      isUnfix: false,
    })) as any;

    createParagraphChangeThrottler(mockController, mockCallback);

    // Simulate paragraph change events
    const paragraphChangeHandler = (mockController.on as jest.Mock).mock.calls.find(
      call => call[0] === 'paragraphChangeSpecific'
    )?.[1];

    if (paragraphChangeHandler) {
      const event: Eventes['paragraphChangeSpecific'] = {
        addParagraphPids: ['pid-1'],
        updateParagraphPids: [],
        removeParagraphPids: [],
        unfixToFixedPids: [],
        updateParagraphUserChangedPids: [],
        userFixedPids: [],
        textNotChangePids: [],
        userChangePids: [],
      };

      // Trigger event
      paragraphChangeHandler(event);

      // Wait for async callback to complete
      await Promise.resolve();
      await Promise.resolve(); // Additional tick for async operations

      // Fast-forward time (throttle is 1000ms)
      jest.advanceTimersByTime(1000);
      await Promise.resolve();

      // Should be called (throttle may call immediately or after delay)
      expect(mockCallback).toHaveBeenCalled();
      const callArgs = mockCallback.mock.calls[0][0];
      expect(callArgs).toHaveProperty('toAdd');
      expect(callArgs).toHaveProperty('toUpdate');
      expect(callArgs).toHaveProperty('toRemove');
      expect(callArgs.toAdd).toHaveProperty('pid-1');
    }
  });
});

