import { initDatabase } from "@aigne/sqlite";
import { SpanStatusCode } from "@opentelemetry/api";
import { ExportResultCode } from "@opentelemetry/core";
import fastq from "fastq";
import { insertTrace, isBlocklet, updateStatusByIds, updateTrace } from "../../core/util.js";
import { migrate } from "../../server/migrate.js";
import getAIGNEHomePath from "../../server/utils/image-home-path.js";
import saveFiles from "../../server/utils/save-files.js";
import { validateTraceSpans } from "./util.js";
class HttpExporter {
    dbPath;
    _db;
    upsert;
    _update;
    updateQueue;
    async getDb() {
        if (isBlocklet)
            return;
        const db = await initDatabase({ url: this.dbPath, wal: true });
        await migrate(db);
        return db;
    }
    constructor({ dbPath, exportFn, updateFn, }) {
        this.dbPath = dbPath;
        this._db ??= this.getDb();
        this.upsert = exportFn ?? (isBlocklet ? async () => { } : this._upsertWithSQLite);
        this._update = updateFn ?? (isBlocklet ? async () => { } : this._updateWithSQLite);
        this.updateQueue = fastq.promise(this.updateWorker.bind(this), 1);
    }
    async updateWorker(task) {
        await this._update(task.id, task.data);
    }
    update = (id, data) => {
        return this.updateQueue.push({ id, data });
    };
    async _upsertWithSQLite(validatedData) {
        const db = await this._db;
        if (!db)
            throw new Error("Database not initialized");
        for (const trace of validatedData) {
            const dataDir = getAIGNEHomePath();
            for (const key of ["files", "images"]) {
                const items = trace.attributes?.output?.[key];
                if (trace?.attributes?.output?.[key] && items?.length) {
                    trace.attributes.output[key] = await saveFiles(items, { dataDir });
                }
            }
            await insertTrace(db, trace);
        }
    }
    async _updateWithSQLite(id, data) {
        const db = await this._db;
        if (!db)
            throw new Error("Database not initialized");
        await updateTrace(db, id, data);
    }
    async export(spans, resultCallback) {
        try {
            const data = validateTraceSpans(spans);
            await this.upsert(data);
            resultCallback({ code: ExportResultCode.SUCCESS });
        }
        catch (error) {
            console.warn("[Observability] Failed to export spans:", error.cause?.message || error.message);
            resultCallback({ code: ExportResultCode.FAILED });
        }
    }
    async shutdown(contextIds = []) {
        await this.updateQueue.drained();
        if (this._db) {
            const db = await this._db;
            await updateStatusByIds(db, contextIds, { code: SpanStatusCode.ERROR, message: "Exited" });
        }
    }
}
export default HttpExporter;
