// A deliberately tiny semver range check — enough for `coreApi` and no more.
//
// Supports `*`, an exact `x.y.z`, `^x.y.z` and `~x.y.z`. That is the whole
// grammar a module manifest is allowed to use (MODULE_API.md §1.1), so pulling
// in the `semver` package for it would add a dependency to the server for a
// twenty-line job. A range this parser does not understand is REJECTED rather
// than assumed to match — an unparseable range must not silently load a module
// against an API it was never tested on.

const PARTS = /^(\d+)\.(\d+)\.(\d+)$/

function parse(version) {
  const m = PARTS.exec(String(version).trim())
  if (!m) return null
  return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) }
}

const gte = (a, b) => {
  if (a.major !== b.major) return a.major > b.major
  if (a.minor !== b.minor) return a.minor > b.minor
  return a.patch >= b.patch
}

/**
 * Does `version` satisfy `range`?
 * @param {string} version an exact x.y.z
 * @param {string} range   `*` | `x.y.z` | `^x.y.z` | `~x.y.z`
 * @returns {boolean} false for anything unparseable, on either side
 */
function satisfies(version, range) {
  const v = parse(version)
  if (!v) return false
  const raw = String(range).trim()
  if (raw === '*') return true

  const op = raw[0] === '^' || raw[0] === '~' ? raw[0] : ''
  const b = parse(op ? raw.slice(1) : raw)
  if (!b) return false

  if (op === '') return v.major === b.major && v.minor === b.minor && v.patch === b.patch
  if (!gte(v, b)) return false
  // ^ allows minor+patch within the same major; ~ allows patch within the same minor.
  if (op === '^') return v.major === b.major
  return v.major === b.major && v.minor === b.minor
}

module.exports = { satisfies, parse }
