// ── Admin: uo-link sidecar control ─────────────────────────────────────────
//
// Configure the connection to the uo-link sidecar (base/ws URL, shared-secret
// token, protocol pin, enabled) and drive the town crier. SECURITY: the token
// is write-only over this API — stored encrypted, NEVER returned; responses
// expose only `hasToken` (same convention as the Discord bot token). Saving
// (re)starts the WS ingest client so a change takes effect with no redeploy.

const uoLinkConfig = require('../../../model/uoLinkConfig/uoLinkConfig.model')
const uoLinkClient = require('../../../utils/uoLinkClient')
const uoLinkSocket = require('../../../utils/uoLinkSocket')
const shardBroadcast = require('../../../utils/shardBroadcast')
const activity = require('../../../model/activity/activity.model')

const log = require('../../../utils/logger')('admin-uolink')

// Assemble the masked config + live health + ingestion stats for the panel.
async function buildStatus() {
  const config = await uoLinkConfig.getSafe()
  const health = await uoLinkClient.health()
  return {
    ...config,
    health: health.ok ? health.data : { ok: false, error: health.error || `status ${health.status}` },
    ingest: uoLinkSocket.getState(),
    sse: shardBroadcast.stats(),
  }
}

// GET /admin/uo-link/config — masked config + live status + ingestion stats.
async function getConfig(req, res) {
  try {
    return res.json(await buildStatus())
  } catch (err) {
    log.error('uoLink.getConfig', err)
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// PUT /admin/uo-link/config — save connection settings + (re)start the socket.
async function saveConfig(req, res) {
  const { baseUrl, wsUrl, token, protocol, enabled } = req.body
  try {
    const current = await uoLinkConfig.getSafe()
    const willHaveToken = Boolean(token) || current.hasToken
    if (enabled && !willHaveToken) {
      return res.status(400).json({ message: 'An auth token is required before enabling.' })
    }

    await uoLinkConfig.save({
      baseUrl,
      wsUrl,
      token,
      protocol: protocol !== undefined ? Number(protocol) : undefined,
      enabled,
      updatedBy: req.user.id,
    })
    // Drop the client's cached config so the health check below uses the new values.
    uoLinkClient.invalidateConfig()

    // (Re)start or stop the ingest socket to match the new enabled/URL/token.
    const saved = await uoLinkConfig.getSafe()
    if (saved.enabled && saved.hasToken) {
      await uoLinkSocket.start()
    } else {
      uoLinkSocket.stop()
      await uoLinkConfig.recordStatus({ status: 'disconnected', pluginConnected: false })
    }

    await activity.log({ req, action: 'uoLink.config.update', detail: { baseUrl: saved.baseUrl, enabled: saved.enabled } })
    log.info('uo-link config updated', { by: req.user.username, enabled: saved.enabled })
    return res.json(await buildStatus())
  } catch (err) {
    log.error('uoLink.saveConfig', err)
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// POST /admin/uo-link/towncrier — publish/replace a town-crier message.
async function postTownCrier(req, res) {
  const { id, lines, durationSec } = req.body
  try {
    const result = await uoLinkClient.postTownCrier({ id, lines, durationSec })
    if (result.ok) {
      await activity.log({ req, action: 'uoLink.towncrier.post', detail: { id } })
      return res.json(result.data || { ok: true, id })
    }
    if (result.status === 400) return res.status(400).json({ message: 'The shard rejected that message (over the line/duration caps?).' })
    if (result.status === 503 || result.status === 0) {
      return res.status(503).json({ message: 'The shard is unavailable right now.' })
    }
    return res.status(502).json({ message: 'Could not reach the shard.' })
  } catch (err) {
    log.error('uoLink.postTownCrier', err)
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// DELETE /admin/uo-link/towncrier/:id — remove a town-crier message.
async function deleteTownCrier(req, res) {
  const { id } = req.params
  try {
    const result = await uoLinkClient.deleteTownCrier(id)
    if (result.ok) {
      await activity.log({ req, action: 'uoLink.towncrier.delete', detail: { id } })
      return res.json(result.data || { ok: true, id })
    }
    if (result.status === 404) return res.status(404).json({ message: 'No town-crier message with that id.' })
    if (result.status === 503 || result.status === 0) {
      return res.status(503).json({ message: 'The shard is unavailable right now.' })
    }
    return res.status(502).json({ message: 'Could not reach the shard.' })
  } catch (err) {
    log.error('uoLink.deleteTownCrier', err)
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// GET /admin/uo-link/stream — the full live feed (incl. audit/cheat), staff only.
function stream(req, res) {
  shardBroadcast.subscribe(req, res, 'admin')
}

module.exports = { getConfig, saveConfig, postTownCrier, deleteTownCrier, stream }
