// server.js's boot ORDER, which is a contract and not a style choice.
//
// Phase 4, slice 3 of MODULE_SYSTEM.md §2.7.2. Five steps have to happen in one
// order, and each arrow is a dependency that is invisible at the call site:
//
//   core schema  →  the declared set (§2.7.2 decision 4) needs the settings row
//                   its seed writes, to know which hosts it may install from
//   declared set →  requiring app.js SCANS the volume (§1.12), so anything put
//                   there afterwards is not in this process
//   require app  →  module schema fragments (§2.6) can only be replayed once the
//                   loader knows which modules there are
//   fragments    →  onBoot runs against tables that exist
//
// This is a source-structure test, and it is worth being plain about what that
// does and does not prove: it cannot tell you the server boots, only that nobody
// has quietly moved one of these five lines past another. It exists because the
// defect it guards against has already happened once and was invisible to every
// other kind of test here. Deferring the app require — which slice 3 had to do —
// moved core's schema ahead of the scan, and `ensureSchema` then skipped the
// module fragments entirely. On this machine's dev database the tables already
// existed, so the module started; on a FRESH database it would have started
// against no tables at all. Every suite in this directory stubs either the
// loader or the pool, so none of them could see it. The browser smoke did, from
// one log line.
process.env.DB_HOST = '127.0.0.1'
process.env.DB_PORT = '59999'

const fs = require('fs')
const path = require('path')

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

const SERVER = fs.readFileSync(path.join(__dirname, '..', 'src', 'server.js'), 'utf8')

/** Where a marker appears, asserted to appear exactly once. */
function at(marker) {
  const first = SERVER.indexOf(marker)
  assert.notEqual(first, -1, `server.js no longer contains ${JSON.stringify(marker)}`)
  assert.equal(
    SERVER.indexOf(marker, first + 1),
    -1,
    `${JSON.stringify(marker)} appears more than once in server.js — this test cannot tell which is the boot step`,
  )
  return first
}

test('boot runs core schema, then the declared set, then the scan, then fragments, then onBoot', () => {
  const steps = [
    ['core schema', at('await ensureSchema({ replayModules: false })')],
    ['declared module set', at('await declaredModules.resolve(')],
    ['the volume scan', at("require('./app')")],
    ['module schema fragments', at('replayFragments()')],
    ['module onBoot', at('await moduleLifecycle.boot()')],
  ]

  for (let i = 1; i < steps.length; i += 1) {
    assert.ok(
      steps[i][1] > steps[i - 1][1],
      `${steps[i][0]} must come after ${steps[i - 1][0]} in server.js`,
    )
  }
})

test('app.js is required inside start(), never at the top of the file', () => {
  // The whole mechanism depends on this. A top-level require runs when server.js
  // is loaded — before a single line of start() — and the scan would then happen
  // before the declared set had a chance to put anything on the volume, silently
  // and with no error anywhere.
  assert.ok(
    at("require('./app')") > at('async function start()'),
    'requiring ./app at the top of server.js scans the volume before the declared set is resolved',
  )
})

test('ensureSchema is asked NOT to replay fragments, and something else does', () => {
  // Both halves matter. Passing the flag without replaying elsewhere is the
  // original defect with an explicit spelling; replaying without the flag runs
  // the fragments twice, the second time being the one that matters.
  assert.match(SERVER, /ensureSchema\(\{ replayModules: false \}\)/)
  assert.match(SERVER, /modules\/schema'\)\.replayFragments\(\)/)
})
