Game/MonolithStats.cs
namespace Monolith;

/// <summary>
/// Submission side of the global leaderboards.
///
/// Stats in s&amp;box are aggregated server-side across every session of the game, so these are
/// genuinely worldwide with no backend of ours. That matters because the shared Monolith itself
/// is only per-host (see GOALS.md, hosting option B): the boards are the one part of the game
/// that really is global, and they are free.
/// </summary>
public static class MonolithStats
{
	/// <summary>
	/// The package these boards belong to. **This must match the published package ident.**
	/// While the project is local and unpublished the boards will simply come back empty,
	/// which the UI reports rather than hiding.
	/// </summary>
	public const string PackageIdent = "idkman.monolith";

	/// <summary>Board 1: cubes removed from Monoliths. Summed, descending.</summary>
	public const string StatCubes = "monolith-cubes";

	/// <summary>Board 2: furthest stage reached in solo. Max-aggregated, descending.</summary>
	public const string StatBestStage = "solo-stage";

	/// <summary>Board 2: most prestiges. Max-aggregated, descending.</summary>
	public const string StatBestCollapses = "prestiges";

	/// <summary>Board 3: fastest full solo ladder run, in seconds. MIN-aggregated, ascending.</summary>
	public const string StatFastestLadder = "ladder-seconds";

	/// <summary>Board 4: fastest shared Monolith felling, in seconds. MIN-aggregated, ascending.</summary>
	public const string StatFastestMonolith = "monolith-seconds";

	/// <summary>Running total, kept because it is free. Not a board.</summary>
	public const string StatCollapses = "collapses";

	/// <summary>Cubes this player has removed from Monoliths.</summary>
	public static void AddCubes( double cubes )
	{
		if ( cubes <= 0 ) return;

		try { Sandbox.Services.Stats.Increment( StatCubes, cubes ); }
		catch ( Exception e ) { Log.Warning( $"Stat submit failed ({StatCubes}): {e.Message}" ); }
	}

	/// <summary>
	/// Furthest solo stage ever reached. SetValue rather than Increment, because the board
	/// aggregates by max: submitting the same high-water mark twice must not double it.
	/// </summary>
	public static void ReportBestStage( int stageOneBased )
	{
		try { Sandbox.Services.Stats.SetValue( StatBestStage, stageOneBased ); }
		catch ( Exception e ) { Log.Warning( $"Stat submit failed ({StatBestStage}): {e.Message}" ); }
	}

	public static void AddCollapse()
	{
		try { Sandbox.Services.Stats.Increment( StatCollapses, 1 ); }
		catch ( Exception e ) { Log.Warning( $"Stat submit failed ({StatCollapses}): {e.Message}" ); }
	}

	/// <summary>
	/// Lifetime prestige count for the board. Max-aggregated via SetValue so a reinstall or a
	/// second machine cannot inflate the total the way Increment would.
	/// </summary>
	public static void ReportCollapses( int total )
	{
		try { Sandbox.Services.Stats.SetValue( StatBestCollapses, total ); }
		catch ( Exception e ) { Log.Warning( $"Stat submit failed ({StatBestCollapses}): {e.Message}" ); }
	}

	/// <summary>Speedrun: seconds for a full stage 1 to 100 solo run. Board aggregates by MIN.</summary>
	public static void ReportLadderTime( float seconds )
	{
		if ( seconds <= 0f ) return;

		try { Sandbox.Services.Stats.SetValue( StatFastestLadder, seconds ); }
		catch ( Exception e ) { Log.Warning( $"Stat submit failed ({StatFastestLadder}): {e.Message}" ); }
	}

	/// <summary>Speedrun: seconds to fell a shared Monolith. Board aggregates by MIN.</summary>
	/// <remarks>
	/// Rejects implausibly short times as well as zero. These boards are MIN-aggregated, so a
	/// nonsense value is not a blemish, it is permanent: it takes first place worldwide and there
	/// is no way to remove it after the fact.
	/// </remarks>
	public static void ReportMonolithTime( float seconds )
	{
		if ( seconds <= 1f ) return;

		try { Sandbox.Services.Stats.SetValue( StatFastestMonolith, seconds ); }
		catch ( Exception e ) { Log.Warning( $"Stat submit failed ({StatFastestMonolith}): {e.Message}" ); }
	}
}