import '../about/external/data/Commitment.dart'; import '../about/internal/application/Standing.dart'; import 'DebtEngine.dart'; import 'Thresholds.dart'; /// Standing is computed continuously and drives real consequences. It is /// derived, never set — there is no way to talk your way up a tier. class StandingEngine { /// The standing implied by the current debt and the missed-non-negotiable /// count. Sick mode never escalates: strictness must never be the response /// to someone who is actually struggling. static Standing evaluate( double debtScore, { int missedNonNegotiables = 0, bool sickMode = false, bool distressed = false, }) { if (sickMode || distressed) { return Standing.Good; } if (debtScore >= Thresholds.lockdownThreshold) { return Standing.Lockdown; } if (debtScore >= Thresholds.groundedThreshold || missedNonNegotiables >= Thresholds.nonNegotiableMissesForGrounded) { return Standing.Grounded; } if (debtScore >= Thresholds.warnedThreshold) { return Standing.Warned; } return Standing.Good; } /// Evaluate straight from the commitment list. static Standing evaluateFor( List commitments, { bool sickMode = false, bool distressed = false, DateTime? now, }) { return evaluate( DebtEngine.totalDebt(commitments, now: now), missedNonNegotiables: DebtEngine.missedNonNegotiables(commitments), sickMode: sickMode, distressed: distressed, ); } /// Whether a new commitment of this class may be created right now. static bool permitsNewCommitment(Standing standing, {bool elective = false}) { if (elective) { return canAddElectives(standing); } return canAddCommitments(standing); } /// Grounded replaces the home screen with the overdue queue — you do not get /// to look at your nice plans, only at your mess. static bool showsOverdueQueueAsHome(Standing standing) { return standing == Standing.Grounded || standing == Standing.Lockdown; } /// Lockdown puts a blocking interstitial in front of the app that must be /// cleared one item at a time. static bool blocksAppOnOpen(Standing standing) { return standing == Standing.Lockdown; } /// Whether the accountability partner is auto-notified at this tier. static bool notifiesPartner(Standing standing) { return standing == Standing.Grounded || standing == Standing.Lockdown; } /// Debt still to shed before dropping a tier. Zero when already at Good. static double debtToNextTierDown(double debtScore, Standing standing) { switch (standing) { case Standing.Lockdown: return debtScore - Thresholds.lockdownThreshold; case Standing.Grounded: return debtScore - Thresholds.groundedThreshold; case Standing.Warned: return debtScore - Thresholds.warnedThreshold; case Standing.Good: return 0; } } }