'use strict';

const pool = require('./index');

async function createJob(connectionId, opKey, userId) {
  const ins = await pool.query(
    `INSERT INTO ebs_jobs (connection_id, op_key, created_by)
     VALUES (:p1, :p2, :p3) RETURNING id INTO :out_id`,
    { p1: connectionId, p2: opKey, p3: userId, out_id: pool.outNumber() }
  );
  return { id: ins.outBinds.out_id[0] };
}

async function startJob(jobId) {
  await pool.query(
    `UPDATE ebs_jobs SET status = 'running', started_at = CURRENT_TIMESTAMP
     WHERE id = :p1 AND status = 'queued'`,
    { p1: jobId }
  );
}

async function completeJob(jobId, { ok, stdout, exit_code, duration_ms }) {
  const { rowCount } = await pool.query(
    `UPDATE ebs_jobs
     SET status = 'done', ok = :p2, stdout = :p3, exit_code = :p4,
         duration_ms = :p5, finished_at = CURRENT_TIMESTAMP
     WHERE id = :p1`,
    { p1: jobId, p2: pool.bool(ok), p3: pool.clob(stdout),
      p4: pool.num(exit_code), p5: pool.num(duration_ms) }
  );
  return rowCount || 0;
}

async function getJob(jobId) {
  const { rows } = await pool.query(
    `SELECT j.*, created_by AS owner_id FROM ebs_jobs j WHERE id = :p1`,
    { p1: jobId }
  );
  return rows[0] || null;
}

async function timeoutStaleJobs() {
  const { rowCount } = await pool.query(
    `UPDATE ebs_jobs SET status = 'timeout', finished_at = CURRENT_TIMESTAMP
     WHERE status IN ('queued','running')
       AND created_at < CURRENT_TIMESTAMP - INTERVAL '75' MINUTE`
  );
  return rowCount || 0;
}

async function getRunningJobs() {
  const { rows } = await pool.query(
    `SELECT id, connection_id FROM ebs_jobs WHERE status = 'running'`
  );
  return rows;
}

async function updateJobLogTail(jobId, logTail) {
  await pool.query(
    `UPDATE ebs_jobs SET log_tail = :p2 WHERE id = :p1`,
    { p1: jobId, p2: pool.clob(logTail) }
  );
}

async function cancelJob(jobId, logTail) {
  await pool.query(
    `UPDATE ebs_jobs SET status = 'cancelled', finished_at = CURRENT_TIMESTAMP,
            log_tail = COALESCE(:p2, log_tail)
     WHERE id = :p1 AND status IN ('queued', 'running')`,
    { p1: jobId, p2: pool.clob(logTail || null) }
  );
}

async function getActiveJobForConnection(connectionId) {
  // Selects enough to REBUILD a UI, not just enough to block a mutex. The
  // caller that reconnects after a page load needs the op, when it started, who
  // owns it, and the log so far.
  const { rows } = await pool.query(
    `SELECT id, op_key, status, started_at, created_at, log_tail,
            created_by AS owner_id, connection_id
       FROM ebs_jobs
      WHERE connection_id = :p1 AND status IN ('queued', 'running')
      ORDER BY id DESC
      FETCH FIRST 1 ROWS ONLY`,
    { p1: connectionId }
  );
  return rows[0] || null;
}

module.exports = { createJob, startJob, completeJob, getJob, timeoutStaleJobs, getRunningJobs, updateJobLogTail, cancelJob, getActiveJobForConnection };
