import crypto from 'node:crypto';

const jobs = new Map();
const locks = new Map();
const maxJobs = 100;

export function createJob(type, vmName) {
  if (locks.has(vmName)) {
    const activeJob = jobs.get(locks.get(vmName));
    const error = new Error(`A ${activeJob?.type || 'job'} job is already active for ${vmName}.`);
    error.status = 409;
    throw error;
  }

  const id = `job_${crypto.randomBytes(8).toString('hex')}`;
  const now = new Date().toISOString();
  const job = {
    id,
    type,
    vmName,
    status: 'queued',
    startedAt: now,
    finishedAt: null,
    currentStep: 'Queued',
    progress: {
      percent: 0,
      detail: 'Queued',
      currentBytes: 0,
      totalBytes: null,
    },
    logs: [],
    error: null,
  };

  jobs.set(id, job);
  locks.set(vmName, id);
  trimJobs();
  return job;
}

export function getJob(id) {
  return jobs.get(id) || null;
}

export function listJobs() {
  return [...jobs.values()].sort((a, b) => b.startedAt.localeCompare(a.startedAt));
}

export function activeJobForVm(vmName) {
  const id = locks.get(vmName);
  return id ? getJob(id) : null;
}

export function latestJobForVm(vmName) {
  return listJobs().find((job) => job.vmName === vmName) || null;
}

export function setJobRunning(job, step) {
  job.status = 'running';
  setJobStep(job, step);
}

export function setJobStep(job, step) {
  job.currentStep = step;
  job.progress = {
    ...job.progress,
    detail: step,
  };
  appendJobLog(job, `==> ${step}`);
}

export function setJobProgress(job, progress = {}) {
  const nextPercent = progress.percent ?? job.progress?.percent ?? 0;
  job.progress = {
    ...job.progress,
    ...progress,
    percent: Math.max(0, Math.min(100, Number(nextPercent) || 0)),
  };
}

export function appendJobLog(job, line) {
  if (!line) return;
  job.logs.push(...String(line).split('\n').filter(Boolean));
  if (job.logs.length > 1000) {
    job.logs = job.logs.slice(-1000);
  }
}

export function finishJob(job, status, error = null) {
  job.status = status;
  job.finishedAt = new Date().toISOString();
  job.currentStep = status === 'success' ? 'Completed' : 'Failed';
  job.progress = {
    ...job.progress,
    percent: status === 'success' ? 100 : job.progress?.percent || 0,
    detail: status === 'success' ? 'Completed' : 'Failed',
  };
  job.error = error ? String(error.message || error) : null;
  locks.delete(job.vmName);
}

function trimJobs() {
  const allJobs = listJobs();
  for (const job of allJobs.slice(maxJobs)) {
    if (job.status !== 'running' && job.status !== 'queued') {
      jobs.delete(job.id);
    }
  }
}
