/**
 * db/ssh-install.js — SSH install credential persistence.
 *
 * Owns: ssh_install_credentials rows (one per connection, upserted on each install attempt).
 *       Audit trail of install status, logs, timestamps.
 * Does NOT own: oracle_connections CRUD (db/agent.js), agent tunnels (db/agent.js),
 *               SSH target vault (db/ssh-targets.js).
 */

'use strict';

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

/**
 * Upsert SSH install credential record.
 * Called when the user submits the /connections/new form.
 * Idempotent — re-running overwrites to allow retry with updated creds.
 */
async function upsertSshInstallCred({
  connectionId,
  userId,
  sshHost,
  sshPort,
  sshUser,
  authMethod,
  encryptedCredential,
  useSudo,
}) {
  await pool.query(
    `MERGE INTO ssh_install_credentials t
     USING (SELECT :p1 AS connection_id, :p2 AS user_id, :p3 AS ssh_host, :p4 AS ssh_port,
                   :p5 AS ssh_user, :p6 AS auth_method, :p7 AS encrypted_credential, :p8 AS use_sudo FROM DUAL) s
        ON (t.connection_id = s.connection_id)
     WHEN MATCHED THEN UPDATE SET
       user_id              = s.user_id,
       ssh_host             = s.ssh_host,
       ssh_port             = s.ssh_port,
       ssh_user             = s.ssh_user,
       auth_method          = s.auth_method,
       encrypted_credential = s.encrypted_credential,
       use_sudo             = s.use_sudo,
       install_status       = 'pending',
       install_log          = NULL,
       error_message        = NULL,
       installed_at         = NULL,
       updated_at           = CURRENT_TIMESTAMP
     WHEN NOT MATCHED THEN
       INSERT (connection_id, user_id, ssh_host, ssh_port, ssh_user, auth_method, encrypted_credential,
               use_sudo, install_status, created_at, updated_at)
       VALUES (s.connection_id, s.user_id, s.ssh_host, s.ssh_port, s.ssh_user, s.auth_method,
               s.encrypted_credential, s.use_sudo, 'pending', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
    { p1: connectionId, p2: userId, p3: sshHost, p4: sshPort || 22, p5: sshUser,
      p6: authMethod, p7: encryptedCredential, p8: pool.bool(useSudo !== false) }
  );
  const result = await pool.query(
    `SELECT id, connection_id FROM ssh_install_credentials WHERE connection_id = :p1`, { p1: connectionId });
  return result.rows[0];
}

/**
 * Mark install as running — prevents concurrent retry while one is in flight.
 */
async function markRunning(connectionId) {
  await pool.query(
    `UPDATE ssh_install_credentials
     SET install_status = 'running', updated_at = CURRENT_TIMESTAMP
     WHERE connection_id = :p1`,
    { p1: connectionId }
  );
}

/**
 * Append a chunk to the install log (truncated at 32 KB total).
 * Called repeatedly as install.sh streams output.
 */
async function appendLog(connectionId, chunk) {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    const { rows } = await client.query(
      `SELECT install_log FROM ssh_install_credentials WHERE connection_id = :p1 FOR UPDATE`, { p1: connectionId });
    if (!rows.length) { await client.query('ROLLBACK'); return; }
    const combined = ((rows[0].install_log || '') + (chunk || '')).slice(0, 32768);
    await client.query(
      `UPDATE ssh_install_credentials SET install_log = :p2, updated_at = CURRENT_TIMESTAMP WHERE connection_id = :p1`,
      { p1: connectionId, p2: pool.clob(combined) });
    await client.query('COMMIT');
  } catch (err) {
    await client.query('ROLLBACK').catch(() => {});
    throw err;
  } finally {
    client.release();
  }
}

/**
 * Mark install as succeeded.
 */
async function markSuccess(connectionId) {
  await pool.query(
    `UPDATE ssh_install_credentials
     SET install_status = 'success',
         installed_at   = CURRENT_TIMESTAMP,
         updated_at     = CURRENT_TIMESTAMP
     WHERE connection_id = :p1`,
    { p1: connectionId }
  );
}

/**
 * Mark install as failed with an error message.
 */
async function markFailed(connectionId, errorMessage) {
  await pool.query(
    `UPDATE ssh_install_credentials
     SET install_status  = 'failed',
         error_message   = :p2,
         updated_at      = CURRENT_TIMESTAMP
     WHERE connection_id = :p1`,
    { p1: connectionId, p2: (errorMessage || 'Unknown error').slice(0, 1024) }
  );
}

/**
 * Fetch the install credential row for a connection.
 * Returns encrypted_credential — caller decrypts.
 */
async function getByConnectionId(connectionId) {
  const result = await pool.query(
    `SELECT id, connection_id, user_id, ssh_host, ssh_port, ssh_user,
            auth_method, encrypted_credential, use_sudo, install_status
     FROM ssh_install_credentials WHERE connection_id = :p1`,
    { p1: connectionId }
  );
  return result.rows[0] || null;
}

module.exports = {
  upsertSshInstallCred,
  markRunning,
  appendLog,
  markSuccess,
  markFailed,
  getByConnectionId,
};
