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

interface ChromeStub {
  runtime: { sendMessage: ReturnType<typeof vi.fn> };
}

interface GlobalWithChrome {
  chrome?: ChromeStub;
  MediaRecorder?: { isTypeSupported: (mime: string) => boolean };
}

describe('codec strict mode', () => {
  beforeEach(() => {
    vi.resetModules();
    (globalThis as unknown as GlobalWithChrome).chrome = {
      runtime: { sendMessage: vi.fn() },
    };
  });

  // WR-02 fix: assertCodecSupported is a pure predicate that throws.
  // The previous spec expected a RECORDING_ERROR broadcast from inside
  // the assertion; that side-effect was removed (single-responsibility +
  // it double-emitted with startRecording's catch block). The notify is
  // now solely the caller's (startRecording's) responsibility — exercised
  // via integration through `classifyCaptureError` rather than from this
  // unit test, since `startRecording` requires a full getDisplayMedia
  // stub that jsdom does not provide.
  it('throws on unsupported vp9 (pure assertion, no side-effect)', async () => {
    (globalThis as unknown as GlobalWithChrome).MediaRecorder = {
      isTypeSupported: vi.fn().mockReturnValue(false),
    };
    const mod = await import('../../src/offscreen/recorder');
    expect(() => mod.assertCodecSupported()).toThrow(/vp9 unsupported/);
    const stub = (globalThis as unknown as GlobalWithChrome).chrome!;
    // The assertion itself MUST NOT emit RECORDING_ERROR.
    expect(stub.runtime.sendMessage).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: 'RECORDING_ERROR' })
    );
  });

  it('does not throw when vp9 IS supported', async () => {
    (globalThis as unknown as GlobalWithChrome).MediaRecorder = {
      isTypeSupported: vi.fn().mockReturnValue(true),
    };
    const mod = await import('../../src/offscreen/recorder');
    expect(() => mod.assertCodecSupported()).not.toThrow();
    const stub = (globalThis as unknown as GlobalWithChrome).chrome!;
    expect(stub.runtime.sendMessage).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: 'RECORDING_ERROR' })
    );
  });
});

// WR-01 fix: classifyCaptureError maps DOMException / Error shapes to a
// stable string union. The popup / SW route can switch on the code
// instead of substring-matching DOMException.message — message text
// changes between Chrome versions and locales, name and prototype do not.
describe('classifyCaptureError (WR-01 stable error codes)', () => {
  beforeEach(() => {
    vi.resetModules();
    (globalThis as unknown as GlobalWithChrome).chrome = {
      runtime: { sendMessage: vi.fn() },
    };
    (globalThis as unknown as GlobalWithChrome).MediaRecorder = {
      isTypeSupported: vi.fn().mockReturnValue(true),
    };
  });

  function makeDomError(name: string, message: string): Error {
    const e = new Error(message);
    (e as { name: string }).name = name;
    return e;
  }

  it('codec error → "codec-unsupported"', async () => {
    const mod = await import('../../src/offscreen/recorder');
    expect(mod.classifyCaptureError(new Error('vp9 unsupported. UA=<test>'))).toBe(
      'codec-unsupported',
    );
  });

  it('NotAllowedError (no system hint) → "user-cancelled"', async () => {
    const mod = await import('../../src/offscreen/recorder');
    expect(
      mod.classifyCaptureError(makeDomError('NotAllowedError', 'Permission denied by user')),
    ).toBe('user-cancelled');
  });

  it('NotAllowedError with "system" in message → "permission-denied"', async () => {
    const mod = await import('../../src/offscreen/recorder');
    expect(
      mod.classifyCaptureError(makeDomError('NotAllowedError', 'Permission denied by system')),
    ).toBe('permission-denied');
  });

  it('SecurityError → "permission-denied"', async () => {
    const mod = await import('../../src/offscreen/recorder');
    expect(mod.classifyCaptureError(makeDomError('SecurityError', 'blocked'))).toBe(
      'permission-denied',
    );
  });

  it('NotFoundError → "no-source-selected"', async () => {
    const mod = await import('../../src/offscreen/recorder');
    expect(mod.classifyCaptureError(makeDomError('NotFoundError', 'no source'))).toBe(
      'no-source-selected',
    );
  });

  it('AbortError → "capture-failed"', async () => {
    const mod = await import('../../src/offscreen/recorder');
    expect(mod.classifyCaptureError(makeDomError('AbortError', 'aborted'))).toBe(
      'capture-failed',
    );
  });

  it('arbitrary error → "unknown"', async () => {
    const mod = await import('../../src/offscreen/recorder');
    expect(mod.classifyCaptureError(new Error('totally novel failure'))).toBe('unknown');
    expect(mod.classifyCaptureError('a bare string')).toBe('unknown');
    expect(mod.classifyCaptureError(null)).toBe('unknown');
  });
});
