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

vi.mock('electron', () => ({ default: { ipcMain: { handle: vi.fn(), on: vi.fn() } } }));
import electron from 'electron';
import { registerInboxApi } from '../../src/main/ipc/inboxApi.js';
import type { InboxIpcDeps } from '../../src/main/ipc/inboxApi.js';

function getHandler(channel: string): (...args: unknown[]) => unknown {
  const handle = (electron.ipcMain as unknown as { handle: ReturnType<typeof vi.fn> }).handle;
  const call = handle.mock.calls.find((c) => c[0] === channel);
  if (!call) throw new Error(`channel ${channel} not registered`);
  return call[1] as (...args: unknown[]) => unknown;
}

function makeDeps(overrides: Partial<InboxIpcDeps> = {}): InboxIpcDeps {
  const repo = {
    search: vi.fn(() => []),
    reviewAggregate: vi.fn(() => ({ totalCount: 0, recentNotes: [], tagCounts: [], dueProgress: { total: 0, passed: 0, pending: 0 } })),
    list: vi.fn(),
    listByStatus: vi.fn(),
    countByStatus: vi.fn(() => 0),
    countByAiStatus: vi.fn(() => 0),
    countTrashed: vi.fn(() => 0),
    countFailed: vi.fn(() => 0),
    listTrashed: vi.fn(() => []),
    setStatus: vi.fn(),
    requeueDisabled: vi.fn(() => 0),
    getAllPendingJobs: vi.fn(() => []),
    getPendingCount: vi.fn(() => 0),
    countToday: vi.fn(() => 0),
    findById: vi.fn(),
    listRevisions: vi.fn(() => []),
    restoreRevision: vi.fn(),
    updateRawText: vi.fn()
  } as unknown as InboxIpcDeps['repo'];
  return {
    repo,
    continuity: { get: vi.fn() } as unknown as InboxIpcDeps['continuity'],
    capture: {} as InboxIpcDeps['capture'],
    health: {} as InboxIpcDeps['health'],
    intent: {} as InboxIpcDeps['intent'],
    getInboxWindow: () => null,
    settings: {} as InboxIpcDeps['settings'],
    providerHolder: {} as InboxIpcDeps['providerHolder'],
    paths: { profileDir: '/tmp' },
    ...overrides
  };
}

describe('inboxApi search/review IPC', () => {
  beforeEach(() => {
    (electron.ipcMain as unknown as { handle: ReturnType<typeof vi.fn> }).handle.mockClear();
  });

  it('inbox:search — repo.search 호출 결과 반환', async () => {
    const deps = makeDeps();
    (deps.repo.search as ReturnType<typeof vi.fn>).mockReturnValue([{ id: 'a' }]);
    registerInboxApi(deps);
    const h = getHandler('inbox:search');
    const r = await h({}, '회의', { status: 'active', limit: 10 });
    expect(deps.repo.search).toHaveBeenCalledWith('회의', { status: 'active', limit: 10 });
    expect(r).toEqual([{ id: 'a' }]);
  });

  it('inbox:review-aggregate — repo.reviewAggregate 호출 결과 반환', async () => {
    const deps = makeDeps();
    const fake = { totalCount: 5, recentNotes: [], tagCounts: [{ tag: 'x', count: 2 }], dueProgress: { total: 1, passed: 1, pending: 0 } };
    (deps.repo.reviewAggregate as ReturnType<typeof vi.fn>).mockReturnValue(fake);
    registerInboxApi(deps);
    const h = getHandler('inbox:review-aggregate');
    const r = await h({}, 'weekly');
    expect(deps.repo.reviewAggregate).toHaveBeenCalledWith('weekly');
    expect(r).toEqual(fake);
  });

  it('inbox:review-aggregate — 잘못된 period reject', async () => {
    const deps = makeDeps();
    registerInboxApi(deps);
    const h = getHandler('inbox:review-aggregate');
    const r = await h({}, 'yearly');
    expect(deps.repo.reviewAggregate).not.toHaveBeenCalled();
    expect(r).toMatchObject({ totalCount: 0 });
  });
});
