import { logger } from "./logger";
import { executeFinalizeLogJob, type FinalizeLogJob } from "./logFinalizer";
import type { CapturedLog } from "./schemas";
import {
  getLogSessionId,
  markSessionTaskFinished,
  markSessionTaskQueued,
  markSessionTaskStarted,
} from "./sessionSupervisor";

const UNASSIGNED_SESSION_QUEUE = "__unassigned__";

export type SessionTaskName = "finalize-log";

type SessionTask<T> = () => Promise<T> | T;

const queues = new Map<string, Promise<void>>();

function queueKeyForSession(sessionId: string | null): string {
  return sessionId ?? UNASSIGNED_SESSION_QUEUE;
}

function errorMessage(err: unknown): string {
  return err instanceof Error ? err.message : String(err);
}

async function runSessionTask<T>(
  sessionId: string | null,
  taskName: SessionTaskName,
  task: SessionTask<T>,
): Promise<T> {
  markSessionTaskStarted(sessionId);
  try {
    const result = await task();
    markSessionTaskFinished(sessionId, null);
    return result;
  } catch (err) {
    const message = errorMessage(err);
    logger.error(`[sessionRuntime] ${taskName} failed for session ${sessionId ?? "unassigned"}`);
    markSessionTaskFinished(sessionId, message);
    return Promise.reject(err);
  }
}

export function enqueueSessionTask<T>(
  sessionId: string | null,
  taskName: SessionTaskName,
  task: SessionTask<T>,
): Promise<T> {
  const queueKey = queueKeyForSession(sessionId);
  const previous = queues.get(queueKey) ?? Promise.resolve();

  markSessionTaskQueued(sessionId);

  const current = previous
    .catch(() => undefined)
    .then(() => runSessionTask(sessionId, taskName, task));
  const settled = current.then(
    () => undefined,
    () => undefined,
  );

  queues.set(queueKey, settled);
  void settled.then(() => {
    if (queues.get(queueKey) === settled) {
      queues.delete(queueKey);
    }
  });

  return current;
}

export function enqueueLogTask<T>(
  log: CapturedLog,
  taskName: SessionTaskName,
  task: SessionTask<T>,
): Promise<T> {
  return enqueueSessionTask(getLogSessionId(log), taskName, task);
}

export function enqueueFinalizeLogJob(job: FinalizeLogJob): Promise<void> {
  return enqueueSessionTask(getLogSessionId(job.log), "finalize-log", () =>
    executeFinalizeLogJob(job),
  );
}
