import { prisma } from "../lib/db";
import { createLogger } from "../lib/logger";
import { runDeploy, stopPreview, signalAbort } from "../services/deploy";

const log = createLogger("JOB_WORKER");
let running = false;

export async function startJobWorker() {
  if (running) return;
  running = true;
  log.info("Job worker started");

  await resetStuckJobs();
  pollLoop();
}

async function resetStuckJobs() {
  const count = await prisma.job.updateMany({
    where: { status: "RUNNING" },
    data: { status: "PENDING", startedAt: null },
  });
  if (count.count > 0) log.info({ count: count.count }, "Reset stuck running jobs to PENDING");
}

async function pollLoop() {
  while (running) {
    try {
      await processPendingJobs();
    } catch (e) {
      log.error({ e }, "Job worker poll error");
    }
    await sleep(1000);
  }
}

const activePreviewJobs = new Map<number, number>();

async function processPendingJobs() {
  const pending = await prisma.job.findMany({
    where: { status: "PENDING" },
    orderBy: { createdAt: "asc" },
    take: 20,
  });

  for (const job of pending) {
    const previewId = job.previewId;
    if (!previewId) continue;

    if (activePreviewJobs.has(previewId)) {
      const existingJobId = activePreviewJobs.get(previewId)!;

      if (job.type === "DEPLOY") {
        log.info({ previewId, newJobId: job.id, abortingJobId: existingJobId }, "New deploy cancels existing");
        signalAbort(previewId);
        await sleep(500);
      } else {
        continue;
      }
    }

    activePreviewJobs.set(previewId, job.id);

    processJob(job).finally(() => {
      if (activePreviewJobs.get(previewId) === job.id) {
        activePreviewJobs.delete(previewId);
      }
    });
  }
}

async function processJob(job: { id: number; type: string; previewId: number | null; payload: any }) {
  log.info({ jobId: job.id, type: job.type, previewId: job.previewId }, "Processing job");

  try {
    if (job.type === "DEPLOY") {
      await runDeploy(job.id);
    } else if (job.type === "STOP" || job.type === "INACTIVITY_STOP") {
      if (job.previewId) {
        const payload = job.payload && typeof job.payload === "object" ? job.payload : {};
        const reason = typeof payload.reason === "string" && payload.reason.trim()
          ? payload.reason.trim()
          : job.type === "INACTIVITY_STOP"
            ? "Inactivity timeout"
            : "Stopped";
        await prisma.job.update({ where: { id: job.id }, data: { status: "RUNNING", startedAt: new Date() } });
        await stopPreview(job.previewId, "STOPPED", reason);
        await prisma.job.update({ where: { id: job.id }, data: { status: "DONE", finishedAt: new Date() } });
      }
    }
  } catch (e: any) {
    log.error({ e, jobId: job.id }, "Job processing error");
    await prisma.job.update({
      where: { id: job.id },
      data: { status: "FAILED", error: e.message, finishedAt: new Date() },
    }).catch(() => {});
  }
}

export function stopJobWorker() {
  running = false;
}

function sleep(ms: number) {
  return new Promise(r => setTimeout(r, ms));
}
