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

vi.mock('electron', () => ({
  default: { ipcMain: { handle: vi.fn(), on: vi.fn() } }
}));

import electron from 'electron';
import { registerNotebookApi } from '../../src/main/ipc/notebookApi.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 makeRepo() {
  return {
    list: vi.fn(() => [] as never[]),
    create: vi.fn(),
    rename: vi.fn(),
    setColor: vi.fn(),
    delete: vi.fn(() => ({ ok: true })),
    moveNote: vi.fn(),
    findById: vi.fn(() => null)
  };
}

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

  it('notebook:list — repo.list 결과 반환', async () => {
    const repo = makeRepo();
    repo.list.mockReturnValue([{ id: '1', name: '기본', color: null, createdAt: 't', updatedAt: 't', noteCount: 0 }] as never);
    registerNotebookApi({ repo: repo as never });
    const h = getHandler('notebook:list');
    const r = await h({}) as unknown[];
    expect(r).toHaveLength(1);
  });

  it('notebook:create — UNIQUE 위반 시 ok:false reason="duplicate_name"', async () => {
    const repo = makeRepo();
    repo.create.mockImplementation(() => { throw new Error('UNIQUE constraint failed: notebooks.name'); });
    registerNotebookApi({ repo: repo as never });
    const h = getHandler('notebook:create');
    const r = await h({}, { name: '회사' }) as { ok: boolean; reason?: string };
    expect(r.ok).toBe(false);
    expect(r.reason).toBe('duplicate_name');
  });

  it('notebook:create — 빈 이름 시 ok:false reason="empty_name"', async () => {
    const repo = makeRepo();
    registerNotebookApi({ repo: repo as never });
    const h = getHandler('notebook:create');
    const r = await h({}, { name: '   ' }) as { ok: boolean; reason?: string };
    expect(r.ok).toBe(false);
    expect(r.reason).toBe('empty_name');
    expect(repo.create).not.toHaveBeenCalled();
  });

  it('notebook:rename — UNIQUE 위반 ok:false reason="duplicate_name"', async () => {
    const repo = makeRepo();
    repo.rename.mockImplementation(() => { throw new Error('UNIQUE constraint failed: notebooks.name'); });
    registerNotebookApi({ repo: repo as never });
    const h = getHandler('notebook:rename');
    const r = await h({}, 'id1', '회사') as { ok: boolean; reason?: string };
    expect(r.ok).toBe(false);
    expect(r.reason).toBe('duplicate_name');
  });

  it('notebook:delete — has_notes 시 ok:false reason 전달', async () => {
    const repo = makeRepo();
    repo.delete.mockReturnValue({ ok: false, reason: 'has_notes' } as never);
    registerNotebookApi({ repo: repo as never });
    const h = getHandler('notebook:delete');
    const r = await h({}, 'id1');
    expect(r).toEqual({ ok: false, reason: 'has_notes' });
  });

  it('notebook:move-note — repo.moveNote 호출 + ok:true', async () => {
    const repo = makeRepo();
    registerNotebookApi({ repo: repo as never });
    const h = getHandler('notebook:move-note');
    const r = await h({}, 'n1', 'nb-2');
    expect(repo.moveNote).toHaveBeenCalledWith('n1', 'nb-2');
    expect(r).toEqual({ ok: true });
  });

  it('notebook:set-color — repo.setColor 호출 + ok:true', async () => {
    const repo = makeRepo();
    registerNotebookApi({ repo: repo as never });
    const h = getHandler('notebook:set-color');
    const r = await h({}, 'id1', '#fff');
    expect(repo.setColor).toHaveBeenCalledWith('id1', '#fff');
    expect(r).toEqual({ ok: true });
  });
});
