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 = {
    updateRawText: vi.fn(),
    listRevisions: vi.fn(() => []),
    restoreRevision: vi.fn(),
    findById: vi.fn(),
    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)
  } 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 revisions IPC', () => {
  beforeEach(() => {
    (electron.ipcMain as unknown as { handle: ReturnType<typeof vi.fn> }).handle.mockClear();
  });

  it('inbox:update-raw-text — repo.updateRawText 호출 + ok:true', async () => {
    const deps = makeDeps();
    registerInboxApi(deps);
    const h = getHandler('inbox:update-raw-text');
    const r = await h({}, 'note-1', 'new text');
    expect(deps.repo.updateRawText).toHaveBeenCalledWith('note-1', 'new text');
    expect(r).toEqual({ ok: true });
  });

  it('inbox:update-raw-text — 빈 문자열 reject', async () => {
    const deps = makeDeps();
    registerInboxApi(deps);
    const h = getHandler('inbox:update-raw-text');
    const r = await h({}, 'note-1', '   ');
    expect(deps.repo.updateRawText).not.toHaveBeenCalled();
    expect(r).toEqual({ ok: false, reason: 'empty' });
  });

  it('inbox:list-revisions — repo.listRevisions 결과 반환', async () => {
    const deps = makeDeps();
    (deps.repo.listRevisions as ReturnType<typeof vi.fn>).mockReturnValue([
      { revId: 1, noteId: 'a', rawText: 'v1', editedAt: 't1', editedBy: 'capture' }
    ]);
    registerInboxApi(deps);
    const h = getHandler('inbox:list-revisions');
    const r = await h({}, 'a');
    expect(r).toEqual([
      { revId: 1, noteId: 'a', rawText: 'v1', editedAt: 't1', editedBy: 'capture' }
    ]);
  });

  it('inbox:restore-revision — repo throw 시 ok:false', async () => {
    const deps = makeDeps();
    (deps.repo.restoreRevision as ReturnType<typeof vi.fn>).mockImplementation(() => {
      throw new Error('revision 99 not found for note a');
    });
    registerInboxApi(deps);
    const h = getHandler('inbox:restore-revision');
    const r = await h({}, 'a', 99);
    expect(r).toEqual({ ok: false, reason: 'revision 99 not found for note a' });
  });
});
