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

const { handlers, mockSetStatus, mockFindById, mockGenerateRaw } = vi.hoisted(() => ({
  handlers: {} as Record<string, (...args: unknown[]) => unknown>,
  mockSetStatus: vi.fn(),
  mockFindById: vi.fn(),
  mockGenerateRaw: vi.fn()
}));

vi.mock('electron', () => ({
  default: {
    ipcMain: {
      handle: (ch: string, fn: (...args: unknown[]) => unknown) => {
        handlers[ch] = fn;
      },
      on: (_ch: string, _fn: unknown) => {}
    },
    dialog: {},
    shell: {}
  }
}));

import { registerInboxApi } from '../../src/main/ipc/inboxApi';

function makeDeps(): Parameters<typeof registerInboxApi>[0] {
  // Minimal stub — `inbox:set-status` 핸들러는 deps.repo.setStatus 만 참조.
  // `ai:classify-status` 는 deps.repo.findById + deps.providerHolder.get() 사용.
  const provider = {
    name: 'mock',
    generate: vi.fn(),
    healthCheck: vi.fn(async () => ({ ok: true })),
    generateRaw: mockGenerateRaw
  };
  return {
    repo: {
      setStatus: mockSetStatus,
      findById: mockFindById,
      list: vi.fn(),
      listByStatus: vi.fn(),
      countByStatus: vi.fn(() => 0)
    } as never,
    continuity: {} as never,
    capture: {} as never,
    health: {} as never,
    intent: {} as never,
    getInboxWindow: () => null,
    settings: {} as never,
    providerHolder: { get: () => provider } as never,
    paths: { profileDir: '/profile' }
  };
}

describe('inbox:set-status IPC', () => {
  beforeEach(() => {
    Object.keys(handlers).forEach((k) => delete handlers[k]);
    mockSetStatus.mockReset();
  });

  it('forwards valid status + reason to repo.setStatus', async () => {
    registerInboxApi(makeDeps());
    const handler = handlers['inbox:set-status'];
    if (handler === undefined) throw new Error('handler not registered');
    const r = await handler(null, 'n1', 'completed', '결재 끝');
    expect(r).toEqual({ ok: true });
    expect(mockSetStatus).toHaveBeenCalledWith('n1', 'completed', '결재 끝');
  });

  it('forwards null reason as-is', async () => {
    registerInboxApi(makeDeps());
    const handler = handlers['inbox:set-status'];
    if (handler === undefined) throw new Error('handler not registered');
    const r = await handler(null, 'n1', 'archived', null);
    expect(r).toEqual({ ok: true });
    expect(mockSetStatus).toHaveBeenCalledWith('n1', 'archived', null);
  });

  it('rejects invalid status without calling repo', async () => {
    registerInboxApi(makeDeps());
    const handler = handlers['inbox:set-status'];
    if (handler === undefined) throw new Error('handler not registered');
    const r = (await handler(null, 'n1', 'invalid', null)) as { ok: boolean; reason?: string };
    expect(r.ok).toBe(false);
    expect(r.reason).toBe('invalid status');
    expect(mockSetStatus).not.toHaveBeenCalled();
  });
});

describe('ai:classify-status IPC', () => {
  beforeEach(() => {
    Object.keys(handlers).forEach((k) => delete handlers[k]);
    mockFindById.mockReset();
    mockGenerateRaw.mockReset();
  });

  it('uses classifyStatus with note rawText/summary', async () => {
    mockFindById.mockReturnValue({
      id: 'n1',
      rawText: 'meeting notes',
      aiSummary: 's'
    });
    mockGenerateRaw.mockResolvedValue(
      '{"recommended":"completed","rationale":"끝남"}'
    );
    registerInboxApi(makeDeps());
    const handler = handlers['ai:classify-status'];
    if (handler === undefined) throw new Error('handler not registered');
    const r = (await handler(null, 'n1', '결재')) as {
      recommended: string;
      rationale: string;
    };
    expect(r.recommended).toBe('completed');
    expect(r.rationale).toBe('끝남');
    // prompt 에 rawText / summary / reason 포함
    const prompt = mockGenerateRaw.mock.calls[0]?.[0] as string;
    expect(prompt).toContain('meeting notes');
    expect(prompt).toContain('결재');
  });

  it('returns archived fallback when note not found', async () => {
    mockFindById.mockReturnValue(null);
    registerInboxApi(makeDeps());
    const handler = handlers['ai:classify-status'];
    if (handler === undefined) throw new Error('handler not registered');
    const r = (await handler(null, 'missing', '결재')) as {
      recommended: string;
      rationale: string;
    };
    expect(r.recommended).toBe('archived');
    expect(r.rationale.length).toBeGreaterThan(0);
    expect(mockGenerateRaw).not.toHaveBeenCalled();
  });

  it('returns archived fallback when AI throws', async () => {
    mockFindById.mockReturnValue({
      id: 'n1',
      rawText: 't',
      aiSummary: null
    });
    mockGenerateRaw.mockRejectedValue(new Error('network'));
    registerInboxApi(makeDeps());
    const handler = handlers['ai:classify-status'];
    if (handler === undefined) throw new Error('handler not registered');
    const r = (await handler(null, 'n1', 'r')) as {
      recommended: string;
      rationale: string;
    };
    expect(r.recommended).toBe('archived');
  });
});
