/**
 * lib/license-caps.js — effective self-hosted license caps, read at point of use.
 *
 * Owns: expiry-aware read of global.TUNEVAULT_LICENSE; the 402 copy for a
 *       connection-cap hit.
 * Does NOT own: license validation or the offline last-known-good grace
 *       (server.js validateSelfHostedLicense()); key generation (lib/license-key.js).
 *
 * Why point-of-use: global.TUNEVAULT_LICENSE is set once, at boot. A container
 * brought up with `docker compose up -d` and never restarted would otherwise keep
 * its boot-time caps indefinitely — so a 30-day trial would never actually expire,
 * and expires_at is the only thing separating a trial from a perpetual license.
 * Checked when used; deliberately no background timer or re-validation loop.
 */

'use strict';

// ── Two DIFFERENT degraded states. Do not conflate them. ────────────────────
//
// MINIMAL_CAPS — "we cannot CONFIRM entitlement": an unvalidated key, offline
// with no cache, or grace expired. This may well be a legitimate paying customer
// whose network blocks outbound (our ICP blocks outbound by default), so health
// checks keep working. Failing open here is deliberate: a network signal is not
// an entitlement signal. Connections are still held at 1 so an unvalidated
// instance is never worth more than a validated one.
const MINIMAL_CAPS = { max_connections: 1, max_health_checks_per_month: -1 };

// EXPIRED_CAPS — "we KNOW they are not entitled": the license's own expires_at
// has passed, which is a fact carried by the license itself and needs no network
// to establish. Health checks — the actual product value — stop. The instance
// still boots and every existing connection, report and ticket stays readable.
const EXPIRED_CAPS = { max_connections: 1, max_health_checks_per_month: 0 };

const PRICING_URL = 'https://tunevault.app/pricing';

/**
 * A cap of -1 means unlimited. So does null/undefined: paid self-hosted licenses
 * are minted with max_health_checks_per_month = NULL (see migration
 * 1783600000000 — "Allow NULL ... to mean unlimited"), and routes/license.js
 * passes that straight through. Treating NULL as a real number would compare
 * `count >= null` → `count >= 0` → true, i.e. block every paying customer.
 */
function isUnlimitedCap(value) {
  return value === -1 || value === null || value === undefined;
}

/**
 * Effective caps right now, with expires_at enforced at the moment of the call.
 *
 * Returns null in cloud mode, and null before/without a license — callers then
 * no-op, exactly as they did when reading global.TUNEVAULT_LICENSE directly.
 * On an expired license returns the licensed object with MINIMAL_CAPS applied and
 * expired: true, so callers can explain *why* they were capped.
 */
function getEffectiveLicense() {
  if (process.env.TUNEVAULT_SELF_HOSTED !== 'true') return null; // cloud — strict no-op

  const lic = global.TUNEVAULT_LICENSE;
  if (!lic) return null;

  // No expiry (perpetual license, or the minimal caps applied offline) — as-is.
  if (!lic.expires_at) return { ...lic, expired: false };

  const expiresAtMs = new Date(lic.expires_at).getTime();
  // An unreadable expiry counts as expired — the same fail-closed posture the
  // offline cache path takes in validateSelfHostedLicense().
  if (Number.isFinite(expiresAtMs) && expiresAtMs > Date.now()) {
    return { ...lic, expired: false };
  }

  return { ...lic, ...EXPIRED_CAPS, expired: true };
}

/**
 * 402 body for a cap hit. `kind` selects which limit was actually hit, so an
 * expired trial is told it expired rather than handed a generic "limit reached"
 * — otherwise the user has no idea why their license stopped working.
 */
function licenseLimitMessage(lic, kind = 'connections') {
  const expired = !!(lic && lic.expired);
  const expiresAtMs = lic && lic.expires_at ? new Date(lic.expires_at).getTime() : NaN;
  const on = Number.isFinite(expiresAtMs)
    ? ` on ${new Date(expiresAtMs).toISOString().slice(0, 10)}`
    : '';

  if (kind === 'health_checks') {
    if (expired) {
      return `Your TuneVault license expired${on}. New health checks are disabled — your existing ` +
             `connections, reports and tickets remain available. Renew at ${PRICING_URL}`;
    }
    return `Monthly health check limit reached. Your license allows ${lic.max_health_checks_per_month} ` +
           `health checks per month. Upgrade at ${PRICING_URL}`;
  }

  if (expired) {
    return `Your TuneVault license expired${on} and is now limited to ${lic.max_connections} connection(s). ` +
           `Renew at ${PRICING_URL}`;
  }
  return `Connection limit reached. Your license allows ${lic.max_connections} connections. Upgrade at ${PRICING_URL}`;
}

module.exports = { getEffectiveLicense, licenseLimitMessage, isUnlimitedCap, MINIMAL_CAPS, EXPIRED_CAPS };
