import { buildFallbackChainData } from './fallback-visualizer.js';
import { logger } from '../../../utils/logger.js';
import { summarizeError } from '../../../utils/error-display.js';
const TIMELINE_MAX_POINTS = 60;
const STATUS_ORDER = {
    degraded: 0,
    rate_limited: 1,
    auth_error: 2,
    unavailable: 3,
    unknown: 4,
    healthy: 5,
};
export class ProviderHealthDataProvider {
    _healthState;
    _modelState;
    _snapshot;
    _subscribers = new Set();
    _timelines = new Map();
    constructor(healthState, modelState) {
        this._healthState = healthState;
        this._modelState = modelState;
        this._seedTimelines(healthState);
        this._snapshot = this._buildSnapshot();
    }
    getSnapshot() {
        return this._snapshot;
    }
    subscribe(callback) {
        this._subscribers.add(callback);
        return () => this._subscribers.delete(callback);
    }
    updateHealthState(healthState) {
        this._healthState = healthState;
        this._appendTimelinePoints(healthState);
        this._rebuild();
    }
    updateModelState(modelState) {
        this._modelState = modelState;
        this._rebuild();
    }
    dispose() {
        this._subscribers.clear();
    }
    _seedTimelines(healthState) {
        for (const [id, record] of healthState.providers) {
            const buffer = { points: [] };
            this._timelines.set(id, buffer);
            this._appendPoint(buffer, record);
        }
    }
    _appendTimelinePoints(healthState) {
        for (const [id, record] of healthState.providers) {
            if (!this._timelines.has(id)) {
                this._timelines.set(id, { points: [] });
            }
            this._appendPoint(this._timelines.get(id), record);
        }
    }
    _appendPoint(buffer, record) {
        const total = record.stats.totalCalls;
        const successRate = total > 0 ? record.stats.successCalls / total : 1;
        const errorRate = total > 0 ? record.stats.errorCalls / total : 0;
        buffer.points.push({
            ts: Date.now(),
            successRate,
            avgLatencyMs: record.stats.avgLatencyMs,
            errorRate,
        });
        if (buffer.points.length > TIMELINE_MAX_POINTS) {
            buffer.points.shift();
        }
    }
    _buildTimeline(providerId) {
        const buffer = this._timelines.get(providerId);
        const points = buffer ? [...buffer.points] : [];
        return {
            providerId,
            points,
            length: points.length,
        };
    }
    _rebuild() {
        this._snapshot = this._buildSnapshot();
        this._notify();
    }
    _buildSnapshot() {
        const entries = [];
        for (const [id, record] of this._healthState.providers) {
            const total = record.stats.totalCalls;
            const successRate = total > 0 ? record.stats.successCalls / total : 1;
            const errorRate = total > 0 ? record.stats.errorCalls / total : 0;
            entries.push({
                providerId: id,
                displayName: record.displayName,
                status: record.status,
                isActive: record.isActive,
                isConfigured: record.isConfigured,
                successRate,
                errorRate,
                p95LatencyMs: record.stats.maxLatencyMs,
                avgLatencyMs: record.stats.avgLatencyMs,
                totalCalls: total,
                cacheHitRate: record.cacheMetrics?.hitRate,
                cacheReadTokens: record.cacheMetrics?.cacheReadTokens,
                cacheWriteTokens: record.cacheMetrics?.cacheWriteTokens,
                lastSuccessAt: record.stats.lastSuccessAt,
                lastErrorAt: record.stats.lastErrorAt,
                lastErrorMessage: record.stats.lastErrorMessage,
                lastCheckedAt: record.lastCheckedAt,
                rateLimitResetAt: record.rateLimitResetAt,
                timeline: this._buildTimeline(id),
            });
        }
        entries.sort((a, b) => {
            const diff = (STATUS_ORDER[a.status] ?? 4) - (STATUS_ORDER[b.status] ?? 4);
            return diff !== 0 ? diff : a.displayName.localeCompare(b.displayName);
        });
        const fallbackChain = buildFallbackChainData(this._modelState, this._healthState);
        return {
            entries,
            compositeStatus: this._healthState.compositeStatus,
            degradedCount: this._healthState.degradedCount,
            unavailableCount: this._healthState.unavailableCount,
            fallbackChain,
            warnings: this._healthState.warnings,
            snapshotAt: Date.now(),
        };
    }
    _notify() {
        for (const callback of this._subscribers) {
            try {
                callback();
            }
            catch (error) {
                logger.warn('ProviderHealthDataProvider subscriber failed', {
                    error: summarizeError(error),
                });
            }
        }
    }
}
