import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { finishSetup, STEPS } from '../setup';
import { state, clearWizardState } from '../setup-steps';

describe('finishSetup', () => {
  beforeEach(() => {
    (global as any).chrome = {
      tabs: {
        create: vi.fn(() => Promise.resolve({ id: 999 })),
        getCurrent: vi.fn(() => Promise.resolve({ id: 42 })),
        remove: vi.fn(() => Promise.resolve()),
      },
      runtime: {
        getURL: vi.fn((p: string) => `chrome-extension://abc/${p}`),
      },
    };
  });

  it('opens vault.html in a new tab', async () => {
    await finishSetup();
    expect(chrome.runtime.getURL).toHaveBeenCalledWith('vault.html');
    expect(chrome.tabs.create).toHaveBeenCalledWith({
      url: 'chrome-extension://abc/vault.html',
    });
  });

  it('closes the current setup tab after opening the vault tab', async () => {
    await finishSetup();
    expect(chrome.tabs.getCurrent).toHaveBeenCalled();
    expect(chrome.tabs.remove).toHaveBeenCalledWith(42);
  });

  it('still opens the vault tab even if closing the setup tab fails', async () => {
    (chrome.tabs.remove as any).mockRejectedValueOnce(new Error('no permission'));
    await expect(finishSetup()).resolves.not.toThrow();
    expect(chrome.tabs.create).toHaveBeenCalled();
  });
});

describe('setup step registry', () => {
  it('has the six steps in canonical order', () => {
    expect(STEPS.map((s) => s.id)).toEqual(['mode', 'host', 'connection', 'vault', 'device', 'done']);
  });

  it('each step renders non-empty HTML and attach returns a teardown', () => {
    const ctx = { state: {} as never, rerender: vi.fn(), goto: vi.fn() };
    for (const step of STEPS) {
      const html = step.render(ctx as never);
      expect(typeof html).toBe('string');
      expect(html.length).toBeGreaterThan(0);
      // render output must be in the DOM before attach (attach wires getElementById listeners)
      document.body.innerHTML = `<div id="app">${html}</div>`;
      const teardown = step.attach(document.body, ctx as never);
      expect(typeof teardown).toBe('function');
      teardown(); // must not throw
    }
  });
});

describe('clearWizardState', () => {
  afterEach(() => {
    clearWizardState();
  });

  it('zero-fills the reachable Uint8Array fields and resets state', () => {
    const carrier = new Uint8Array([1, 2, 3, 4]);
    const ref = new Uint8Array([5, 6, 7, 8]);
    state.carrierImageBytes = carrier;
    state.referenceImageBytes = ref;
    state.passphrase = 'secret';
    state.mode = 'new';

    clearWizardState();

    expect(Array.from(carrier)).toEqual([0, 0, 0, 0]);   // fill(0) ran on the captured ref
    expect(Array.from(ref)).toEqual([0, 0, 0, 0]);
    expect(state.carrierImageBytes).toBeNull();           // field reset
    expect(state.referenceImageBytes).toBeNull();
    expect(state.passphrase).toBe('');
    expect(state.mode).toBeNull();
  });
});
