// Public · Modules — what this backend is currently serving beyond core.
//
// Phase 2, PR 6 of docs/website/MODULE_SYSTEM.md §2.7. The published shape is
// settled in MODULE_API.md §2.1 (`capabilities` are opaque strings, published
// here, for clients to feature-detect against).
//
// Two decisions are visible in the ten lines below and are the whole of this
// file's design:
//
//   • **`started` only.** The public surface answers "what is serving", and
//     nothing else. A module that failed to load, or that an operator disabled,
//     is simply ABSENT — the same treatment §4.4 already gives its routes and
//     its nav, so an anonymous visitor sees a site without that capability
//     rather than a site advertising a capability that 503s. `state`, the
//     failure stage and the failure reason are core's business and belong to the
//     admin Modules screen; none of the three is published here.
//   • **No database, and no siteMode gate.** The answer comes from the loader's
//     in-memory records, so this endpoint keeps working with the database down —
//     the same class as /public/version and /public/status, both of which must
//     answer during maintenance so a client can bootstrap and render the
//     maintenance page. A client that could not feature-detect while the site
//     was in maintenance would render its maintenance page as though no module
//     existed.
//
// This endpoint is deliberately NOT how a module's client chunk gets loaded.
// `utils/htmlShell.js` injects a `<script type="module">` per started module
// (MODULE_API.md §3.1.3), so the browser is handed the tag rather than a URL to
// go and fetch; there is no `client` field here for the same reason there is no
// second copy of any other fact. See MODULE_SYSTEM.md §2.6, amended to match.

const loader = require('../../../modules/loader')

const log = require('../../../utils/logger')('public:modules')

/** id, name, version and capabilities — everything else the loader knows is internal. */
const publish = (m) => ({
  id: m.id,
  name: m.name,
  version: m.version,
  capabilities: m.capabilities,
})

function getModules(req, res) {
  try {
    // Scan order (alphabetical by id) comes from the loader and is preserved:
    // there is no dependency resolution, so any other order would imply a
    // precedence nothing computes (MODULE_API.md §4.2).
    return res.json({ modules: loader.list().filter((m) => m.state === 'started').map(publish) })
  } catch (err) {
    // The only reachable throw is §7.6's guard — the module list read before
    // modules.load() ran. That is a mis-ordered boot, not a bad request, so it
    // is logged rather than answered with an empty list: `{ modules: [] }` is a
    // true answer for a core with no modules installed and a caller cannot tell
    // the two apart.
    log.error('module list unavailable', { message: err.message })
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

module.exports = { getModules }
