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

vi.mock('electron', () => ({ default: { ipcMain: { handle: vi.fn() }, dialog: {}, shell: {} } }));
vi.mock('../../src/main/services/VisionDetect.js', () => ({
  refreshVisionCache: vi.fn(async () => ({ ok: true as const, models: ['gemma3:12b-vision'] }))
}));
vi.mock('../../src/main/services/GitClient.js');

import electron from 'electron';
import { refreshVisionCache } from '../../src/main/services/VisionDetect.js';
import { registerSettingsApi } from '../../src/main/ipc/settingsApi.js';
import type { SettingsIpcDeps } from '../../src/main/ipc/settingsApi.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() {
  const settings = {
    getVisionModel: vi.fn(async () => 'gemma3:12b-vision'),
    setVisionModel: vi.fn(async () => {}),
    getVisionCapableCache: vi.fn(async () => ({
      models: ['gemma3:12b-vision', 'llava:13b'],
      at: '2026-05-10T05:00:00Z'
    })),
    setVisionCapableCache: vi.fn(async () => {}),
    // existing methods used by other handlers
    getAll: vi.fn(async () => ({
      ollama: { endpoint: 'http://localhost:11434', model: 'gemma2:2b' }
    })),
    setAiEnabled: vi.fn(async () => {}),
    setOnboardingCompleted: vi.fn(async () => {}),
    isAiEnabled: vi.fn(async () => true),
    getSyncRepoUrl: vi.fn(async () => null),
    setSyncRepoUrl: vi.fn(async () => {}),
    isAutoSyncEnabled: vi.fn(async () => false),
    getSyncIntervalMin: vi.fn(async () => 30),
    setSyncIntervalMin: vi.fn(async () => {}),
    setAutoSyncEnabled: vi.fn(async () => {})
  };

  const syncSvc = {
    getSyncDir: vi.fn(() => '/tmp/sync'),
    listConflicts: vi.fn(() => []),
    resolveConflict: vi.fn(async () => ({ ok: true as const })),
    getLastStatus: vi.fn(() => ({ lastAt: null as string | null, lastResult: null as { ok: boolean } | null }))
  };

  const deps: Partial<SettingsIpcDeps> = {
    backup: { runDaily: vi.fn(async () => ({ snapshotted: false })) } as never,
    exportSvc: {} as never,
    importSvc: {} as never,
    syncSvc: syncSvc as never,
    telemetry: { exportTo: vi.fn(async () => ({ eventCount: 0 })) } as never,
    settings: settings as never,
    getInboxWindow: () => null
  };

  return { settings, syncSvc, deps };
}

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

  it('3 vision channels registered', () => {
    const { deps } = makeDeps();
    registerSettingsApi(deps as SettingsIpcDeps);

    const handle = (electron.ipcMain as unknown as { handle: ReturnType<typeof vi.fn> }).handle;
    const channels = handle.mock.calls.map((c) => c[0]);
    expect(channels).toContain('settings:get-vision-models');
    expect(channels).toContain('settings:set-vision-model');
    expect(channels).toContain('settings:refresh-vision-cache');
  });

  it('settings:get-vision-models returns { models, at, selected } from settings', async () => {
    const { deps, settings } = makeDeps();
    registerSettingsApi(deps as SettingsIpcDeps);
    const h = getHandler('settings:get-vision-models');
    const r = await h({});
    expect(settings.getVisionCapableCache).toHaveBeenCalled();
    expect(settings.getVisionModel).toHaveBeenCalled();
    expect(r).toEqual({
      models: ['gemma3:12b-vision', 'llava:13b'],
      at: '2026-05-10T05:00:00Z',
      selected: 'gemma3:12b-vision'
    });
  });

  it('settings:set-vision-model calls settings.setVisionModel(value) + returns { ok: true }', async () => {
    const { deps, settings } = makeDeps();
    registerSettingsApi(deps as SettingsIpcDeps);
    const h = getHandler('settings:set-vision-model');
    const r = await h({}, 'llava:13b');
    expect(settings.setVisionModel).toHaveBeenCalledWith('llava:13b');
    expect(r).toEqual({ ok: true });
  });

  it('settings:refresh-vision-cache calls refreshVisionCache and returns result', async () => {
    const { deps } = makeDeps();
    registerSettingsApi(deps as SettingsIpcDeps);
    const h = getHandler('settings:refresh-vision-cache');
    const r = await h({});
    expect(refreshVisionCache).toHaveBeenCalledWith({
      settings: deps.settings,
      endpoint: 'http://localhost:11434'
    });
    expect(r).toEqual({ ok: true, models: ['gemma3:12b-vision'] });
  });

  it('settings:set-vision-model with null clears the value', async () => {
    const { deps, settings } = makeDeps();
    registerSettingsApi(deps as SettingsIpcDeps);
    const h = getHandler('settings:set-vision-model');
    const r = await h({}, null);
    expect(settings.setVisionModel).toHaveBeenCalledWith(null);
    expect(r).toEqual({ ok: true });
  });
});
