import { getJobsStatus, setProgressChangeCallback, setStateChangeCallback, updateJobProgress } from '../frameworks/queue/pQueueAdapter.js';
import { onLog } from '../frameworks/logging/logBuffer.js';
const wsClients = new Set();
let injectedDeps = null;
export function getWsClientsCount() {
    return wsClients.size;
}
export function broadcastProgress(jobId, progress, event) {
    const message = JSON.stringify({
        type: 'progress',
        jobId,
        progress,
        event,
        timestamp: new Date().toISOString(),
    });
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(message);
        }
    }
}
function broadcastLogEntry(log) {
    const message = JSON.stringify({
        type: 'log',
        log,
    });
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(message);
        }
    }
}
function broadcastStateChange() {
    const jobs = getJobsStatus();
    const clientCount = Array.from(wsClients).filter(c => c.readyState === 1).length;
    console.log(`[WS Broadcast] state: ${jobs.active.length} active reviews to ${clientCount} clients`);
    const message = JSON.stringify({
        type: 'state',
        activeReviews: jobs.active,
        recentReviews: jobs.recent,
        timestamp: new Date().toISOString(),
    });
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(message);
        }
    }
}
export function broadcastPendingChanged() {
    const message = JSON.stringify({
        type: 'pending-changed',
        timestamp: new Date().toISOString(),
    });
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(message);
        }
    }
}
export function broadcastBackfillProgress(progress) {
    const messageType = progress.status === 'completed' ? 'backfill-complete' : 'backfill-progress';
    const message = JSON.stringify({
        type: messageType,
        data: progress,
        timestamp: new Date().toISOString(),
    });
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(message);
        }
    }
}
export function buildBudgetStatusMessage(viewModel) {
    return JSON.stringify({
        type: 'budget-status',
        data: viewModel,
        timestamp: new Date().toISOString(),
    });
}
export function buildBudgetExceededMessage(payload) {
    return JSON.stringify({
        type: 'budget-exceeded',
        data: payload,
        timestamp: new Date().toISOString(),
    });
}
export function broadcastBudgetStatus(viewModel) {
    const message = buildBudgetStatusMessage(viewModel);
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(message);
        }
    }
}
export function broadcastBudgetExceeded(payload) {
    const message = buildBudgetExceededMessage(payload);
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(message);
        }
    }
}
export function setupWebSocketCallbacks(deps) {
    injectedDeps = deps;
    onLog(broadcastLogEntry);
    setProgressChangeCallback((jobId, progress, event) => {
        broadcastProgress(jobId, progress, event);
    });
    setStateChangeCallback(() => {
        broadcastStateChange();
    });
}
export function startWatchingReviewContext(jobId, localPath, mergeRequestId) {
    if (!injectedDeps) {
        throw new Error('WebSocket dependencies not initialized. Call setupWebSocketCallbacks first.');
    }
    const { reviewContextWatcher, progressPresenter } = injectedDeps;
    reviewContextWatcher.start(localPath, mergeRequestId, (contextProgress) => {
        const reviewProgress = progressPresenter.toReviewProgress(contextProgress);
        updateJobProgress(jobId, reviewProgress);
    });
}
export function stopWatchingReviewContext(mergeRequestId) {
    if (!injectedDeps) {
        throw new Error('WebSocket dependencies not initialized. Call setupWebSocketCallbacks first.');
    }
    injectedDeps.reviewContextWatcher.stop(mergeRequestId);
}
export async function registerWebSocketRoutes(app, deps) {
    const { logger } = deps;
    app.get('/ws', { websocket: true }, (socket) => {
        logger.info('WebSocket client connected');
        wsClients.add(socket);
        const jobs = getJobsStatus();
        logger.info({ activeCount: jobs.active.length, recentCount: jobs.recent.length }, 'Sending init to new WebSocket client');
        socket.send(JSON.stringify({
            type: 'init',
            activeReviews: jobs.active,
            recentReviews: jobs.recent,
            timestamp: new Date().toISOString(),
        }));
        socket.on('message', (message) => {
            try {
                const data = JSON.parse(message.toString());
                if (data.type === 'ping') {
                    socket.send(JSON.stringify({ type: 'pong' }));
                }
            }
            catch {
                // Ignore invalid messages
            }
        });
        socket.on('close', () => {
            logger.info('WebSocket client disconnected');
            wsClients.delete(socket);
        });
        socket.on('error', (error) => {
            logger.warn({ error }, 'WebSocket error');
            wsClients.delete(socket);
        });
    });
}
//# sourceMappingURL=websocket.js.map