namespace MeterVault.Infrastructure.Dashboard;
/// KPI card figures for a period plus the delta versus the previous comparable period.
public sealed record CostKpi(double Current, double Previous)
{
public double Delta => Current - Previous;
public double DeltaPercent => Previous == 0 ? 0 : (Current - Previous) / Math.Abs(Previous) * 100.0;
/// +1 up, -1 down, 0 flat.
public int Direction => Math.Sign(Math.Round(Delta, 2));
}
/// The overview KPIs: month and year cost, each with its previous-period comparison.
public sealed record DashboardSummary(DateOnly AsOf, CostKpi Month, CostKpi Year, double LatestMonthCost);
/// One slice of the cost breakdown / "what costs most" view.
public sealed record CategorySlice(string Name, string? ColorHex, double Cost);
/// The first thing missing before the dashboard can show a cost, in the order they are set up.
public enum CostSetupGap
{
None,
NoMeters,
NoCategories,
NoMembers,
NoTariffs,
}
///
/// What exists of the chain a cost figure needs: a meter, a category that counts it, and a price.
///
///
/// Manual costs stand in for the meter side only on an instance without meters. Once there are meters,
/// their chain is diagnosed on its own: the diagnosis only runs when this year shows no cost, so any
/// manual cost there is from another year and explains nothing about the meters.
///
public sealed record CostSetup(bool HasMeters, bool HasCategories, bool HasMembers, bool HasTariffs, bool HasManualCosts)
{
public CostSetupGap FirstGap =>
!HasMeters && !HasManualCosts ? CostSetupGap.NoMeters
: !HasCategories ? CostSetupGap.NoCategories
: !HasMeters ? CostSetupGap.None
: !HasMembers ? CostSetupGap.NoMembers
: !HasTariffs ? CostSetupGap.NoTariffs
: CostSetupGap.None;
}
/// A point on a monthly cost/consumption trend.
public sealed record TrendPoint(DateOnly Period, double Cost);
/// A row of the "what cost more / less" difference view.
public sealed record DifferenceRow(string Name, double Current, double Previous)
{
public double Delta => Current - Previous;
public double DeltaPercent => Previous == 0 ? 0 : (Current - Previous) / Math.Abs(Previous) * 100.0;
}