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

const { mockApp, mockExecFile } = vi.hoisted(() => ({
  mockApp: { getLoginItemSettings: vi.fn() },
  mockExecFile: vi.fn()
}));

vi.mock('electron', () => ({
  default: { app: mockApp }
}));

vi.mock('node:child_process', () => ({
  execFile: mockExecFile
}));

import { collectAutostartState } from '../../src/main/services/AutostartDiagnostic';

const ORIGINAL_PLATFORM = process.platform;

function setPlatform(p: NodeJS.Platform): void {
  Object.defineProperty(process, 'platform', { value: p, configurable: true });
}

describe('AutostartDiagnostic — collectAutostartState', () => {
  beforeEach(() => {
    mockApp.getLoginItemSettings.mockReset();
    mockExecFile.mockReset();
  });

  afterEach(() => {
    setPlatform(ORIGINAL_PLATFORM);
  });

  it('returns withArgs / noArgs / execPath structure', async () => {
    setPlatform('darwin');
    mockApp.getLoginItemSettings
      .mockReturnValueOnce({ openAtLogin: true, executableWillLaunchAtLogin: true })
      .mockReturnValueOnce({ openAtLogin: false, executableWillLaunchAtLogin: true });

    const state = await collectAutostartState();

    expect(state.withArgs).toEqual({ openAtLogin: true, executableWillLaunchAtLogin: true });
    expect(state.noArgs).toEqual({ openAtLogin: false, executableWillLaunchAtLogin: true });
    expect(state.execPath).toBe(process.execPath);
  });

  it('passes args=["--hidden"] for the first call, no args for the second', async () => {
    setPlatform('darwin');
    mockApp.getLoginItemSettings
      .mockReturnValueOnce({ openAtLogin: true, executableWillLaunchAtLogin: true })
      .mockReturnValueOnce({ openAtLogin: true, executableWillLaunchAtLogin: true });

    await collectAutostartState();

    expect(mockApp.getLoginItemSettings).toHaveBeenNthCalledWith(1, { args: ['--hidden'] });
    expect(mockApp.getLoginItemSettings).toHaveBeenNthCalledWith(2);
  });

  it('non-win32: does not set registryPath/registryValue', async () => {
    setPlatform('darwin');
    mockApp.getLoginItemSettings.mockReturnValue({ openAtLogin: true, executableWillLaunchAtLogin: true });

    const state = await collectAutostartState();

    expect(state.registryPath).toBeUndefined();
    expect(state.registryValue).toBeUndefined();
    expect(mockExecFile).not.toHaveBeenCalled();
  });

  it('Windows: returns registryPath + registryValue when reg.exe succeeds', async () => {
    setPlatform('win32');
    mockApp.getLoginItemSettings.mockReturnValue({ openAtLogin: true, executableWillLaunchAtLogin: true });
    mockExecFile.mockImplementation((_cmd: string, _args: string[], cb: (err: Error | null, stdout: string, stderr: string) => void) => {
      cb(null, '\r\nHKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\r\n    Inkling    REG_SZ    "C:\\Users\\u\\Inkling.exe" --hidden\r\n', '');
    });

    const state = await collectAutostartState();

    expect(state.registryPath).toBe('HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\Inkling');
    expect(state.registryValue).toContain('Inkling.exe');
    expect(state.registryValue).toContain('--hidden');
  });

  it('Windows: silent fallback on reg.exe error', async () => {
    setPlatform('win32');
    mockApp.getLoginItemSettings.mockReturnValue({ openAtLogin: true, executableWillLaunchAtLogin: true });
    mockExecFile.mockImplementation((_cmd: string, _args: string[], cb: (err: Error | null, stdout: string, stderr: string) => void) => {
      cb(new Error('not found'), '', '');
    });

    const state = await collectAutostartState();

    expect(state.registryPath).toBe('HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\Inkling');
    expect(state.registryValue).toBeNull();
  });
});
