import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
  registerHost,
  __resetHostForTests,
  getState,
  setState,
  navigate,
  sendMessage,
} from '../state';
import type { StateHost } from '../state';
import type { PopupState } from '../popup-state';

function makeHost(initial?: Partial<PopupState>): StateHost {
  let state: PopupState = {
    view: 'list',
    entries: [],
    selectedId: null,
    selectedItem: null,
    selectedIndex: 0,
    searchQuery: '',
    activeGroup: null,
    error: null,
    loading: false,
    capturedTabId: null,
    capturedUrl: '',
    newType: null,
    vaultSettings: null,
    generatorDefaults: null,
    historyItemId: null,
    ...initial,
  };

  return {
    getState: () => state,
    setState: (partial) => { state = { ...state, ...partial }; },
    navigate: vi.fn(),
    sendMessage: vi.fn().mockResolvedValue({ ok: true }),
    escapeHtml: (s) => s,
    popOutToTab: vi.fn(),
    isInTab: () => false,
    openVaultTab: vi.fn(),
  };
}

describe('shared/state', () => {
  beforeEach(() => {
    __resetHostForTests();
  });

  it('register-then-getState round-trips', () => {
    const host = makeHost({ view: 'detail' });
    registerHost(host);
    expect(getState().view).toBe('detail');
  });

  it('double-register throws', () => {
    registerHost(makeHost());
    expect(() => registerHost(makeHost())).toThrow(/already registered/);
  });

  it('__resetHostForTests clears the singleton', () => {
    registerHost(makeHost());
    __resetHostForTests();
    expect(() => getState()).toThrow(/No state host/);
  });

  it('getState without host throws', () => {
    expect(() => getState()).toThrow(/No state host/);
  });

  it('setState merges partial state', () => {
    const host = makeHost();
    registerHost(host);
    setState({ loading: true });
    expect(getState().loading).toBe(true);
  });

  it('navigate delegates to host', () => {
    const host = makeHost();
    registerHost(host);
    navigate('settings');
    expect(host.navigate).toHaveBeenCalledWith('settings', undefined);
  });

  it('sendMessage delegates to host', async () => {
    const host = makeHost();
    registerHost(host);
    const resp = await sendMessage({ type: 'is_unlocked' });
    expect(host.sendMessage).toHaveBeenCalledWith({ type: 'is_unlocked' });
    expect(resp).toEqual({ ok: true });
  });
});
