// jobStore.js — in-memory job tracker for product import jobs
const { v4: uuid } = require('uuid');

const jobs = {};

const MAX_LOGS = 120;
const MAX_ERRORS = 30;
const MAX_RESULTS = 100;

function nowIso() {
  return new Date().toISOString();
}

function elapsedSeconds(startedAt) {
  if (!startedAt) return 0;
  return Number(((Date.now() - new Date(startedAt).getTime()) / 1000).toFixed(1));
}

function createJob({ shop, brandId, brandName, totalSelected = 0 }) {
  const id = uuid();
  const job = {
    id,
    shop,
    brandId: String(brandId || ''),
    brandName: brandName || '',
    status: 'started',
    step: 'started',
    detail: 'Initialising import job...',
    currentProduct: null,
    liveStats: {
      total: totalSelected,
      processed: 0,
      created: 0,
      skipped: 0,
      failed: 0,
      remaining: totalSelected,
      successRate: 0,
      label: `0/${totalSelected}`,
    },
    errors: [],
    logs: [],
    results: [],
    startedAt: nowIso(),
    updatedAt: nowIso(),
    finishedAt: null,
    durationSeconds: null,
    cancelled: false,
  };
  jobs[id] = job;
  return job;
}

function updateJob(jobId, patch) {
  const job = jobs[jobId];
  if (!job) return null;
  Object.assign(job, patch, { updatedAt: nowIso() });
  // Recompute derived stats whenever liveStats is patched
  if (patch.liveStats) {
    const s = job.liveStats;
    s.remaining = Math.max(0, s.total - s.processed);
    s.successRate = s.processed > 0
      ? Number((((s.processed - s.failed) / s.processed) * 100).toFixed(1))
      : 0;
    s.label = `${s.processed}/${s.total}`;
  }
  return job;
}

function appendJobLog(jobId, line, extraPatch = {}) {
  const job = jobs[jobId];
  if (!job) return null;
  job.logs.push({ at: nowIso(), line });
  if (job.logs.length > MAX_LOGS) job.logs.shift();
  Object.assign(job, extraPatch, { updatedAt: nowIso() });
  return job;
}

function recordProductResult(jobId, result) {
  const job = jobs[jobId];
  if (!job) return null;
  job.results.push(result);
  if (job.results.length > MAX_RESULTS) job.results.shift();
  job.updatedAt = nowIso();
  return job;
}

function recordProductError(jobId, errorEntry) {
  const job = jobs[jobId];
  if (!job) return null;
  job.errors.push(errorEntry);
  if (job.errors.length > MAX_ERRORS) job.errors.shift();
  job.updatedAt = nowIso();
  return job;
}

function finishJob(jobId, status = 'done') {
  const job = jobs[jobId];
  if (!job) return null;
  const finishedAt = nowIso();
  const durationSeconds = elapsedSeconds(job.startedAt);
  const s = job.liveStats;
  Object.assign(job, {
    status,
    step: status === 'done' ? 'completed' : status,
    finishedAt,
    durationSeconds,
    updatedAt: finishedAt,
  });
  s.remaining = 0;
  s.successRate = s.processed > 0
    ? Number((((s.processed - s.failed) / s.processed) * 100).toFixed(1))
    : 0;
  return job;
}

function cancelJob(jobId) {
  const job = jobs[jobId];
  if (!job) return null;
  job.cancelled = true;
  job.status = 'cancelling';
  job.updatedAt = nowIso();
  return job;
}

function isJobCancelled(jobId) {
  return !!(jobs[jobId]?.cancelled);
}

function getJob(jobId) {
  return jobs[jobId] || null;
}

function listJobs(shop = null) {
  const all = Object.values(jobs);
  const filtered = shop ? all.filter(j => j.shop === shop) : all;
  return filtered.sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt));
}

function getLatestJobForShop(shop) {
  const shopJobs = listJobs(shop);
  return shopJobs[0] || null;
}

module.exports = {
  createJob,
  updateJob,
  appendJobLog,
  recordProductResult,
  recordProductError,
  finishJob,
  cancelJob,
  isJobCancelled,
  getJob,
  listJobs,
  getLatestJobForShop,
};
