import {
  describe,
  it,
  expect,
  vi,
  beforeEach,
  afterEach
} from 'vitest';

const {
  mockGetSystemIdleTime,
  mockSend,
  mockGetMainWindow
} = vi.hoisted(() => {
  const send = vi.fn();
  const getSystemIdleTime = vi.fn(() => 0);
  const getMainWindow = vi.fn(() => ({
    isDestroyed: () => false,
    webContents: { send }
  }));

  return {
    mockGetSystemIdleTime: getSystemIdleTime,
    mockSend: send,
    mockGetMainWindow: getMainWindow
  };
});

vi.mock('electron', () => ({
  powerMonitor: {
    getSystemIdleTime: mockGetSystemIdleTime
  }
}));

vi.mock('../window/create-window', () => ({
  getMainWindow: mockGetMainWindow
}));

import {
  startIdleMonitor,
  stopIdleMonitor,
  getIdleState
} from './idle-monitor';

describe('idle-monitor', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    mockGetSystemIdleTime.mockReturnValue(0);
    mockSend.mockClear();
  });

  afterEach(() => {
    stopIdleMonitor();
    vi.useRealTimers();
  });

  it('returns active when idle time is below threshold', () => {
    mockGetSystemIdleTime.mockReturnValue(0);
    expect(getIdleState()).toBe('active');
  });

  it('returns idle when idle time exceeds 15 minutes', () => {
    mockGetSystemIdleTime.mockReturnValue(15 * 60);
    expect(getIdleState()).toBe('idle');
  });

  it('sends idle-state-changed to renderer when transitioning to idle', () => {
    startIdleMonitor();

    mockGetSystemIdleTime.mockReturnValue(15 * 60);
    vi.advanceTimersByTime(10_000);

    expect(mockSend).toHaveBeenCalledWith('idle-state-changed', 'idle');
  });

  it('sends idle-state-changed to renderer when transitioning back to active', () => {
    startIdleMonitor();

    // Go idle
    mockGetSystemIdleTime.mockReturnValue(15 * 60);
    vi.advanceTimersByTime(10_000);
    mockSend.mockClear();

    // Go active
    mockGetSystemIdleTime.mockReturnValue(5);
    vi.advanceTimersByTime(10_000);

    expect(mockSend).toHaveBeenCalledWith('idle-state-changed', 'active');
  });

  it('does not fire duplicates when state stays the same', () => {
    startIdleMonitor();

    mockGetSystemIdleTime.mockReturnValue(15 * 60);
    vi.advanceTimersByTime(10_000);
    vi.advanceTimersByTime(10_000);
    vi.advanceTimersByTime(10_000);

    // Only one transition, so only one call
    const idleCalls = mockSend.mock.calls.filter(
      ([channel, state]: [string, string]) => channel === 'idle-state-changed' && state === 'idle'
    );

    expect(idleCalls.length).toBe(1);
  });

  it('stops polling after stopIdleMonitor', () => {
    startIdleMonitor();

    mockGetSystemIdleTime.mockReturnValue(15 * 60);
    vi.advanceTimersByTime(10_000);
    mockSend.mockClear();

    stopIdleMonitor();

    mockGetSystemIdleTime.mockReturnValue(0);
    vi.advanceTimersByTime(10_000);

    expect(mockSend).not.toHaveBeenCalled();
  });

  it('does not notify when main window is null', () => {
    mockGetMainWindow.mockReturnValue(null);
    startIdleMonitor();

    mockGetSystemIdleTime.mockReturnValue(15 * 60);
    vi.advanceTimersByTime(10_000);

    expect(mockSend).not.toHaveBeenCalled();
    mockGetMainWindow.mockReturnValue({
      isDestroyed: () => false,
      webContents: { send: mockSend }
    });
  });
});
