import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ScreenPoller } from '../../src/bridge/screen-poller.js';

// Mock CmuxClient
function createMockClient(screenContent = 'line1\nline2\nline3') {
  return {
    readScreen: vi.fn().mockResolvedValue(screenContent),
    sendText: vi.fn(),
    sendKey: vi.fn(),
  } as any;
}

describe('ScreenPoller', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  it('emits update on first poll', async () => {
    const client = createMockClient();
    const poller = new ScreenPoller(client, 200);
    const updates: any[] = [];

    poller.on('update', (u: any) => updates.push(u));
    poller.subscribe('client1', 'workspace:1', 'surface:1');

    // Fast-forward past first poll
    await vi.advanceTimersByTimeAsync(50);

    expect(updates.length).toBe(1);
    expect(updates[0].full).toBe(true);
    expect(updates[0].lines).toEqual(['line1', 'line2', 'line3']);

    poller.destroy();
  });

  it('does not emit when content is unchanged', async () => {
    const client = createMockClient();
    const poller = new ScreenPoller(client, 200);
    const updates: any[] = [];

    poller.on('update', (u: any) => updates.push(u));
    poller.subscribe('client1', 'workspace:1', 'surface:1');

    // First poll
    await vi.advanceTimersByTimeAsync(50);
    expect(updates.length).toBe(1);

    // Second poll — same content
    await vi.advanceTimersByTimeAsync(250);
    expect(updates.length).toBe(1);

    poller.destroy();
  });

  it('sends diff patches when content changes', async () => {
    const client = createMockClient('line1\nline2\nline3');
    const poller = new ScreenPoller(client, 200);
    const updates: any[] = [];

    poller.on('update', (u: any) => updates.push(u));
    poller.subscribe('client1', 'workspace:1', 'surface:1');

    // First poll
    await vi.advanceTimersByTimeAsync(50);
    expect(updates.length).toBe(1);

    // Change content
    client.readScreen.mockResolvedValue('line1\nline2-changed\nline3');

    // Second poll
    await vi.advanceTimersByTimeAsync(250);
    expect(updates.length).toBe(2);
    expect(updates[1].full).toBe(false);
    expect(updates[1].patches).toBeDefined();

    poller.destroy();
  });

  it('stops polling when all subscribers unsubscribe', async () => {
    const client = createMockClient();
    const poller = new ScreenPoller(client, 200);

    poller.subscribe('client1', 'workspace:1', 'surface:1');
    await vi.advanceTimersByTimeAsync(50);

    poller.unsubscribe('client1', 'surface:1');

    const callCount = client.readScreen.mock.calls.length;
    await vi.advanceTimersByTimeAsync(500);

    // Should not have polled again
    expect(client.readScreen.mock.calls.length).toBe(callCount);

    poller.destroy();
  });

  it('resets to fast polling on resetToFast', async () => {
    const client = createMockClient();
    const poller = new ScreenPoller(client, 200);

    poller.subscribe('client1', 'workspace:1', 'surface:1');
    await vi.advanceTimersByTimeAsync(50);

    // Simulate idle
    for (let i = 0; i < 5; i++) {
      await vi.advanceTimersByTimeAsync(250);
    }

    poller.resetToFast('surface:1');

    // Should have reset idle count internally
    poller.destroy();
  });
});
