import { EventEmitter } from "node:events";
import { resolve } from "node:path";
import { AGENT_PROVIDER_IDS } from "./provider-manifest.js";
const DEFAULT_CWD_KEY = "__default__";
export class ProviderSnapshotManager {
    constructor(providerRegistry, logger) {
        this.providerRegistry = providerRegistry;
        this.logger = logger;
        this.snapshots = new Map();
        this.warmUps = new Map();
        this.events = new EventEmitter();
        this.destroyed = false;
    }
    getSnapshot(cwd) {
        const cwdKey = normalizeCwdKey(cwd);
        const entries = this.snapshots.get(cwdKey);
        if (!entries) {
            const loadingEntries = this.resetSnapshotToLoading(cwdKey);
            void this.warmUp(cwd);
            return entriesToArray(loadingEntries);
        }
        return entriesToArray(entries);
    }
    refresh(cwd) {
        const cwdKey = normalizeCwdKey(cwd);
        if (this.warmUps.has(cwdKey)) {
            return;
        }
        this.resetSnapshotToLoading(cwdKey);
        this.emitChange(cwdKey);
        void this.warmUp(cwd);
    }
    on(event, listener) {
        this.events.on(event, listener);
        return this;
    }
    off(event, listener) {
        this.events.off(event, listener);
        return this;
    }
    destroy() {
        this.destroyed = true;
        this.events.removeAllListeners();
        this.snapshots.clear();
        this.warmUps.clear();
    }
    createLoadingEntries() {
        const entries = new Map();
        for (const provider of this.getProviderIds()) {
            entries.set(provider, {
                provider,
                status: "loading",
            });
        }
        return entries;
    }
    async warmUp(cwd) {
        const cwdKey = normalizeCwdKey(cwd);
        const inFlight = this.warmUps.get(cwdKey);
        if (inFlight) {
            return inFlight;
        }
        const warmUpPromise = Promise.allSettled(this.getProviderIds().map((provider) => this.refreshProvider(cwdKey, provider, cwd))).then(() => undefined);
        this.warmUps.set(cwdKey, warmUpPromise);
        try {
            await warmUpPromise;
        }
        finally {
            if (this.warmUps.get(cwdKey) === warmUpPromise) {
                this.warmUps.delete(cwdKey);
            }
        }
    }
    async refreshProvider(cwdKey, provider, cwd) {
        const definition = this.providerRegistry[provider];
        if (!definition) {
            return;
        }
        const snapshot = this.getOrCreateSnapshot(cwdKey);
        snapshot.set(provider, {
            provider,
            status: "loading",
        });
        try {
            const client = definition.createClient(this.logger);
            const available = await client.isAvailable();
            if (!available) {
                snapshot.set(provider, {
                    provider,
                    status: "unavailable",
                });
                this.emitChange(cwdKey);
                return;
            }
            const [models, modes] = await Promise.all([
                definition.fetchModels({ cwd }),
                definition.fetchModes({ cwd }),
            ]);
            snapshot.set(provider, {
                provider,
                status: "ready",
                models,
                modes,
                fetchedAt: new Date().toISOString(),
            });
            this.emitChange(cwdKey);
        }
        catch (error) {
            snapshot.set(provider, {
                provider,
                status: "error",
                error: toErrorMessage(error),
            });
            this.logger.warn({ err: error, provider, cwd: cwdKey }, "Failed to refresh provider snapshot");
            this.emitChange(cwdKey);
        }
    }
    emitChange(cwdKey) {
        if (this.destroyed) {
            return;
        }
        const snapshot = this.snapshots.get(cwdKey);
        if (!snapshot) {
            return;
        }
        this.events.emit("change", entriesToArray(snapshot), denormalizeCwdKey(cwdKey));
    }
    getOrCreateSnapshot(cwdKey) {
        const existing = this.snapshots.get(cwdKey);
        if (existing) {
            return existing;
        }
        const created = this.createLoadingEntries();
        this.snapshots.set(cwdKey, created);
        return created;
    }
    resetSnapshotToLoading(cwdKey) {
        const snapshot = this.getOrCreateSnapshot(cwdKey);
        snapshot.clear();
        for (const [provider, entry] of this.createLoadingEntries()) {
            snapshot.set(provider, entry);
        }
        return snapshot;
    }
    getProviderIds() {
        return AGENT_PROVIDER_IDS.filter((provider) => this.providerRegistry[provider]);
    }
}
function normalizeCwdKey(cwd) {
    if (!cwd) {
        return DEFAULT_CWD_KEY;
    }
    const trimmed = cwd.trim();
    if (!trimmed) {
        return DEFAULT_CWD_KEY;
    }
    return resolve(trimmed);
}
function denormalizeCwdKey(cwdKey) {
    return cwdKey === DEFAULT_CWD_KEY ? undefined : cwdKey;
}
function entriesToArray(entries) {
    return Array.from(entries.values(), cloneEntry);
}
function cloneEntry(entry) {
    return {
        ...entry,
        models: entry.models?.map((model) => ({ ...model })),
        modes: entry.modes?.map((mode) => ({ ...mode })),
    };
}
function toErrorMessage(error) {
    if (error instanceof Error && error.message) {
        return error.message;
    }
    if (typeof error === "string" && error) {
        return error;
    }
    return "Unknown error";
}
//# sourceMappingURL=provider-snapshot-manager.js.map