Implement Certification Status Derivation Business Logic
epic-peer-mentor-detail-screen-foundation-task-006 — Implement the CertificationStatusService as a pure Dart class (no Flutter dependencies, no network calls). The service accepts a certification expiry date, a pause flag, and an optional warning threshold (defaulting to 30 days). It must: return paused when the pause flag is true regardless of expiry, return expired when the expiry date is in the past, return expiring_soon when days remaining is within the warning threshold, and return active otherwise. Also compute and return the days_remaining integer for countdown display in UI widgets.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Strip time-of-day from both `now` and `expiryDate` before comparison using `DateTime(date.year, date.month, date.day)` to avoid edge cases where a 23:59 'now' treats the same calendar day as expired. Inject `now` as an optional parameter rather than a constructor dependency to keep the API simple while enabling testing. Do not add a Riverpod `Provider` wrapper in this task — that is a separate concern for the feature's provider layer. The business rule priority order (paused → expired → expiringSoon → active) must be implemented as a strict `if/else if` chain, not as parallel conditions, to guarantee the documented precedence.
Consider adding a `debugLabel` getter to `CertificationStatusService` for easier logging during development. Avoid using `Duration` arithmetic directly on dates with time zones — always normalize to date-only `DateTime` first.
Testing Requirements
Pure Dart unit tests covering all branches. Required test cases: (1) isPaused=true, valid future expiry → status=paused, daysRemaining=positive. (2) isPaused=true, already expired → status=paused (pause takes priority). (3) expiry yesterday → status=expired, daysRemaining=-1.
(4) expiry today → status=expiringSoon, daysRemaining=0. (5) expiry in 15 days, threshold=30 → status=expiringSoon, daysRemaining=15. (6) expiry in 31 days, threshold=30 → status=active, daysRemaining=31. (7) threshold=0, expiry in 1 day → status=active.
(8) threshold=0, expiry today → status=expiringSoon, daysRemaining=0. All tests inject `now` parameter — no test relies on `DateTime.now()` directly. Aim for 100% branch coverage on this service.
The design token theme extension may conflict with existing ThemeData extensions already registered in the app, causing runtime assertion errors or token resolution failures across all screens that consume the tokens.
Mitigation & Contingency
Mitigation: Audit all existing ThemeData extensions before implementation. Use a unique extension key namespace and add integration tests that instantiate the combined theme in a test app harness.
Contingency: If conflicts arise, isolate design tokens behind a dedicated provider singleton (Riverpod) rather than a ThemeData extension, updating all consuming widgets to read from the provider instead.
The 30-day warning threshold for expiring_soon status may differ between HLF's stated requirement in workshops (60 days mentioned in user stories) and the 30-day value in component documentation, causing disagreement during acceptance testing.
Mitigation & Contingency
Mitigation: Explicitly confirm the threshold value with HLF stakeholder before implementation. Make the threshold a named constant (kCertificationWarnDays) so it can be updated without logic changes.
Contingency: If stakeholder confirms 60 days post-implementation, update the constant and re-run the unit test suite — no architectural change required.