const { test } = require('node:test');
const assert = require('node:assert/strict');
const { loadAudioSession, captureEvents, makeInputProvider, makeMonitoringProvider } = require('./audio_session_test_harness');

async function registerSource(api, payload = {}) {
    return api.dispatch({
        capability: 'audio-input',
        command: 'register-source',
        source: payload.providerId || 'test',
        payload: {
            sourceId: 'source-raw-id-12345',
            logicalSourceKey: 'test:instrument:primary',
            providerId: 'test_provider',
            kind: 'instrument',
            safeLabel: 'Input 1',
            channelSummary: { channelCount: 2, channelShape: 'stereo', supports: ['mono', 'stereo'] },
            operations: ['source.open', 'source.close'],
            operationHandlers: {
                'source.open': () => ({ outcome: 'handled', status: 'open', mediaStream: { secret: true } }),
                'source.close': () => ({ outcome: 'handled', status: 'closed', nativeHandle: { secret: true } }),
            },
            ...payload,
        },
    });
}

test('audio-input requires sourceId providerId and logicalSourceKey', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    const missingSource = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'test', payload: { providerId: 'test', logicalSourceKey: 'test:key' } });
    const missingProvider = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'test', payload: { sourceId: 'source-1', logicalSourceKey: 'test:key' } });
    const missingLogical = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'test', payload: { sourceId: 'source-1', providerId: 'test' } });
    const incompatible = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'test', payload: { sourceId: 'source-1', providerId: 'test', logicalSourceKey: 'test:key', version: 99 } });

    assert.equal(missingSource.outcome, 'failed');
    assert.equal(missingProvider.outcome, 'failed');
    assert.equal(missingLogical.outcome, 'failed');
    assert.equal(incompatible.outcome, 'incompatible-version');
});

test('audio-input registration list inspect select and snapshots pseudonymize source identity', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;
    const registeredEvents = captureEvents(window, 'audio-input:source-registered');
    const selectedEvents = captureEvents(window, 'audio-input:source-selected');

    const registered = await registerSource(api, { label: 'Scarlett 2i2 Serial 987654' });
    const listed = await api.dispatch({ capability: 'audio-input', command: 'list-sources', source: 'note_detect' });
    const inspected = await api.dispatch({ capability: 'audio-input', command: 'inspect', source: 'note_detect' });
    const selected = await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'note_detect', payload: { logicalSourceKey: 'test:instrument:primary' } });
    const missing = await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'note_detect', payload: { sourceId: 'missing-device' } });

    assert.equal(registered.status, 'applied');
    assert.equal(listed.payload.sources.length, 1);
    assert.equal(inspected.payload.totalSources, 1);
    assert.equal(selected.status, 'applied');
    assert.equal(selected.payload.logicalSourceKey, 'test:instrument:primary');
    assert.equal(missing.outcome, 'degraded');
    assert.match(registered.payload.sourceId, /^source-\d+$/);
    assert.match(selected.payload.sourceId, /^source-\d+$/);
    assert.equal(registeredEvents.length, 1);
    assert.equal(selectedEvents.length, 1);

    const encoded = JSON.stringify(window.feedBack.audioSession.snapshot().domains['audio-input']);
    assert.equal(encoded.includes('source-raw-id-12345'), false);
    assert.equal(encoded.includes('Scarlett'), false);
    assert.equal(encoded.includes('987654'), false);
});

test('audio-input pseudonyms are per-bundle: distinct within a snapshot, never leak raw identity', () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;

    audioSession.registerInputSource({ sourceId: 'mic-A', providerId: 'note_detect', logicalSourceKey: 'note_detect:mic-a', label: '/Users/me/My Songs/mic A' });
    audioSession.registerInputSource({ sourceId: 'mic-B', providerId: 'note_detect', logicalSourceKey: 'note_detect:mic-b', label: 'device B' });

    const inputDomain = audioSession.snapshot().domains['audio-input'];
    const pseudonyms = inputDomain.sources.map(source => source.diagnosticsPseudonym);

    // Per-bundle pseudonyms are distinct within one snapshot (spec FR-011/SC-005).
    assert.equal(new Set(pseudonyms).size, pseudonyms.length);
    for (const pseudonym of pseudonyms) assert.match(pseudonym, /^source-\d+$/);

    // Raw source ids/labels never leak into diagnostics.
    const encoded = JSON.stringify(inputDomain);
    assert.equal(encoded.includes('mic-A'), false);
    assert.equal(encoded.includes('/Users/me'), false);
});

test('audio-input degraded select and unknown unregister never leak the raw source id', () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;

    const degraded = audioSession.selectInputSource('/Users/me/secret-device', 'note_detect');
    const removed = audioSession.unregisterInputSource('/Users/me/secret-device');

    assert.equal(degraded.outcome, 'degraded');
    assert.match(degraded.payload.sourceId, /^source-\d+$/);
    assert.equal(removed.outcome, 'no-handler');
    assert.match(removed.payload.sourceId, /^source-\d+$/);

    const encoded = JSON.stringify({ degraded, removed, snapshot: audioSession.snapshot() });
    assert.equal(encoded.includes('/Users/me'), false);
    assert.equal(encoded.includes('secret-device'), false);
});

test('unknown unregister echoes the requested logicalSourceKey/providerId without pseudonymizing them', () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;

    const removed = audioSession.unregisterInputSource({ logicalSourceKey: 'note_detect:instrument:primary', providerId: 'note_detect' });

    assert.equal(removed.outcome, 'no-handler');
    // The logical key is a redaction-safe handle — echoed back verbatim, not pseudonymized as a sourceId.
    assert.equal(removed.payload.logicalSourceKey, 'note_detect:instrument:primary');
    assert.equal(removed.payload.providerId, 'note_detect');
    assert.equal(removed.payload.sourceId, '');
    assert.equal(removed.payload.removed, false);
});

test('unregister-source uses providerId to target the right source among logical-key duplicates', () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;

    audioSession.registerInputSource({ sourceId: 'native-x', logicalSourceKey: 'dup:disambig', providerId: 'native_p', kind: 'instrument', safeLabel: 'N' });
    audioSession.registerInputSource({ sourceId: 'compat-x', logicalSourceKey: 'dup:disambig', providerId: 'compat_p', compatibilitySource: 'legacy', kind: 'instrument', safeLabel: 'C' });

    // Without providerId, priority resolves to the native winner; the compat providerId must target compat.
    const removed = audioSession.unregisterInputSource({ logicalSourceKey: 'dup:disambig', providerId: 'compat_p' });

    assert.equal(removed.outcome, 'handled');
    assert.equal(removed.payload.providerId, 'compat_p');
    assert.equal(audioSession.snapshot().domains['audio-input'].totalSources, 1);
});

test('register-source rejects a sourceId already owned by another provider', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    const first = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'prov_a', payload: { sourceId: 'shared-id', logicalSourceKey: 'a:key', providerId: 'prov_a' } });
    const collision = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'prov_b', payload: { sourceId: 'shared-id', logicalSourceKey: 'b:key', providerId: 'prov_b' } });
    // Re-registering the same source by the same provider is still an update, not a collision.
    const update = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'prov_a', payload: { sourceId: 'shared-id', logicalSourceKey: 'a:key', providerId: 'prov_a' } });

    assert.equal(first.outcome, 'handled');
    assert.equal(collision.outcome, 'failed');
    assert.equal(update.outcome, 'handled');
    // The original provider's source survives the rejected collision.
    const inspected = await api.dispatch({ capability: 'audio-input', command: 'inspect', source: 'test', payload: { logicalSourceKey: 'a:key' } });
    assert.equal(inspected.payload.source.providerId, 'prov_a');
});

test('register-source rejects a logicalSourceKey that is not redaction-safe', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    const result = await api.dispatch({ capability: 'audio-input', command: 'register-source', source: 'p', payload: { sourceId: 's1', providerId: 'p', logicalSourceKey: '/Users/me/secret token=abc123' } });

    assert.equal(result.outcome, 'failed');
    // The unsafe key must not be stored or leak into diagnostics.
    const encoded = JSON.stringify(window.feedBack.audioSession.snapshot());
    assert.equal(encoded.includes('/Users/me'), false);
    assert.equal(encoded.includes('token=abc123'), false);
});

test('enumerate denied with an unsafe providerId never leaks it into diagnostics', async () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;

    // Missing explicit/userInitiated -> denied; the caller-supplied providerId is unsafe and is
    // recorded as the outcome participantId/providerId, so it must be bounded in the snapshot.
    const result = await audioSession.enumerateInputSources({ providerId: '/Users/me/p token=xyz789' });

    assert.equal(result.outcome, 'denied');
    const outcomes = audioSession.snapshot().recentOutcomes;
    assert.equal(outcomes.some(outcome => outcome.operation === 'source.enumerate' && outcome.outcome === 'denied'), true);
    const encoded = JSON.stringify(audioSession.snapshot());
    assert.equal(encoded.includes('/Users/me'), false);
    assert.equal(encoded.includes('token=xyz789'), false);
});

test('a malicious dispatch source is redacted before becoming a requesterId in diagnostics', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'req-raw', logicalSourceKey: 'test:req' });
    // The capability dispatch `source` (the requester identity) is attacker-controlled here.
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: '/Users/me/plugin token=abc123', payload: { logicalSourceKey: 'test:req' } });

    const encoded = JSON.stringify(window.feedBack.audioSession.snapshot());
    assert.equal(encoded.includes('/Users/me'), false);
    assert.equal(encoded.includes('abc123'), false);
});

test('caller-provided logical keys are bounded so a path/token cannot leak into diagnostics on a miss', () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;

    // Untrusted callers may pass an unsafe value as a logical key; select/unregister misses must not
    // echo or record it raw into the redaction-safe diagnostics snapshot.
    const selected = audioSession.selectInputSource({ logicalSourceKey: '/Users/me/secret token=abc123' }, 'note_detect');
    const removed = audioSession.unregisterInputSource({ logicalSourceKey: '/Users/me/secret token=abc123', providerId: '/Users/me/provider' });

    assert.equal(selected.outcome, 'degraded');
    assert.equal(removed.outcome, 'no-handler');
    const encoded = JSON.stringify({ selected, removed, snapshot: audioSession.snapshot() });
    assert.equal(encoded.includes('/Users/me'), false);
    assert.equal(encoded.includes('token=abc123'), false);
});

test('audio-monitoring distinguishes a failed state from transient unavailability', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api);
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:instrument:primary' } });
    const failedProvider = makeMonitoringProvider({ providerId: 'mon-failed', startResult: { outcome: 'failed', status: 'failed', reason: 'JUCE barrier failed' } });
    const unavailableProvider = makeMonitoringProvider({ providerId: 'mon-unavail', availability: 'unavailable' });
    await api.dispatch({ capability: 'audio-monitoring', command: 'register-provider', source: 'mon-failed', payload: failedProvider.provider });
    await api.dispatch({ capability: 'audio-monitoring', command: 'register-provider', source: 'mon-unavail', payload: unavailableProvider.provider });

    const failed = await api.dispatch({ capability: 'audio-monitoring', command: 'start', source: 'note_detect', payload: { providerId: 'mon-failed', requesterId: 'note_detect', authorization: 'user-action' } });
    const unavailable = await api.dispatch({ capability: 'audio-monitoring', command: 'start', source: 'note_detect', payload: { providerId: 'mon-unavail', requesterId: 'note_detect', authorization: 'user-action' } });

    assert.equal(failed.outcome, 'failed');
    assert.equal(failed.payload.state, 'failed');
    assert.equal(unavailable.outcome, 'unavailable');
    assert.equal(unavailable.payload.availability, 'unavailable');
});

test('inspect list and select do not call provider enumeration or open handlers', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;
    const provider = makeInputProvider({ providerId: 'note_detect', logicalSourceKey: 'note_detect:instrument:primary' });

    await registerSource(api, provider.source);
    await api.dispatch({ capability: 'audio-input', command: 'inspect', source: 'test' });
    await api.dispatch({ capability: 'audio-input', command: 'list-sources', source: 'test' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'test', payload: { logicalSourceKey: 'note_detect:instrument:primary' } });

    assert.deepEqual(provider.calls, []);
});

test('open-source and close-source record outcomes events and no live handles', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;
    const openedEvents = captureEvents(window, 'audio-input:source-opened');
    const closedEvents = captureEvents(window, 'audio-input:source-closed');
    const degradedEvents = captureEvents(window, 'audio-input:source-open-degraded');
    const deniedEvents = captureEvents(window, 'audio-input:permission-denied');

    await registerSource(api);
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:instrument:primary' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', requiredChannelShape: 'mono', purpose: 'note-detection' } });
    const close = await api.dispatch({ capability: 'audio-input', command: 'close-source', source: 'note_detect', payload: { requesterId: 'note_detect', openSessionId: open.payload.openSessionId } });

    assert.equal(open.outcome, 'handled');
    assert.equal(open.payload.state, 'open');
    assert.equal(close.outcome, 'handled');
    assert.equal(close.payload.state, 'closed');
    assert.equal(openedEvents.length, 1);
    assert.equal(closedEvents.length, 1);

    await registerSource(api, { sourceId: 'denied-source', logicalSourceKey: 'test:denied', availability: 'denied', reason: 'permission denied token=abc' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:denied' } });
    const denied = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(denied.outcome, 'denied');
    assert.equal(deniedEvents.length, 1);

    await registerSource(api, { sourceId: 'mono-only', logicalSourceKey: 'test:mono', channelSummary: { channelCount: 1, channelShape: 'mono', supports: ['mono'] } });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:mono' } });
    const incompatible = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', requiredChannelShape: 'stereo' } });
    assert.equal(incompatible.outcome, 'degraded');
    assert.equal(incompatible.payload.state, 'incompatible');
    assert.equal(degradedEvents.length >= 1, true);

    const encoded = JSON.stringify(window.feedBack.audioSession.snapshot());
    assert.equal(encoded.includes('mediaStream'), false);
    assert.equal(encoded.includes('nativeHandle'), false);
    assert.equal(encoded.includes('token=abc'), false);
});

test('open-source reports no-owner no-handler unsupported failed and malformed provider data distinctly', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    const noOwner = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(noOwner.outcome, 'no-owner');

    await registerSource(api, { sourceId: 'no-open', logicalSourceKey: 'test:no-open', providerId: 'no_open_provider', operations: [], operationHandlers: {} });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:no-open' } });
    const unsupported = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(unsupported.outcome, 'unsupported-command');

    await registerSource(api, { sourceId: 'no-handler', logicalSourceKey: 'test:no-handler', providerId: 'no_handler_provider', operations: ['source.open'], operationHandlers: {} });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:no-handler' } });
    const noHandler = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(noHandler.outcome, 'no-handler');

    await registerSource(api, { sourceId: 'failed', logicalSourceKey: 'test:failed', providerId: 'failed_provider', operationHandlers: { 'source.open': () => { throw new Error('failed near /Users/example/source'); } } });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:failed' } });
    const failed = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(failed.outcome, 'failed');

    await registerSource(api, { sourceId: 'malformed', logicalSourceKey: 'test:malformed', providerId: 'malformed_provider', operationHandlers: { 'source.open': () => 'ok' } });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:malformed' } });
    const malformed = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(malformed.outcome, 'handled');

    const outcomes = window.feedBack.audioSession.snapshot().recentOutcomes.filter(outcome => outcome.domain === 'audio-input');
    assert.equal(outcomes.some(outcome => outcome.status === 'no-owner' || outcome.outcome === 'no-owner'), true);
    assert.equal(outcomes.some(outcome => outcome.outcome === 'unsupported-command'), true);
    assert.equal(outcomes.some(outcome => outcome.outcome === 'no-handler'), true);
    assert.equal(outcomes.some(outcome => outcome.outcome === 'failed'), true);
});

test('open-source never switches to a non-selected source addressed by raw sourceId or logical key', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'primary-raw', logicalSourceKey: 'test:primary' });
    await registerSource(api, { sourceId: 'other-raw', logicalSourceKey: 'test:other' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:primary' } });

    const bySourceId = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', sourceId: 'other-raw' } });
    assert.equal(bySourceId.outcome, 'degraded');
    const byKey = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', logicalSourceKey: 'test:other' } });
    assert.equal(byKey.outcome, 'degraded');

    const selectedOpen = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(selectedOpen.outcome, 'handled');
    assert.equal(selectedOpen.payload.logicalSourceKey, 'test:primary');
});

test('open-source emits an open-session-shaped payload (with requester attribution) for a pre-denied source', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;
    const deniedEvents = captureEvents(window, 'audio-input:permission-denied');

    await registerSource(api, { sourceId: 'predenied-raw', logicalSourceKey: 'test:predenied', availability: 'denied', reason: 'permission denied' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:predenied' } });
    const denied = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', purpose: 'note-detection' } });

    assert.equal(denied.outcome, 'denied');
    const payload = deniedEvents[0].payload;
    assert.ok(payload.openSessionId);
    assert.equal(payload.requesters[0].requesterId, 'note_detect');
    assert.equal(payload.requesters[0].purpose, 'note-detection');
    // Requester entries match the real open-session shape — no per-requester openedAt.
    assert.equal('openedAt' in payload.requesters[0], false);
});

test('open-source source-open-degraded uses an open-session-shaped payload when nothing is selected', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;
    const degraded = captureEvents(window, 'audio-input:source-open-degraded');

    // No source selected -> degraded; the event must share the OpenInputSessionSummary schema.
    const result = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', purpose: 'note-detection' } });

    assert.equal(result.outcome, 'no-owner');
    const payload = degraded[degraded.length - 1].payload;
    assert.ok(payload.openSessionId);
    assert.equal(payload.state, 'unavailable');
    assert.equal(payload.requesters[0].requesterId, 'note_detect');
    assert.equal('openedAt' in payload.requesters[0], false);
});

test('open-source reports the selected source as unavailable when no matching source is registered', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'gone-raw', logicalSourceKey: 'test:gone' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:gone' } });
    // The provider unregisters the source while it is still the selected logical key.
    await api.dispatch({ capability: 'audio-input', command: 'unregister-source', source: 'test', payload: { logicalSourceKey: 'test:gone', providerId: 'test_provider' } });

    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });

    assert.equal(open.outcome, 'degraded');
    // Distinct, accurate message — not the misleading "not the selected source".
    assert.match(open.reason, /selected input source is unavailable/i);
});

test('open-source and close-source ignore a payload requesterId so callers cannot spoof session ownership', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'owned-raw', logicalSourceKey: 'test:owned' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:owned' } });

    // The authenticated dispatch caller is note_detect; a spoofed payload requesterId must be ignored.
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'victim_plugin' } });
    assert.equal(open.outcome, 'handled');
    assert.equal(open.payload.requesters.some(item => item.requesterId === 'note_detect'), true);
    assert.equal(open.payload.requesters.some(item => item.requesterId === 'victim_plugin'), false);

    // A different caller spoofing note_detect's id cannot release note_detect's reference.
    const spoofClose = await api.dispatch({ capability: 'audio-input', command: 'close-source', source: 'attacker', payload: { requesterId: 'note_detect', openSessionId: open.payload.openSessionId } });
    assert.equal(spoofClose.outcome, 'handled');
    assert.equal(spoofClose.payload.state, 'open');
    assert.equal(spoofClose.payload.requesters.some(item => item.requesterId === 'note_detect'), true);
});

test('open-source and close-source propagate a provider non-handled outcome exactly', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    // source.open returns an explicit non-denied/failed outcome -> must propagate, not collapse to degraded.
    await registerSource(api, { sourceId: 'po-raw', logicalSourceKey: 'test:po', operationHandlers: {
        'source.open': () => ({ outcome: 'unsupported-command', reason: 'no can do' }),
        'source.close': () => ({ outcome: 'handled', status: 'closed' }),
    } });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:po' } });
    const openUnsupported = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(openUnsupported.outcome, 'unsupported-command');

    // source.close returns an explicit non-failed outcome -> propagate exactly.
    await registerSource(api, { sourceId: 'pc-raw', logicalSourceKey: 'test:pc', providerId: 'pc_provider', operationHandlers: {
        'source.open': () => ({ outcome: 'handled', status: 'open' }),
        'source.close': () => ({ outcome: 'unsupported-command', reason: 'nope' }),
    } });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:pc' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(open.outcome, 'handled');
    const closeUnsupported = await api.dispatch({ capability: 'audio-input', command: 'close-source', source: 'note_detect', payload: { requesterId: 'note_detect', openSessionId: open.payload.openSessionId } });
    assert.equal(closeUnsupported.outcome, 'unsupported-command');
});

test('close-source accepts logicalKey/sourceKey aliases like the other audio-input paths', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'alias-raw', logicalSourceKey: 'test:alias' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:alias' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(open.outcome, 'handled');

    // Close by the `logicalKey` alias (no openSessionId) — must resolve the same session.
    const close = await api.dispatch({ capability: 'audio-input', command: 'close-source', source: 'note_detect', payload: { requesterId: 'note_detect', logicalKey: 'test:alias' } });
    assert.equal(close.outcome, 'handled');
    assert.equal(close.payload.state, 'closed');
});

test('close-source resolves by logicalSourceKey when requiredChannelShape is omitted', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    // Default source is stereo; open without a channel-shape hint so the session is keyed by the
    // source's resolved shape, then close by logical key alone (requiredChannelShape is optional).
    await registerSource(api, { sourceId: 'closable-raw', logicalSourceKey: 'test:closable' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:closable' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(open.outcome, 'handled');

    const close = await api.dispatch({ capability: 'audio-input', command: 'close-source', source: 'note_detect', payload: { requesterId: 'note_detect', logicalSourceKey: 'test:closable' } });
    assert.equal(close.outcome, 'handled');
    assert.equal(close.payload.state, 'closed');
});

test('close-source with an explicit wrong requiredChannelShape does not close a differently-shaped session', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    // Default source supports mono+stereo; open as mono so the session is keyed by 'mono'.
    await registerSource(api, { sourceId: 'shaped-raw', logicalSourceKey: 'test:shaped' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:shaped' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', requiredChannelShape: 'mono' } });
    assert.equal(open.outcome, 'handled');

    // An explicit, wrong shape must NOT fall back and close the mono session.
    const wrongShape = await api.dispatch({ capability: 'audio-input', command: 'close-source', source: 'note_detect', payload: { requesterId: 'note_detect', logicalSourceKey: 'test:shaped', requiredChannelShape: 'stereo' } });
    assert.equal(wrongShape.outcome, 'no-handler');
    assert.equal(window.feedBack.audioSession.snapshot().domains['audio-input'].totalOpenSessions, 1);

    // The original session still closes via openSessionId.
    const right = await api.dispatch({ capability: 'audio-input', command: 'close-source', source: 'note_detect', payload: { requesterId: 'note_detect', openSessionId: open.payload.openSessionId } });
    assert.equal(right.outcome, 'handled');
});

test('open-source rejects a non-selected duplicate sharing the logical key, addressed by sourceId', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    // Native + compatibility-backed duplicate share one logical key; the native source wins.
    await registerSource(api, { sourceId: 'native-raw', logicalSourceKey: 'dup:key', providerId: 'native_provider' });
    await registerSource(api, { sourceId: 'compat-raw', logicalSourceKey: 'dup:key', providerId: 'compat_provider', compatibilitySource: 'legacy browser handoff' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'dup:key' } });

    // Opening by the compatibility duplicate's sourceId must not switch to it.
    const wrong = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', sourceId: 'compat-raw' } });
    assert.equal(wrong.outcome, 'degraded');

    // The selected native winner still opens, routed to its own provider.
    const right = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', sourceId: 'native-raw' } });
    assert.equal(right.outcome, 'handled');
    assert.equal(right.payload.providerId, 'native_provider');
});

test('inspect resolves a raw sourceId to its source via the stable logical key', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'inspect-raw-id', logicalSourceKey: 'test:inspectable' });

    // The snapshot pseudonymizes sourceId, so inspect must resolve the raw provider sourceId itself.
    const byRaw = await api.dispatch({ capability: 'audio-input', command: 'inspect', source: 'test', payload: { sourceId: 'inspect-raw-id' } });
    assert.ok(byRaw.payload.source);
    assert.equal(byRaw.payload.source.logicalSourceKey, 'test:inspectable');

    const byKey = await api.dispatch({ capability: 'audio-input', command: 'inspect', source: 'test', payload: { logicalSourceKey: 'test:inspectable' } });
    assert.ok(byKey.payload.source);
    assert.equal(byKey.payload.source.logicalSourceKey, 'test:inspectable');
});

test('inspect by a shared logical key returns the native winner, not a suppressed duplicate', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    // Register the compatibility duplicate FIRST so a naive logicalSourceKey-only match would pick it.
    await registerSource(api, { sourceId: 'compat-first', logicalSourceKey: 'dup:inspect', providerId: 'compat_p', compatibilitySource: 'legacy' });
    await registerSource(api, { sourceId: 'native-second', logicalSourceKey: 'dup:inspect', providerId: 'native_p' });

    const inspected = await api.dispatch({ capability: 'audio-input', command: 'inspect', source: 'test', payload: { logicalSourceKey: 'dup:inspect' } });

    assert.ok(inspected.payload.source);
    assert.equal(inspected.payload.source.providerId, 'native_p');
    assert.equal(inspected.payload.source.sourceMode, 'native');
});

test('enumerate preserves a provider-supplied safeLabel through redaction', async () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;
    const provider = makeInputProvider({
        providerId: 'labelled',
        sourceId: 'labelled-bootstrap',
        logicalSourceKey: 'labelled:bootstrap',
        sources: [{ sourceId: 'lbl-1', logicalSourceKey: 'lbl:one', kind: 'instrument', safeLabel: 'Studio Mic' }],
    });
    audioSession.registerInputSource(provider.source);

    const enumerated = await audioSession.enumerateInputSources({ providerId: 'labelled', explicit: true, requesterId: 'settings' });

    assert.equal(enumerated.outcome, 'handled');
    const registered = enumerated.payload.sources.find(source => source.logicalSourceKey === 'lbl:one');
    assert.ok(registered);
    // The explicitly-safe label survives _safeInputValue instead of falling back to a pseudonym.
    assert.equal(registered.label, 'Studio Mic');
});

test('enumerate returns no-handler when providers exist but none support source.enumerate', async () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;

    audioSession.registerInputSource({ sourceId: 'nohandler-raw', logicalSourceKey: 'nh:key', providerId: 'nh_provider', operations: ['source.open'], operationHandlers: { 'source.open': () => ({ outcome: 'handled', status: 'open' }) } });

    const result = await audioSession.enumerateInputSources({ providerId: 'nh_provider', explicit: true, requesterId: 'settings' });
    assert.equal(result.outcome, 'no-handler');
});

test('open-source hint mismatch echoes a pseudonymized sourceId hint without leaking the raw id', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'sel-raw', logicalSourceKey: 'test:sel' });
    await registerSource(api, { sourceId: 'other-raw', logicalSourceKey: 'test:other' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:sel' } });

    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect', sourceId: 'other-raw' } });

    assert.equal(open.outcome, 'degraded');
    assert.match(open.payload.sourceId, /^source-\d+$/);
    const encoded = JSON.stringify({ open, snapshot: window.feedBack.audioSession.snapshot() });
    assert.equal(encoded.includes('other-raw'), false);
});

test('enumerate propagates a provider source.enumerate denial instead of empty success', async () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;
    const provider = makeInputProvider({
        providerId: 'denier',
        sourceId: 'denier-bootstrap',
        logicalSourceKey: 'denier:bootstrap',
        operationHandlers: {
            'source.enumerate': () => ({ outcome: 'denied', reason: 'user declined microphone' }),
        },
    });
    audioSession.registerInputSource(provider.source);

    const enumerated = await audioSession.enumerateInputSources({ providerId: 'denier', explicit: true, requesterId: 'settings' });

    assert.equal(enumerated.outcome, 'denied');
    const outcomes = audioSession.snapshot().recentOutcomes;
    assert.equal(outcomes.some(outcome => outcome.operation === 'source.enumerate' && outcome.outcome === 'denied'), true);

    // A non-denied/failed explicit outcome is preserved exactly (not collapsed to degraded).
    const incompatible = makeInputProvider({
        providerId: 'oldproto',
        sourceId: 'oldproto-bootstrap',
        logicalSourceKey: 'oldproto:bootstrap',
        operationHandlers: { 'source.enumerate': () => ({ outcome: 'incompatible-version', reason: 'needs v1' }) },
    });
    audioSession.registerInputSource(incompatible.source);
    const enumeratedOld = await audioSession.enumerateInputSources({ providerId: 'oldproto', explicit: true, requesterId: 'settings' });
    assert.equal(enumeratedOld.outcome, 'incompatible-version');
});

test('enumerateInputSources returns distinct sourceId pseudonyms across sources', async () => {
    const window = loadAudioSession();
    const audioSession = window.feedBack.audioSession;
    const provider = makeInputProvider({
        providerId: 'multi_provider',
        sourceId: 'mp-bootstrap',
        logicalSourceKey: 'mp:bootstrap',
        sources: [
            { sourceId: 'mp-raw-1', logicalSourceKey: 'mp:one', kind: 'instrument', safeLabel: 'In 1' },
            { sourceId: 'mp-raw-2', logicalSourceKey: 'mp:two', kind: 'instrument', safeLabel: 'In 2' },
        ],
    });
    audioSession.registerInputSource(provider.source);
    const enumerated = await audioSession.enumerateInputSources({ providerId: 'multi_provider', explicit: true, requesterId: 'settings' });

    assert.equal(enumerated.outcome, 'handled');
    assert.equal(enumerated.payload.sources.length, 2);
    const ids = enumerated.payload.sources.map(source => source.sourceId);
    ids.forEach(id => assert.match(id, /^source-\d+$/));
    // Distinct sources must get distinct pseudonyms, not all collapse to `source-01`.
    assert.equal(new Set(ids).size, 2);
});

test('open-session ids correlate between openSessions and recentOutcomes in a snapshot', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'corr-raw', logicalSourceKey: 'test:corr' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:corr' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(open.outcome, 'handled');

    const openId = open.payload.openSessionId;
    const snap = window.feedBack.audioSession.snapshot();
    assert.equal(snap.domains['audio-input'].openSessions[0].openSessionId, openId);
    const openOutcome = snap.recentOutcomes.find(outcome => outcome.operation === 'open-source' && outcome.status === 'open' && outcome.openSessionId);
    assert.ok(openOutcome);
    // The generated input-open id is left verbatim so an outcome correlates with its open session.
    assert.equal(openOutcome.openSessionId, openId);
});

test('startSession closes open input sessions from the previous session before replacing it', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;
    const audioSession = window.feedBack.audioSession;
    let providerClosed = 0;

    await registerSource(api, {
        sourceId: 'restart-raw',
        logicalSourceKey: 'test:restart',
        operationHandlers: {
            'source.open': () => ({ outcome: 'handled', status: 'open' }),
            'source.close': () => { providerClosed += 1; return { outcome: 'handled', status: 'closed' }; },
        },
    });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:restart' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(open.outcome, 'handled');

    // A direct song-switch startSession() (no stopSession first) must still release provider capture.
    audioSession.startSession({ sessionId: 'main:next-song' });
    await new Promise(resolve => setImmediate(resolve));

    assert.equal(providerClosed, 1);
    assert.equal(audioSession.snapshot().domains['audio-input'].totalOpenSessions, 0);
});

test('a persisted selected-source key that is not redaction-safe is ignored on restore', () => {
    const window = loadAudioSession();

    // Simulate a tampered/mutated localStorage entry.
    window.localStorage.setItem('feedBack.audioInput.selectedLogicalSourceKey', '/Users/me/evil token=zzz999');
    const snap = window.feedBack.audioSession.startSession({ sessionId: 'main:restore-test' });

    assert.equal(snap.domains['audio-input'].selected, null);
    const encoded = JSON.stringify(snap);
    assert.equal(encoded.includes('/Users/me'), false);
    assert.equal(encoded.includes('token=zzz999'), false);
});

test('stopSession closes open input sessions and notifies providers', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;
    const audioSession = window.feedBack.audioSession;
    const closedEvents = captureEvents(window, 'audio-input:source-closed');
    let providerClosed = 0;

    await registerSource(api, {
        sourceId: 'live-raw',
        logicalSourceKey: 'test:live',
        operationHandlers: {
            'source.open': () => ({ outcome: 'handled', status: 'open' }),
            'source.close': () => { providerClosed += 1; return { outcome: 'handled', status: 'closed' }; },
        },
    });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'test:live' } });
    const open = await api.dispatch({ capability: 'audio-input', command: 'open-source', source: 'note_detect', payload: { requesterId: 'note_detect' } });
    assert.equal(open.outcome, 'handled');
    assert.equal(audioSession.snapshot().domains['audio-input'].totalOpenSessions, 1);

    audioSession.stopSession('song switch');
    await new Promise(resolve => setImmediate(resolve));

    assert.equal(audioSession.snapshot().domains['audio-input'].totalOpenSessions, 0);
    assert.equal(closedEvents.length >= 1, true);
    assert.equal(providerClosed, 1);
});

test('startSession keeps the in-memory selection when persistence has failed, ignoring stale storage', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    // Seed storage with a stale key, then make subsequent writes fail.
    window.localStorage.setItem('feedBack.audioInput.selectedLogicalSourceKey', 'stale:old-input');
    window.localStorage.setItem = () => { throw new Error('quota'); };

    await registerSource(api, { sourceId: 'current-raw', logicalSourceKey: 'current:input' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'current:input' } });
    assert.equal(window.feedBack.audioSession.snapshot().domains['audio-input'].storageStatus, 'failed');

    const snap = window.feedBack.audioSession.startSession({ sessionId: 'main:after-fail' });

    // Must keep the in-memory 'current:input', not revert to the stale storage key.
    assert.equal(snap.domains['audio-input'].selected.logicalSourceKey, 'current:input');
    assert.equal(snap.domains['audio-input'].storageStatus, 'failed');
});

test('selected source persistence restore and storage-unavailable fallback are stable', async () => {
    const window = loadAudioSession();
    const api = window.feedBack.capabilities;

    await registerSource(api, { sourceId: 'persisted-source', logicalSourceKey: 'persisted:input' });
    await api.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'persisted:input' } });
    const afterStart = window.feedBack.audioSession.startSession({ sessionId: 'main:next-song' });
    assert.equal(afterStart.domains['audio-input'].selected.logicalSourceKey, 'persisted:input');
    assert.equal(afterStart.domains['audio-input'].selected.restoreStatus, 'restored');

    const noStorageWindow = loadAudioSession();
    noStorageWindow.localStorage.setItem = () => { throw new Error('blocked'); };
    const noStorageApi = noStorageWindow.feedBack.capabilities;
    await registerSource(noStorageApi, { sourceId: 'session-source', logicalSourceKey: 'session:input' });
    await noStorageApi.dispatch({ capability: 'audio-input', command: 'select-source', source: 'user', payload: { logicalSourceKey: 'session:input' } });
    const noStorageSnapshot = noStorageWindow.feedBack.audioSession.startSession({ sessionId: 'main:no-storage-song' });
    assert.equal(noStorageSnapshot.domains['audio-input'].storageStatus, 'failed');
    assert.equal(noStorageSnapshot.domains['audio-input'].selected.logicalSourceKey, 'session:input');
});
