// @vitest-environment happy-dom
//
// The native mirror underlies serverPins and the workspaces map, both of
// which persist to localStorage; the default (node) vitest environment
// doesn't provide it, hence happy-dom (see serverPins.test.js).
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { hydrate, writeThrough } from './nativeMirror';

const KEY = 'test-mirror-key';

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

  it('hydrate seeds localStorage from a native value', async () => {
    const loader = vi.fn().mockResolvedValue('{"from":"native"}');
    await hydrate(KEY, loader);
    expect(localStorage.getItem(KEY)).toBe('{"from":"native"}');
  });

  it('hydrate pushes localStorage up to native once when native is empty', async () => {
    localStorage.setItem(KEY, '{"legacy":true}');
    const loader = vi.fn().mockResolvedValue(null);
    const pusher = vi.fn().mockResolvedValue(undefined);
    await hydrate(KEY, loader, pusher);
    expect(pusher).toHaveBeenCalledWith('{"legacy":true}');
    // Migration doesn't touch the existing local copy.
    expect(localStorage.getItem(KEY)).toBe('{"legacy":true}');
  });

  it('hydrate does nothing when native is empty and localStorage is empty too', async () => {
    const loader = vi.fn().mockResolvedValue(null);
    const pusher = vi.fn();
    await hydrate(KEY, loader, pusher);
    expect(pusher).not.toHaveBeenCalled();
    expect(localStorage.getItem(KEY)).toBeNull();
  });

  it('hydrate leaves localStorage untouched when native read fails / no bridge (web)', async () => {
    localStorage.setItem(KEY, '{"already":"there"}');
    const loader = vi.fn().mockResolvedValue(undefined);
    await hydrate(KEY, loader);
    expect(localStorage.getItem(KEY)).toBe('{"already":"there"}');
  });

  it('hydrate swallows a rejecting loader and leaves localStorage untouched', async () => {
    localStorage.setItem(KEY, '{"already":"there"}');
    const loader = vi.fn().mockRejectedValue(new Error('boom'));
    await expect(hydrate(KEY, loader)).resolves.toBeUndefined();
    expect(localStorage.getItem(KEY)).toBe('{"already":"there"}');
  });

  it('writeThrough updates localStorage and calls the native saver', async () => {
    const saver = vi.fn().mockResolvedValue(true);
    writeThrough(KEY, '{"a":1}', saver);
    expect(localStorage.getItem(KEY)).toBe('{"a":1}');
    // Native save is fire-and-forget; give its microtask a tick to run.
    await Promise.resolve();
    await Promise.resolve();
    expect(saver).toHaveBeenCalledWith('{"a":1}');
  });

  it('writeThrough without a saver just updates localStorage', () => {
    writeThrough(KEY, '{"b":2}');
    expect(localStorage.getItem(KEY)).toBe('{"b":2}');
  });

  it('writeThrough swallows a rejecting saver', async () => {
    const saver = vi.fn().mockRejectedValue(new Error('nope'));
    expect(() => writeThrough(KEY, '{"c":3}', saver)).not.toThrow();
    await Promise.resolve();
    await Promise.resolve();
  });
});
