// Unit tests for the moderation dashboard's pure reshaping/annotation logic.
// DB-free (like the rest of this suite) — the SQL layer is exercised manually
// against a dev database per the plan's verification steps.
const { test } = require('node:test')
const assert = require('node:assert/strict')

const moderation = require('../src/model/moderation/moderation.pure')

test('reshapeWindows: folds rows into windows and zero-fills missing types', () => {
  const rows = [
    { action_type: 'ban', d1: 1, d7: 3, d30: 5 },
    { action_type: 'warn', d1: 0, d7: 2, d30: 9 },
  ]
  const { windows } = moderation.reshapeWindows(rows)
  assert.deepEqual(windows['24h'], { ban: 1, kick: 0, mute: 0, warn: 0 })
  assert.deepEqual(windows['7d'], { ban: 3, kick: 0, mute: 0, warn: 2 })
  assert.deepEqual(windows['30d'], { ban: 5, kick: 0, mute: 0, warn: 9 })
})

test('reshapeWindows: coerces string/decimal SUM results to numbers', () => {
  const { windows } = moderation.reshapeWindows([{ action_type: 'mute', d1: '2', d7: '2', d30: '4' }])
  assert.strictEqual(windows['24h'].mute, 2)
  assert.strictEqual(windows['30d'].mute, 4)
})

test('reshapeWindows: ignores unknown action types (e.g. future enum values)', () => {
  const { windows } = moderation.reshapeWindows([{ action_type: 'filter_hit', d1: 9, d7: 9, d30: 9 }])
  assert.deepEqual(windows['24h'], { ban: 0, kick: 0, mute: 0, warn: 0 })
})

test('reshapeWindows: empty input yields all-zero windows', () => {
  const { windows } = moderation.reshapeWindows([])
  assert.deepEqual(windows, {
    '24h': { ban: 0, kick: 0, mute: 0, warn: 0 },
    '7d': { ban: 0, kick: 0, mute: 0, warn: 0 },
    '30d': { ban: 0, kick: 0, mute: 0, warn: 0 },
  })
})

test('annotate: flags automated when staff id matches the bot application id', () => {
  const [row] = moderation.annotate([{ staff_user_id: '999', target_site_user_id: null }], '999')
  assert.equal(row.is_automated, true)
})

test('annotate: string/number snowflake mismatch still matches (coerced)', () => {
  // mod_actions stores staff_user_id as VARCHAR, but bot_config.application_id
  // could arrive as a number — the compare must coerce both sides.
  const [row] = moderation.annotate([{ staff_user_id: 999, target_site_user_id: null }], '999')
  assert.equal(row.is_automated, true)
})

test('annotate: staff action (id differs from bot) is not automated', () => {
  const [row] = moderation.annotate([{ staff_user_id: '111', target_site_user_id: null }], '999')
  assert.equal(row.is_automated, false)
})

test('annotate: no bot application id configured means nothing is automated', () => {
  const [row] = moderation.annotate([{ staff_user_id: '999', target_site_user_id: null }], null)
  assert.equal(row.is_automated, false)
})

test('annotate: folds joined identity columns into linked_account', () => {
  const [row] = moderation.annotate(
    [{ staff_user_id: '1', target_site_user_id: 7, target_site_username: 'perry' }],
    null,
  )
  assert.deepEqual(row.linked_account, { id: 7, username: 'perry' })
})

test('annotate: no linked identity yields null linked_account', () => {
  const [row] = moderation.annotate([{ staff_user_id: '1', target_site_user_id: null }], null)
  assert.equal(row.linked_account, null)
})

test('windowValue: picks the right column per window key and coerces to number', () => {
  const row = { d1: '2', d7: 5, d30: '11' }
  assert.strictEqual(moderation.windowValue(row, '24h'), 2)
  assert.strictEqual(moderation.windowValue(row, '7d'), 5)
  assert.strictEqual(moderation.windowValue(row, '30d'), 11)
})

test('windowValue: null row (no rows in window) yields 0', () => {
  assert.strictEqual(moderation.windowValue(null, '24h'), 0)
  assert.strictEqual(moderation.windowValue(undefined, '30d'), 0)
})

test('windowValue: null sum column yields 0', () => {
  assert.strictEqual(moderation.windowValue({ d1: null, d7: null, d30: null }, '7d'), 0)
})

test('windowValue: a null narrow-window column never falls back to the 30d total', () => {
  // Regression: an earlier rewrite used `{...}[key] ?? row.d30`, so a null d1/d7
  // returned the 30-day count instead of 0 — inflating the 24h/7d tiles.
  const row = { d1: null, d7: null, d30: 5 }
  assert.strictEqual(moderation.windowValue(row, '24h'), 0)
  assert.strictEqual(moderation.windowValue(row, '7d'), 0)
  assert.strictEqual(moderation.windowValue(row, '30d'), 5)
})
