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

// Term capture lives in the model (shardState.model.upsertGovernor →
// recordGovernorTransition) and talks to the db module. We exercise the real
// logic against an in-memory fake by monkeypatching the shared db module object
// (same instance the model require()s) — no DB, no mocking library.
const db = require('../src/model/shardState/shardState.db')
const model = require('../src/model/shardState/shardState.model')

let terms // in-memory shard_governor_terms
let nextId
const saved = {}

beforeEach(() => {
  terms = []
  nextId = 1
  for (const k of ['currentGovernorTerm', 'closeGovernorTerm', 'openGovernorTerm', 'upsertGovernor']) {
    saved[k] = db[k]
  }
  db.currentGovernorTerm = async (city) =>
    terms.find((t) => t.city === city && t.ended_at === null) || null
  db.closeGovernorTerm = async (id, endedAt) => {
    const row = terms.find((t) => t.id === id)
    if (row) row.ended_at = endedAt
  }
  db.openGovernorTerm = async ({ city, serial, name, acct, webId, startedAt }) => {
    terms.push({ id: nextId++, city, governor_serial: serial, governor_name: name,
      governor_acct: acct, governor_web_id: webId, started_at: startedAt, ended_at: null })
  }
  db.upsertGovernor = async () => {} // snapshot write — irrelevant to term capture
})

function restore() {
  for (const k of Object.keys(saved)) db[k] = saved[k]
}

test('a repeated city.update with the same governor does NOT open a second term', async () => {
  await model.upsertGovernor({ city: 'Britain', governor: { serial: '0x1', name: 'Darrow' }, t: 100 })
  await model.upsertGovernor({ city: 'Britain', governor: { serial: '0x1', name: 'Darrow' }, t: 200 })
  const open = terms.filter((t) => t.ended_at === null)
  assert.equal(terms.length, 1)
  assert.equal(open.length, 1)
  assert.equal(open[0].governor_serial, '0x1')
  assert.equal(open[0].started_at, 100)
  restore()
})

test('a governor change closes the old term and opens a new one', async () => {
  await model.upsertGovernor({ city: 'Britain', governor: { serial: '0x1', name: 'Darrow' }, t: 100 })
  await model.upsertGovernor({ city: 'Britain', governor: { serial: '0x2', name: 'Mira' }, t: 300 })
  assert.equal(terms.length, 2)
  const [first, second] = terms
  assert.equal(first.governor_serial, '0x1')
  assert.equal(first.ended_at, 300) // closed at the transition time
  assert.equal(second.governor_serial, '0x2')
  assert.equal(second.ended_at, null) // now current
  assert.equal(second.started_at, 300)
  restore()
})

test('a seat going vacant closes the term without opening a new one', async () => {
  await model.upsertGovernor({ city: 'Britain', governor: { serial: '0x1', name: 'Darrow' }, t: 100 })
  await model.upsertGovernor({ city: 'Britain', governor: null, t: 400 })
  assert.equal(terms.length, 1)
  assert.equal(terms[0].ended_at, 400)
  restore()
})

test('terms are tracked independently per city', async () => {
  await model.upsertGovernor({ city: 'Britain', governor: { serial: '0x1' }, t: 100 })
  await model.upsertGovernor({ city: 'Minoc', governor: { serial: '0x9' }, t: 120 })
  await model.upsertGovernor({ city: 'Britain', governor: { serial: '0x1' }, t: 200 }) // dup, no-op
  assert.equal(terms.length, 2)
  assert.equal(terms.filter((t) => t.ended_at === null).length, 2)
  restore()
})
