// uo-link sidecar connection config store. Mirrors botConfig/emailConfig: the DB
// layer only ever sees ciphertext, and only getWithToken() (used server-side to
// call the sidecar over REST/WS) decrypts it. The admin-facing getSafe() never
// includes the token — it exposes only `hasToken`. A blank `token` on save means
// "leave the existing token unchanged" (same convention as the other configs).

const db = require('./uoLinkConfig.db')
const secretBox = require('../../utils/secretBox')

const DEFAULT_PROTOCOL = Number(process.env.UOLINK_PROTOCOL) || 1

function toSafe(row) {
  if (!row) {
    return {
      baseUrl: process.env.UOLINK_BASE_URL || null,
      wsUrl: process.env.UOLINK_WS_URL || null,
      protocol: DEFAULT_PROTOCOL,
      enabled: false,
      hasToken: false,
      status: 'disconnected',
      statusDetail: null,
      pluginConnected: false,
      lastEventAt: null,
      bootId: null,
    }
  }
  return {
    baseUrl: row.base_url || null,
    wsUrl: row.ws_url || null,
    protocol: row.protocol || DEFAULT_PROTOCOL,
    enabled: Boolean(row.enabled),
    hasToken: Boolean(row.auth_token_enc),
    status: row.status || 'disconnected',
    statusDetail: row.status_detail || null,
    pluginConnected: Boolean(row.plugin_connected),
    lastEventAt: row.last_event_at || null,
    bootId: row.boot_id || null,
  }
}

async function getSafe() {
  return toSafe(await db.get())
}

// Decrypted token included — server-side only (calling the sidecar's REST/WS
// API). Returns null when nothing has been saved yet.
async function getWithToken() {
  const row = await db.get()
  if (!row) return null
  return { ...toSafe(row), token: row.auth_token_enc ? secretBox.decrypt(row.auth_token_enc) : null }
}

// Save admin-supplied config. `token` undefined or '' means "leave the existing
// token unchanged" (same convention as botConfig.save).
async function save({ baseUrl, wsUrl, token, protocol, enabled, updatedBy }) {
  const fields = {}
  if (baseUrl !== undefined) fields.base_url = baseUrl
  if (wsUrl !== undefined) fields.ws_url = wsUrl
  if (token) fields.auth_token_enc = secretBox.encrypt(token)
  if (protocol !== undefined) fields.protocol = protocol
  if (enabled !== undefined) fields.enabled = enabled ? 1 : 0
  if (updatedBy !== undefined) fields.updated_by = updatedBy
  const row = await db.upsert(fields)
  return toSafe(row)
}

// Mirror the sidecar's last-reported connection state into the DB so the admin
// panel has something to show between polls and the public status endpoint can
// read it without a live round-trip.
async function recordStatus({ status, statusDetail, pluginConnected, lastEventAt, bootId }) {
  const fields = {}
  if (status !== undefined) fields.status = status
  if (statusDetail !== undefined) fields.status_detail = statusDetail
  if (pluginConnected !== undefined) fields.plugin_connected = pluginConnected ? 1 : 0
  // lastEventAt may arrive as an ISO string (e.g. "2026-07-10T22:08:27Z"); the
  // mariadb DATETIME parser rejects the "T"/"Z", so hand it a real Date (same
  // fix as botConfig.recordStatus's last_connected_at).
  if (lastEventAt !== undefined) fields.last_event_at = lastEventAt ? new Date(lastEventAt) : null
  if (bootId !== undefined) fields.boot_id = bootId
  if (Object.keys(fields).length === 0) return getSafe()
  const row = await db.upsert(fields)
  return toSafe(row)
}

module.exports = { getSafe, getWithToken, save, recordStatus }
