using MeterVault.App.Localization; using MeterVault.Core.Analysis; using MeterVault.Infrastructure.Dashboard; namespace MeterVault.App.Analysis; /// /// How every page states a cost change (D-07, brief §10 Phase 4 exit): one rule — , /// the totals when both periods are complete, else the paired buckets both sides have complete, else not comparable — /// and one wording, so the Overview, an energy type, the Analysis page and a meter never disagree about the same scope and /// period. /// public static class CostChanges { /// The change for a card: null without a comparison; unavailable ("No comparison") when not comparable. public static Change? ForCard(CostChange change) { ArgumentNullException.ThrowIfNull(change); return change.Basis == CostChangeBasis.NoComparison ? null : change.Change; } /// The change for a table's total row; null unless one is stated. public static Change? ForTotalRow(CostChange change) { ArgumentNullException.ThrowIfNull(change); return change.Change.IsAvailable ? change.Change : null; } /// Whether a rise is good news: neutral when either amount is a credit. public static ChangePolarity Polarity(CostChange change) { ArgumentNullException.ThrowIfNull(change); return ChangePolarities.ForCost(change.Current, change.Previous); } /// /// The caption of a change: what it is compared with, and — when only part of the period could be matched — that it /// is ("Same period last year · over the part both periods cover"). /// public static string? Caption(AnalysisQuery query, CostChange change) { ArgumentNullException.ThrowIfNull(query); ArgumentNullException.ThrowIfNull(change); if (change.Basis == CostChangeBasis.NoComparison) { return null; } var compared = query.Comparison.Display(); return change.IsPartial ? compared + " · " + Strings.Overview_MatchedOnly : compared; } }