import * as fs from 'fs';
import * as path from 'path';
import * as readline from 'readline';
/**
 * Unified activity stream that merges events from multiple sources
 * into a single chronological timeline.
 */
export class ActivityStream {
    constructor(projectRoot) {
        this.projectRoot = projectRoot;
    }
    /** Get recent activity events merged from all sources */
    async getRecentActivity(options = {}) {
        const { limit = 100, categories, severity, since } = options;
        const events = [];
        // Gather from all sources in parallel
        const [analyticsEvents, decisionEvents, notificationEvents, costEvents] = await Promise.all([
            this.readAnalyticsEvents(),
            this.readDecisionLog(),
            this.readNotificationEvents(),
            this.readCostEvents(),
        ]);
        events.push(...analyticsEvents, ...decisionEvents, ...notificationEvents, ...costEvents);
        // Sort by timestamp descending
        events.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
        // Apply filters
        let filtered = events;
        if (categories && categories.length > 0) {
            filtered = filtered.filter(e => categories.includes(e.category));
        }
        if (severity) {
            filtered = filtered.filter(e => e.severity === severity);
        }
        if (since) {
            filtered = filtered.filter(e => e.timestamp >= since);
        }
        return filtered.slice(0, limit);
    }
    /** Read analytics events.jsonl */
    async readAnalyticsEvents() {
        const filePath = path.join(this.projectRoot, '.specweave/state/analytics/events.jsonl');
        return this.readJsonlAsActivity(filePath, (entry) => ({
            timestamp: entry.timestamp || new Date().toISOString(),
            category: entry.category === 'skill_activation' ? 'command' :
                entry.category === 'agent_spawn' ? 'command' :
                    'command',
            severity: 'info',
            title: entry.action || entry.name || entry.type || 'Analytics event',
            detail: entry.detail || entry.description,
            source: 'analytics',
            metadata: entry,
        }));
    }
    /** Read decisions.jsonl */
    async readDecisionLog() {
        const filePath = path.join(this.projectRoot, '.specweave/logs/decisions.jsonl');
        return this.readJsonlAsActivity(filePath, (entry) => ({
            timestamp: entry.timestamp || new Date().toISOString(),
            category: 'hook',
            severity: entry.decision === 'blocked' || entry.decision === 'denied'
                ? 'warning'
                : 'info',
            title: `Hook ${entry.decision || 'decision'}: ${entry.hook || entry.name || ''}`,
            detail: entry.reason || entry.message,
            source: 'decisions',
            metadata: entry,
        }));
    }
    /** Read notifications as events */
    async readNotificationEvents() {
        const filePath = path.join(this.projectRoot, '.specweave/state/notifications.json');
        try {
            if (!fs.existsSync(filePath))
                return [];
            const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
            const notifications = data?.notifications || data || [];
            if (!Array.isArray(notifications))
                return [];
            return notifications.slice(-50).map((n) => ({
                timestamp: n.createdAt || n.timestamp || new Date().toISOString(),
                category: 'notification',
                severity: n.severity === 'critical' ? 'error' :
                    n.severity === 'warning' ? 'warning' :
                        'info',
                title: n.title || n.message || 'Notification',
                detail: n.detail || n.body,
                source: 'notifications',
                metadata: { id: n.id, type: n.type, dismissed: !!n.dismissedAt },
            }));
        }
        catch {
            return [];
        }
    }
    /** Read cost events */
    async readCostEvents() {
        const filePath = path.join(this.projectRoot, '.specweave/logs/costs.json');
        try {
            if (!fs.existsSync(filePath))
                return [];
            const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
            const sessions = data?.sessions || [];
            if (!Array.isArray(sessions))
                return [];
            return sessions.slice(-30).map((s) => ({
                timestamp: s.timestamp || s.startedAt || new Date().toISOString(),
                category: 'cost',
                severity: 'info',
                title: `Session ${s.sessionId?.slice(0, 8) || ''}: $${(s.cost || 0).toFixed(4)}`,
                detail: `${s.model || 'unknown'} | ${((s.inputTokens || 0) + (s.outputTokens || 0)).toLocaleString()} tokens`,
                source: 'costs',
                metadata: { sessionId: s.sessionId, model: s.model, cost: s.cost },
            }));
        }
        catch {
            return [];
        }
    }
    /** Read a JSONL file and convert each line to an ActivityEvent */
    async readJsonlAsActivity(filePath, transform) {
        if (!fs.existsSync(filePath))
            return [];
        const events = [];
        try {
            const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
            const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
            for await (const line of rl) {
                if (!line.trim())
                    continue;
                try {
                    const entry = JSON.parse(line);
                    events.push(transform(entry));
                }
                catch { /* skip malformed */ }
            }
        }
        catch { /* file read error */ }
        // Return only last 50 from each source to keep things manageable
        return events.slice(-50);
    }
}
//# sourceMappingURL=activity-stream.js.map