"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InMemoryRuntimeAdapter = void 0;
const nanoid_1 = require("nanoid");
const http_1 = require("../utils/http");
class InMemoryRuntimeAdapter {
    runs = [];
    tasks = [];
    traces = [];
    idempotencyCache = new Map();
    now() {
        return new Date().toISOString();
    }
    cacheKey(action, key) {
        if (!key || !key.trim())
            return null;
        return `${action}:${key.trim()}`;
    }
    readIdempotency(action, idempotencyKey) {
        const cache = this.cacheKey(action, idempotencyKey);
        if (!cache)
            return null;
        return this.idempotencyCache.get(cache) ?? null;
    }
    writeIdempotency(action, idempotencyKey, value) {
        const cache = this.cacheKey(action, idempotencyKey);
        if (!cache)
            return;
        this.idempotencyCache.set(cache, value);
    }
    pushEvent(input) {
        this.traces.push({
            timestamp: this.now(),
            level: input.level ?? "info",
            service: "llm_normalizer_backend",
            sessionId: input.sessionId,
            runId: input.runId,
            taskId: input.taskId ?? null,
            eventType: input.eventType,
            payload: input.payload
        });
    }
    startRun(input) {
        const cached = this.readIdempotency("startRun", input.idempotencyKey);
        if (cached)
            return cached;
        const record = {
            sessionId: input.sessionId ?? `session_${(0, nanoid_1.nanoid)(8)}`,
            runId: `run_${(0, nanoid_1.nanoid)(10)}`,
            status: "RUNNING",
            initiator: input.initiator ?? "operator",
            source: input.source ?? "gui",
            createdAt: this.now(),
            updatedAt: this.now(),
            metadata: input.metadata ?? {}
        };
        this.runs.unshift(record);
        this.pushEvent({
            runId: record.runId,
            sessionId: record.sessionId,
            eventType: "RUN_STARTED",
            payload: record.metadata
        });
        this.writeIdempotency("startRun", input.idempotencyKey, record);
        return record;
    }
    finishRun(input) {
        const cached = this.readIdempotency("finishRun", input.idempotencyKey);
        if (cached)
            return cached;
        const record = this.runs.find((item) => item.runId === input.runId);
        if (!record) {
            throw new http_1.ApiError("RUN_NOT_FOUND", `Run not found: ${input.runId}`, 404);
        }
        record.status = input.status;
        record.updatedAt = this.now();
        record.source = input.source ?? record.source;
        record.metadata = {
            ...(record.metadata ?? {}),
            ...(input.metadata ?? {}),
            reason: input.reason ?? null
        };
        this.pushEvent({
            runId: record.runId,
            sessionId: record.sessionId,
            eventType: `RUN_FINISHED_${input.status}`,
            level: input.status === "ERROR" ? "error" : "info",
            payload: { reason: input.reason ?? null }
        });
        this.writeIdempotency("finishRun", input.idempotencyKey, record);
        return record;
    }
    listRuns() {
        return [...this.runs];
    }
    getRun(runId) {
        return this.runs.find((item) => item.runId === runId) ?? null;
    }
    enqueueTask(input) {
        const cached = this.readIdempotency("enqueueTask", input.idempotencyKey);
        if (cached)
            return cached;
        const run = this.getRun(input.runId);
        if (!run) {
            throw new http_1.ApiError("RUN_NOT_FOUND", `Run not found: ${input.runId}`, 404);
        }
        const task = {
            taskId: `task_${(0, nanoid_1.nanoid)(10)}`,
            runId: run.runId,
            status: "QUEUED",
            payload: input.payload,
            source: input.source ?? "gui",
            createdAt: this.now(),
            updatedAt: this.now()
        };
        this.tasks.unshift(task);
        this.pushEvent({
            runId: run.runId,
            sessionId: run.sessionId,
            taskId: task.taskId,
            eventType: "TASK_ENQUEUED",
            payload: task.payload
        });
        this.writeIdempotency("enqueueTask", input.idempotencyKey, task);
        return task;
    }
    claimTask() {
        const task = this.tasks.find((item) => item.status === "QUEUED");
        if (!task) {
            return null;
        }
        task.status = "RUNNING";
        task.updatedAt = this.now();
        const run = this.getRun(task.runId);
        if (run) {
            this.pushEvent({
                runId: run.runId,
                sessionId: run.sessionId,
                taskId: task.taskId,
                eventType: "TASK_CLAIMED"
            });
        }
        return task;
    }
    completeTask(input) {
        const cached = this.readIdempotency("completeTask", input.idempotencyKey);
        if (cached)
            return cached;
        const task = this.tasks.find((item) => item.taskId === input.taskId);
        if (!task) {
            throw new http_1.ApiError("TASK_NOT_FOUND", `Task not found: ${input.taskId}`, 404);
        }
        task.status = "DONE";
        task.updatedAt = this.now();
        task.result = input.result;
        task.source = input.source ?? task.source;
        const run = this.getRun(task.runId);
        if (run) {
            this.pushEvent({
                runId: run.runId,
                sessionId: run.sessionId,
                taskId: task.taskId,
                eventType: "TASK_DONE",
                payload: input.result
            });
        }
        this.writeIdempotency("completeTask", input.idempotencyKey, task);
        return task;
    }
    failTask(input) {
        const cached = this.readIdempotency("failTask", input.idempotencyKey);
        if (cached)
            return cached;
        const task = this.tasks.find((item) => item.taskId === input.taskId);
        if (!task) {
            throw new http_1.ApiError("TASK_NOT_FOUND", `Task not found: ${input.taskId}`, 404);
        }
        task.status = "ERROR";
        task.updatedAt = this.now();
        task.error = input.error;
        task.source = input.source ?? task.source;
        const run = this.getRun(task.runId);
        if (run) {
            this.pushEvent({
                runId: run.runId,
                sessionId: run.sessionId,
                taskId: task.taskId,
                eventType: "TASK_ERROR",
                level: "error",
                payload: {
                    errorCode: input.error.code,
                    errorMessage: input.error.message
                }
            });
        }
        this.writeIdempotency("failTask", input.idempotencyKey, task);
        return task;
    }
    getResults() {
        return this.tasks.filter((item) => item.status === "DONE" || item.status === "ERROR");
    }
    getRunTrace(runId) {
        return this.traces.filter((item) => item.runId === runId);
    }
    health() {
        return {
            ok: true,
            queueDepth: this.tasks.filter((item) => item.status === "QUEUED").length,
            runsTotal: this.runs.length
        };
    }
}
exports.InMemoryRuntimeAdapter = InMemoryRuntimeAdapter;
