/// The counterweights. An app built on guilt has an obvious failure mode: the
/// people who need it most delete it during their worst week. These are not
/// nice-to-haves — they are the retention strategy.
class GuardrailEngine {
  /// Debt increase over the window that counts as a spike.
  static const double debtSpikeDelta = 15;

  /// App opens per week below which engagement counts as dropped.
  static const int engagementFloor = 3;

  /// Readiness score below which inputs count as degraded.
  static const int readinessFloor = 4;

  /// Distress is the conjunction, not any single signal: debt spiking *and*
  /// engagement dropping *and* readiness degrading. Strictness must never be
  /// the response to someone who is actually struggling.
  static bool detectDistress({
    required double debtDelta,
    required int appOpensThisWeek,
    required int meanReadiness,
  }) {
    final bool debtSpiking = debtDelta >= debtSpikeDelta;
    final bool disengaging = appOpensThisWeek <= engagementFloor;
    final bool degrading = meanReadiness > 0 && meanReadiness <= readinessFloor;

    return debtSpiking && disengaging && degrading;
  }

  /// How many amnesty tokens remain this month. Rationed so they feel
  /// valuable, but they exist so a bad flu does not destroy three months of
  /// progress.
  static int tokensRemaining(int granted, int spent) {
    final int remaining = granted - spent;
    return remaining > 0 ? remaining : 0;
  }

  static bool canSpendAmnesty(int granted, int spent) {
    return tokensRemaining(granted, spent) > 0;
  }

  /// When distressed, the plan is cut to this many non-negotiables and nothing
  /// else is asked for.
  static const int distressPlanSize = 3;
}
