/**
 * Terminal Persistence 服务单元测试
 */
import { promises as fs } from 'node:fs';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const tmpRoot = path.join(process.cwd(), '.tmp-vitest-terminal-persistence');
vi.mock('node:os', () => ({
    default: {
        tmpdir: () => tmpRoot,
    },
}));
const persistence = await import('./terminal-persistence.js');
function makeSession(sessionId, agentId) {
    return {
        sessionId,
        agentId,
        workspacePath: `/workspace/${agentId}`,
        command: 'claude',
        startedAt: new Date(0).toISOString(),
        status: 'running',
    };
}
describe('terminal-persistence service', () => {
    beforeEach(async () => {
        await fs.rm(tmpRoot, { recursive: true, force: true });
    });
    afterEach(async () => {
        await fs.rm(tmpRoot, { recursive: true, force: true });
    });
    it('generates correct file path', () => {
        const getPersistedSessionsFile = (tmpdir) => {
            return `${tmpdir}/agentswork-runtime-bridge/sessions.json`;
        };
        const path = getPersistedSessionsFile('/tmp');
        expect(path.includes('agentswork-runtime-bridge')).toBe(true);
        expect(path.endsWith('sessions.json')).toBe(true);
    });
    it('serializes sessions to JSON correctly', () => {
        const sessions = [{ sessionId: 'session-1', agentId: 'agent-1', status: 'running' }];
        const json = JSON.stringify(sessions);
        const parsed = JSON.parse(json);
        expect(parsed.length).toBe(1);
        expect(parsed[0].sessionId).toBe('session-1');
    });
    it('filters running sessions on load', () => {
        const sessions = [
            { sessionId: 's1', status: 'running' },
            { sessionId: 's2', status: 'stopped' },
            { sessionId: 's3', status: 'running' },
        ];
        const runningSessions = sessions.filter(s => s.status === 'running');
        expect(runningSessions.length).toBe(2);
    });
    it('adds new session', () => {
        const sessions = [];
        const upsertSession = (newSession) => {
            const index = sessions.findIndex(s => s.sessionId === newSession.sessionId);
            if (index >= 0)
                sessions[index] = newSession;
            else
                sessions.push(newSession);
        };
        upsertSession({ sessionId: 'session-1', agentId: 'agent-1' });
        expect(sessions.length).toBe(1);
    });
    it('updates existing session', () => {
        const sessions = [{ sessionId: 'session-1', status: 'running' }];
        const index = sessions.findIndex(s => s.sessionId === 'session-1');
        if (index >= 0)
            sessions[index] = { sessionId: 'session-1', status: 'stopped' };
        expect(sessions[0].status).toBe('stopped');
    });
    it('removes session by id', () => {
        const sessions = [{ sessionId: 's1' }, { sessionId: 's2' }];
        const index = sessions.findIndex(s => s.sessionId === 's2');
        if (index >= 0)
            sessions.splice(index, 1);
        expect(sessions.length).toBe(1);
        expect(sessions.find(s => s.sessionId === 's2')).toBeUndefined();
    });
    it('prefers removing persisted session after stop to prevent later recovery', () => {
        const sessions = [
            { sessionId: 's1', agentId: 'a1', status: 'running' },
            { sessionId: 's2', agentId: 'a2', status: 'running' },
        ];
        const stoppedSessionId = 's1';
        const afterRemove = sessions.filter(s => s.sessionId !== stoppedSessionId);
        expect(afterRemove).toEqual([
            { sessionId: 's2', agentId: 'a2', status: 'running' },
        ]);
        expect(afterRemove.find(s => s.sessionId === stoppedSessionId)).toBeUndefined();
    });
    it('finds session by agentId', () => {
        const sessions = [
            { sessionId: 's1', agentId: 'a1' },
            { sessionId: 's2', agentId: 'a2' },
        ];
        const found = sessions.find(s => s.agentId === 'a2');
        expect(found?.sessionId).toBe('s2');
    });
    it('handles invalid JSON on load', () => {
        const parseSessions = (raw) => {
            try {
                return JSON.parse(raw);
            }
            catch {
                return [];
            }
        };
        expect(parseSessions('invalid json')).toEqual([]);
    });
    it('keeps a single running session per agent when upserting', async () => {
        await persistence.upsertPersistedSession(makeSession('session-old', 'agent-1'));
        await persistence.upsertPersistedSession(makeSession('session-new', 'agent-1'));
        const sessions = await persistence.loadPersistedSessions();
        expect(sessions).toHaveLength(1);
        expect(sessions[0].sessionId).toBe('session-new');
    });
    it('persists latest runtime action state for restart recovery', async () => {
        await persistence.upsertPersistedSession(makeSession('session-1', 'agent-1'));
        const updated = await persistence.updatePersistedSessionRuntimeState('session-1', {
            runtimeStatus: 'tool_using',
            runtimeActionType: 'mcp',
            runtimeActionLabel: 'poll_message',
            runtimeActionDetail: '等待群聊消息',
        });
        const sessions = await persistence.loadPersistedSessions();
        expect(updated).toBe(true);
        expect(sessions[0]).toMatchObject({
            sessionId: 'session-1',
            runtimeStatus: 'tool_using',
            runtimeActionType: 'mcp',
            runtimeActionLabel: 'poll_message',
            runtimeActionDetail: '等待群聊消息',
        });
    });
    it('serializes concurrent upserts without losing sessions', async () => {
        await Promise.all([
            persistence.upsertPersistedSession(makeSession('session-1', 'agent-1')),
            persistence.upsertPersistedSession(makeSession('session-2', 'agent-2')),
            persistence.upsertPersistedSession(makeSession('session-3', 'agent-3')),
        ]);
        const sessions = await persistence.loadPersistedSessions();
        expect(sessions.map(session => session.sessionId).sort()).toEqual(['session-1', 'session-2', 'session-3']);
    });
    it('does not revive a removed session during concurrent writes', async () => {
        await persistence.savePersistedSessions([
            makeSession('session-1', 'agent-1'),
            makeSession('session-2', 'agent-2'),
        ]);
        await Promise.all([
            persistence.removePersistedSession('session-1'),
            persistence.upsertPersistedSession(makeSession('session-3', 'agent-3')),
        ]);
        const sessions = await persistence.loadPersistedSessions();
        expect(sessions.map(session => session.sessionId).sort()).toEqual(['session-2', 'session-3']);
    });
});
