import { afterEach, describe, expect, it } from 'vitest';
import { claimMcpLaunchBinding, clearMcpLaunchBindingQueues, enqueueMcpLaunchBinding, getMcpLaunchQueueSize, } from './mcp-launch-binding-queue.js';
describe('mcp launch binding queue', () => {
    afterEach(() => {
        clearMcpLaunchBindingQueues();
    });
    it('claims same-workspace same-server bindings by agent id', () => {
        const firstBinding = enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server-a:7380/ws/agent',
            runtimeAccessToken: 'token-a',
            userId: 'user-a',
            schedulerBaseUrl: 'http://server-a:7380',
        });
        const secondBinding = enqueueMcpLaunchBinding({
            agentId: 'agent-b',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server-a:7380/ws/agent',
            runtimeAccessToken: 'token-b',
            userId: 'user-b',
            schedulerBaseUrl: 'http://server-a:7380',
        });
        expect(getMcpLaunchQueueSize('C:/repo/demo')).toBe(2);
        const first = claimMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server-a:7380/ws/agent',
        });
        const second = claimMcpLaunchBinding({
            agentId: 'agent-b',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server-a:7380/ws/agent',
        });
        expect(first?.agentId).toBe('agent-a');
        expect(first?.runtimeAccessToken).toBe('token-a');
        expect(second?.agentId).toBe('agent-b');
        expect(second?.runtimeAccessToken).toBe('token-b');
        expect(getMcpLaunchQueueSize('C:/repo/demo')).toBe(0);
    });
    it('does not claim another agent token from the same workspace and server', () => {
        const binding = enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        });
        expect(claimMcpLaunchBinding({
            agentId: 'agent-b',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        })).toBeNull();
        expect(binding?.agentId).toBe('agent-a');
        expect(getMcpLaunchQueueSize('C:/repo/demo')).toBe(1);
    });
    it('skips older same-workspace bindings when claiming a later agent token', () => {
        enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
            runtimeAccessToken: 'token-a',
        });
        enqueueMcpLaunchBinding({
            agentId: 'agent-b',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
            runtimeAccessToken: 'token-b',
        });
        const claimed = claimMcpLaunchBinding({
            agentId: 'agent-b',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        });
        expect(claimed?.agentId).toBe('agent-b');
        expect(claimed?.runtimeAccessToken).toBe('token-b');
        expect(getMcpLaunchQueueSize('C:/repo/demo')).toBe(1);
    });
    it('allows legacy claim without agent id only when one binding matches', () => {
        const binding = enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        });
        expect(claimMcpLaunchBinding({
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        })?.agentId).toBe('agent-a');
        expect(binding?.id).toBeTruthy();
    });
    it('rejects legacy claim without agent id when same-workspace same-server bindings are ambiguous', () => {
        enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        });
        enqueueMcpLaunchBinding({
            agentId: 'agent-b',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        });
        expect(claimMcpLaunchBinding({
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server:7380/ws/agent',
        })).toBeNull();
        expect(getMcpLaunchQueueSize('C:/repo/demo')).toBe(2);
    });
    it('matches by server URL as well as workspace path', () => {
        const firstBinding = enqueueMcpLaunchBinding({ agentId: 'agent-a', workspacePath: 'C:/repo/demo', serverUrl: 'ws://server-a:7380/ws/agent' });
        const secondBinding = enqueueMcpLaunchBinding({ agentId: 'agent-b', workspacePath: 'C:/repo/demo', serverUrl: 'ws://server-b:7380/ws/agent' });
        expect(claimMcpLaunchBinding({ workspacePath: 'C:/repo/demo', serverUrl: 'ws://server-b:7380/ws/agent' })?.agentId).toBe('agent-b');
        expect(claimMcpLaunchBinding({ workspacePath: 'C:/repo/demo', serverUrl: 'ws://server-a:7380/ws/agent' })?.agentId).toBe('agent-a');
    });
    it('matches http scheduler URLs and websocket agent URLs by scheduler origin', () => {
        enqueueMcpLaunchBinding({ agentId: 'agent-a', workspacePath: 'C:/repo/demo', schedulerBaseUrl: 'http://server-a:7380' });
        expect(claimMcpLaunchBinding({
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://server-a:7380/ws/agent',
        })?.agentId).toBe('agent-a');
    });
    it('repairs duplicated scheduler ports when enqueuing launch bindings', () => {
        const binding = enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            schedulerBaseUrl: 'http://127.0.0.1:8080:8080',
        });
        expect(binding?.serverUrl).toBe('http://127.0.0.1:8080');
        expect(binding?.schedulerBaseUrl).toBe('http://127.0.0.1:8080');
        expect(claimMcpLaunchBinding({
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://127.0.0.1:8080/ws/agent',
        })?.schedulerBaseUrl).toBe('http://127.0.0.1:8080');
    });
    it('matches localhost and 127.0.0.1 launch binding scheduler aliases', () => {
        enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://localhost:8080/ws/agent',
        });
        expect(claimMcpLaunchBinding({
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://127.0.0.1:8080/ws/agent',
        })?.agentId).toBe('agent-a');
    });
    it('falls back to the only same-workspace binding when scheduler URLs differ', () => {
        enqueueMcpLaunchBinding({
            agentId: 'agent-a',
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://127.0.0.1:7380/ws/agent',
        });
        expect(claimMcpLaunchBinding({
            workspacePath: 'C:/repo/demo',
            serverUrl: 'ws://101.42.200.84:7380/ws/agent',
        })?.agentId).toBe('agent-a');
    });
    it('does not mix different workspace paths', () => {
        const firstBinding = enqueueMcpLaunchBinding({ agentId: 'agent-a', workspacePath: 'C:/repo/a', serverUrl: 'ws://server:7380/ws/agent' });
        const secondBinding = enqueueMcpLaunchBinding({ agentId: 'agent-b', workspacePath: 'C:/repo/b', serverUrl: 'ws://server:7380/ws/agent' });
        expect(claimMcpLaunchBinding({ workspacePath: 'C:/repo/b', serverUrl: 'ws://server:7380/ws/agent' })?.agentId).toBe('agent-b');
        expect(claimMcpLaunchBinding({ workspacePath: 'C:/repo/a', serverUrl: 'ws://server:7380/ws/agent' })?.agentId).toBe('agent-a');
    });
    it('returns null for missing or invalid workspaces', () => {
        expect(enqueueMcpLaunchBinding({ agentId: '', workspacePath: 'C:/repo/demo' })).toBeNull();
        expect(enqueueMcpLaunchBinding({ agentId: 'agent-a', workspacePath: '' })).toBeNull();
        expect(claimMcpLaunchBinding({ workspacePath: 'C:/repo/missing', serverUrl: 'ws://server:7380/ws/agent' })).toBeNull();
        expect(claimMcpLaunchBinding({ workspacePath: 'C:/repo/demo' })).toBeNull();
    });
});
