// @vitest-environment happy-dom
//
// serverPins persists to localStorage, which the default (node) vitest
// environment doesn't provide; no other test in this repo needs a DOM, so
// this is scoped to the file rather than switching the global environment.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { getPin, hostFromIp, removePin, setPin } from './serverPins';

// Mocked once for the whole file; the Tauri-mode describe block below
// reconfigures its behavior per test and re-imports serverPins (via
// vi.resetModules) so the module's own top-level Tauri detection + hydration
// re-runs against the current mock.
vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn() }));

describe('serverPins', () => {
  beforeEach(() => localStorage.clear());

  it('extracts host:port from urls and bare hosts', () => {
    expect(hostFromIp('https://vps.example:8666/api')).toBe('vps.example:8666');
    expect(hostFromIp('localhost:8666')).toBe('localhost:8666');
    expect(hostFromIp('  http://10.0.0.5:8666 ')).toBe('10.0.0.5:8666');
  });

  it('round-trips a pin', () => {
    setPin('vps.example:8666', 'AA:BB');
    expect(getPin('https://vps.example:8666')).toBe('AA:BB');
  });

  it('removes a pin', () => {
    setPin('h:1', 'AA');
    removePin('h:1');
    expect(getPin('h:1')).toBeNull();
  });

  it('missing pin is null', () => {
    expect(getPin('nowhere:1')).toBeNull();
  });
});

describe('serverPins (Tauri desktop bridge)', () => {
  beforeEach(() => {
    localStorage.clear();
    vi.resetModules();
    window.__TAURI_INTERNALS__ = {};
  });

  afterEach(() => {
    delete window.__TAURI_INTERNALS__;
  });

  it('hydrates localStorage from pins_all at module init', async () => {
    const { invoke } = await import('@tauri-apps/api/core');
    invoke.mockReset().mockImplementation(async (cmd) => {
      if (cmd === 'pins_all') return { 'vps.example:8666': 'AA:BB' };
      return undefined;
    });
    const mod = await import('./serverPins');
    expect(mod.getPin('vps.example:8666')).toBe('AA:BB');
  });

  it('migrates existing localStorage pins up to native when native has none yet', async () => {
    localStorage.setItem('cbm-server-pins-v1', JSON.stringify({ 'h:1': 'ZZ' }));
    const { invoke } = await import('@tauri-apps/api/core');
    invoke.mockReset().mockImplementation(async (cmd) => {
      if (cmd === 'pins_all') return {};
      return undefined;
    });
    await import('./serverPins');
    expect(invoke).toHaveBeenCalledWith('pins_set', { host: 'h:1', fingerprint: 'ZZ' });
  });

  it('setPin/removePin also invoke the native pins_set/pins_remove commands', async () => {
    const { invoke } = await import('@tauri-apps/api/core');
    invoke.mockReset().mockImplementation(async (cmd) => {
      if (cmd === 'pins_all') return {};
      return undefined;
    });
    const mod = await import('./serverPins');
    invoke.mockClear();

    mod.setPin('vps.example:8666', 'AA:BB');
    await Promise.resolve();
    await Promise.resolve();
    expect(invoke).toHaveBeenCalledWith('pins_set', { host: 'vps.example:8666', fingerprint: 'AA:BB' });

    mod.removePin('vps.example:8666');
    await Promise.resolve();
    await Promise.resolve();
    expect(invoke).toHaveBeenCalledWith('pins_remove', { host: 'vps.example:8666' });
  });
});
