// Staff-location visibility on GET /public/shard/online. The endpoint is
// token-free, so it inspects the caller's session (getUserFromRequest) and only
// includes each staff member's in-game location (map/x/y/z) for admins and
// moderators. Players and the public still see who is online, but not where.
//
// Point the DB at a closed port BEFORE requiring anything that builds the pool,
// so any stray query fails fast instead of hanging. The model + auth are stubbed,
// so the DB is never actually hit.
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')

const ctrl = require('../src/router/v1/public/shard.controller')
const shardState = require('../src/model/shardState/shardState.model')
const auth = require('../src/utils/auth')
const db = require('../src/utils/db')

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

function mockRes() {
  return {
    statusCode: 200,
    body: null,
    status(c) {
      this.statusCode = c
      return this
    },
    json(b) {
      this.body = b
      return this
    },
  }
}

// One online staff member with a location the model would return.
const ONLINE_ROW = { serial: '0x1', name: 'Lady Mod', map: 'Felucca', x: 1495, y: 1628, z: 10 }

const originals = {
  listOnlineLinked: shardState.listOnlineLinked,
  getUserFromRequest: auth.getUserFromRequest,
}
afterEach(() => {
  shardState.listOnlineLinked = originals.listOnlineLinked
  auth.getUserFromRequest = originals.getUserFromRequest
})

// Stub the model to return the staff member, and the session to the given viewer.
function setup(viewer) {
  shardState.listOnlineLinked = async () => [ONLINE_ROW]
  auth.getUserFromRequest = () => viewer
}

const LOCATION_KEYS = ['map', 'x', 'y', 'z']

for (const role of ['admin', 'moderator']) {
  test(`getOnline includes location for a ${role}`, async () => {
    setup({ id: 1, username: 'staff', role })
    const res = mockRes()
    await ctrl.getOnline({}, res)
    assert.equal(res.statusCode, 200)
    assert.equal(res.body.length, 1)
    const entry = res.body[0]
    assert.equal(entry.name, 'Lady Mod')
    assert.equal(entry.serial, '0x1')
    assert.equal(entry.map, 'Felucca')
    assert.equal(entry.x, 1495)
    assert.equal(entry.y, 1628)
    assert.equal(entry.z, 10)
  })
}

test('getOnline omits location for a logged-in player', async () => {
  setup({ id: 2, username: 'joe', role: 'player' })
  const res = mockRes()
  await ctrl.getOnline({}, res)
  assert.equal(res.statusCode, 200)
  const entry = res.body[0]
  // Still shows they are online…
  assert.equal(entry.name, 'Lady Mod')
  assert.equal(entry.serial, '0x1')
  // …but the location fields are absent entirely (not null/placeholder).
  for (const k of LOCATION_KEYS) assert.ok(!(k in entry), `expected "${k}" to be omitted`)
})

test('getOnline omits location for an unauthenticated request', async () => {
  setup(null) // getUserFromRequest returns null for anon callers
  const res = mockRes()
  await ctrl.getOnline({}, res)
  assert.equal(res.statusCode, 200)
  const entry = res.body[0]
  assert.equal(entry.name, 'Lady Mod')
  assert.equal(entry.serial, '0x1')
  for (const k of LOCATION_KEYS) assert.ok(!(k in entry), `expected "${k}" to be omitted`)
})

// An editor is staff but not admin/moderator — they should not see location.
test('getOnline omits location for an editor', async () => {
  setup({ id: 3, username: 'ed', role: 'editor' })
  const res = mockRes()
  await ctrl.getOnline({}, res)
  const entry = res.body[0]
  for (const k of LOCATION_KEYS) assert.ok(!(k in entry), `expected "${k}" to be omitted`)
})
