// Point the DB at a closed port BEFORE requiring the modules (they build the pool).
// The uoLinkConfig model is monkeypatched so no query runs.
process.env.DB_HOST = '127.0.0.1'
process.env.DB_PORT = '59999'

const { test, after, afterEach } = require('node:test')
const assert = require('node:assert/strict')

// The uo-link REST client's headline contract (see its module header and
// CLAUDE.md): it NEVER throws — every call resolves to { ok, data, status, error }
// so a public page or an admin poll degrades to "shard unavailable" instead of
// 500ing. The regression these tests lock down: resolveConfig() decrypts the
// stored auth token, and secretBox.decrypt THROWS when the ciphertext can't be
// authenticated (SECRET_ENC_KEY rotated, or a DB dump restored under a different
// key). It used to run OUTSIDE call()'s try, so that throw escaped the client and
// 500'd every live-shard route.
const uoLinkClient = require('../src/utils/uoLinkClient')
const uoLinkConfig = require('../src/model/uoLinkConfig/uoLinkConfig.model')
const db = require('../src/utils/db')

after(() => db.close())

const origGetWithToken = uoLinkConfig.getWithToken
afterEach(() => {
  uoLinkConfig.getWithToken = origGetWithToken
  uoLinkClient.invalidateConfig() // drop the 5s config cache between cases
})

test('an undecryptable stored token resolves to { ok: false } instead of throwing', async () => {
  uoLinkConfig.getWithToken = async () => {
    // Exactly what crypto's Decipheriv.final() raises on a bad key / tampered blob.
    throw new Error('Unsupported state or unable to authenticate data')
  }
  uoLinkClient.invalidateConfig()

  const result = await uoLinkClient.health()

  assert.equal(result.ok, false, 'must report failure, not throw')
  assert.equal(result.status, 0)
  assert.match(result.error, /unreadable/i, 'distinguishes config failure from a dead sidecar')
})

test('every read helper stays on the { ok:false } contract when config is unreadable', async () => {
  uoLinkConfig.getWithToken = async () => {
    throw new Error('Unsupported state or unable to authenticate data')
  }
  uoLinkClient.invalidateConfig()

  // The routes that regressed: character sheet, roster and vendor lookups, which
  // are reachable from both /admin/shard/* and the player-facing /player/shard/*.
  for (const call of [
    () => uoLinkClient.getCharBySerial('0x1'),
    () => uoLinkClient.getRoster('someacct'),
    () => uoLinkClient.getVendors('someacct'),
  ]) {
    const result = await call()
    assert.equal(result.ok, false)
    assert.equal(result.status, 0)
  }
})

test('a missing/blank config still reports "not configured" (unchanged behaviour)', async () => {
  uoLinkConfig.getWithToken = async () => null
  uoLinkClient.invalidateConfig()

  const result = await uoLinkClient.health()

  assert.equal(result.ok, false)
  assert.equal(result.status, 0)
  assert.match(result.error, /not configured/i)
})
