/* 
 * Copyright (c)2009-2025 DemiVision, LLC. All Rights Reserved. The information 
 * herein is the CONFIDENTIAL and PROPRIETARY information of DemiVision, LLC. 
 */

using DXLib.Data;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Implements an in-memory cache for accessing deserialized JSON definitions for analysis dashboards and their underlying
 * reports. Improves performance by minimizing the number of file reads and deserialization operations required. Methods
 * are also provided for adding custom dashboards/reports to the pre-defined list. DashboardCache is a singleton for
 * convenient global access.
 */
public class DashboardCache
{
	/* Properties */

	// Raw JSON for master dashboard list
	public JsonAnalyze Json { get; private set; }

	// Cached JSON config (reports stored within dashboards)
	public Dictionary<string,JsonDashboard> Dashboards { get; private set; }

	// Cached card metrics
	public Dictionary<string,DataMetrics> Metrics { get; private set; }

	/* Fields */

	// Singleton instance
	private static DashboardCache instance;
	
	// Once-only initialization
	private bool initialized;

	/* Methods */
	private DashboardCache()
	{}

	// Singleton instance
	public static DashboardCache Instance { get { return instance ??= new DashboardCache(); } }

	// Post construction initialization
	public async Task Init()
	{
		if ( !initialized )
		{
			// Deserialize raw JSON
			await ReadJson();

			// Parse dashboard list
			await ReadDashboards();

			// Add custom dashboards to list
			ReadCustom();
			
			initialized = true;
		}
	}

	// Returns display name for specified stat
	public string GetStatName( string category, string stat )
	{
		IList<JsonReport> reports = Dashboards[ category ].Reports;

		// Find matching report
		foreach ( JsonReport report in reports )
		{
			// Return title
			if ( report.Key == stat )
			{
				return report.Title;
			}
		}

		return null;
	}

	/* Report */

	// Returns cached JSON config matching specified report parameters. Changes to returned report affect ALL reports of that type.
	public JsonReport GetReport( string category, string type, string stat )
	{
		IList<JsonReport> reports = Dashboards[ category ].Reports;

		// Find matching report
		foreach ( JsonReport report in reports )
		{
			if ( (report.Type == type) && (report.Key == stat) )
			{
				return report;
			}
		}

		return null;
	}

	// Returns cached JSON config matching params from custom report
	public JsonReport GetReport( CustomReport report )
	{
		return GetReport( report.Category, report.Type, report.Stat );
	}

	/* Metrics */

	// Returns cached metric totals for specified data set
	public DataMetrics GetMetrics( string dataSet )
	{
		return Metrics[ dataSet ];
	}

	// Caches metric totals for all relevant data sets
	public async Task CacheMetrics( DashboardConfig config, bool team1 )
	{
		Metrics = new Dictionary<string, DataMetrics>
		{
			{ DataConfig.SkillsData, await CacheDataSet( config, team1, DataConfig.SkillsData ) },
			{ DataConfig.ScoringData, await CacheDataSet( config, team1, DataConfig.ScoringData ) },
			{ DataConfig.SideoutData, await CacheDataSet( config, team1, DataConfig.SideoutData ) },
			{ DataConfig.PlayingData, await CacheDataSet( config, team1, DataConfig.PlayingData ) },
			{ DataConfig.MatchData, await CacheDataSet( config, team1, DataConfig.MatchData ) }
		};
	}

	// Aggregates, accumulates, and calculates all metrics for specified data set
	private static async Task<DataMetrics> CacheDataSet( DashboardConfig config, bool team1, string dataSet )
	{
		// Configure query
		DataConfig dataConfig = new()
		{
			IsForceEligible = config.IsForceEligible,

			Scope = config.Scope,
			ScopeObject = config.ScopeObject,

			Organization = config.Organization,
			IsTeam1 = team1,

			Team1 = config.Team1,
			Team2 = config.Team2,

			Action = null,
			OuterDim = null,
			InnerDim = null,

			DataSet = dataSet
		};

		// Aggregate all stats within scope
		DataStats stats = await DataFilter.Filter( dataConfig );

		// Accumulate variables, calculate statistics
		DataMetrics totals = await DataMetrics.ProcessTotals( dataConfig, stats );

		// Opponent NA for legacy data set
		totals.HasRally = stats.HasRally;

		return totals;
	}

	/* Read */

	// Reads and deserializes raw JSON for master dashboard list
	private async Task ReadJson()
	{
		Json = await DXResource.ReadJson<JsonAnalyze>( "Dashboards/dashboards.json" );
	}

	// Parses JSON configuration files for all categories
	private async Task ReadDashboards()
	{
		// Create cache container
		Dashboards = new Dictionary<string, JsonDashboard>();

		foreach ( JsonGroup group in Json.Groups )
		{
			// Each category in separate JSON file
			foreach ( JsonCategory category in group.Categories )
			{
				string key = category.Key.Replace( ".", "_" ).ToLower();

				// Deserialize JSON
				JsonDashboard dashboard = await DXResource.ReadJson<JsonDashboard>( $"Dashboards/{key}.json" );
				
				// Cache
				Dashboards.Add( category.Key, dashboard );
			}
		}
	}

	/* Data Form */

	// Returns list of categories eligible for primary/opponent team
	public List<DXItem> GetCategoryList( bool team1 )
	{
		List<DXItem> list = [];

		// Build list, sorted by group (fixed) than category (alpha)
		foreach ( JsonGroup group in Json.Groups )
		{
			foreach ( JsonCategory category in group.GetCategories() )
			{
				// Some categories may be excluded from custom reports
				if ( category.AllowCustom )
				{
					// Some categories NA for opponent
					if ( team1 || category.Opponent )
					{
						list.Add( new DXItem( category.Key, category.Title ) );
					}
				}
			}
		}

		return list;
	}

	// Returns list of Analyze By dimensions available for specified category
	public static List<DXItem> GetDimensionList( string category, bool team1 = true )
	{
		List<DXItem> list = [];

		// Get JSON
		JsonDashboard dashboard = Instance.Dashboards[ category ];

		// Get full dimension list
		Dictionary<string,string> dimensions = DXString.GetLookupTable( "custom.dimension" );

		// Only include eligible dimensions
		foreach ( string dimension in dashboard.Dimensions )
		{
			if ( team1 || (dimension == KeyDimension.RotationKey) )
			{
				list.Add( new DXItem( dimension, dimensions[ dimension ] ) );
			}
		}

		return list;
	}

	// Returns list of eligible report types for specified category
	public static List<DXItem> GetTypeList( string category )
	{
		List<DXItem> list = [];

		// Get JSON
		JsonDashboard dashboard = Instance.Dashboards[ category ];

		// Get full type list
		Dictionary<string,string> types = DXString.GetLookupTable( "custom.type" );

		// Only include eligible types
		foreach ( string type in dashboard.Types )
		{
			// Momentum Chart never eligible
			if ( type != AnalyzeKeys.MomentumKey )
			{
				list.Add( new DXItem( type, types[ type ] ) );
			}
		}

		return list;
	}

	// Returns list of statistics available for specified category and type
	public List<DXItem> GetStatList( string category, string type )
	{
		List<DXItem> list = [];

		// Isolate specified category
		JsonDashboard dashboard = Dashboards[ category ];

		// Only include stat reports of specified type
		foreach ( JsonReport report in dashboard.Reports )
		{
			if ( report.Type == type )
			{
				list.Add( new DXItem( report.Key, report.Title ) );
			}
		}

		return list;
	}

	/* Custom */

	// Adds/updates db persisted custom dashboards to JSON config
	public void ReadCustom()
	{
		ClearCustom();

		// Dashboards persisted in User
		IList<CustomDashboard> dashboards = Shell.CurrentUser.Dashboards;

		// Add to JSON cache
		foreach ( CustomDashboard dashboard in dashboards )
		{
			AddCustom( dashboard );
		}
	}

	// Caches specified custom dashboard
	public void AddCustom( CustomDashboard dashboard )
	{
		string key = dashboard.GetKey();

		// Create category config (dashboard card)
		JsonCategory jsonCategory = new()
		{
			Key = key,
			Title = dashboard.Name,

			Metrics = GetMetrics( dashboard ),

			AllowCustom = false,
			Custom = dashboard
		};

		GetCustom().Categories.Add( jsonCategory );

		// Create dashboard config
		JsonDashboard jsonDashboard = new()
		{
			IsCustom = true
		};

		// Create underlying reports
		foreach ( CustomReport report in dashboard.Reports )
		{
			JsonReport jsonReport = GetReport( report );

			// Add common config
			jsonDashboard.Reports.Add( jsonReport );

			// Custom must include pre-configured dimension
			jsonDashboard.CustomDimensions.Add( report.Dimension );
		}

		// Key MUST be unique
		Dashboards.TryAdd( key, jsonDashboard );

		// Must save reference here
		dashboard.JsonCategory = jsonCategory;
	}

	// Clears specified custom dashboard from cache
	public void RemoveCustom( CustomDashboard dashboard )
	{
		Dashboards.Remove( dashboard.GetKey() );

		GetCustom().Categories.Remove( dashboard.JsonCategory );
	}

	// Clears all custom dashboards
	private void ClearCustom()
	{
		// Clear categories
		GetCustom().Categories.Clear();

		List<string> keys = Dashboards.Keys.ToList();

		// Clear underlying dashboards (and reports)
		foreach ( string key in keys )
		{
			JsonDashboard dashboard = Dashboards[ key ];

			if ( dashboard.IsCustom )
			{
				Dashboards.Remove( key );
			}
		}
	}

	// Returns JSON config for custom dashboard group
	private JsonGroup GetCustom()
	{
		return Json.Groups[0];
	}

	// Returns metrics to be shown on custom dashboard card
	private List<JsonMetric> GetMetrics( CustomDashboard dashboard )
	{
		List<JsonMetric> list = [];

		// Check each report for highlighted metrics
		foreach ( CustomReport report in dashboard.Reports )
		{
			if ( report.IsOnCard )
			{
				// Retrieve config (always use config from grid)
				JsonReport jsonReport = GetReport( report.Category, AnalyzeKeys.GridKey, report.Stat );
				JsonCustom jsonCustom = jsonReport.Custom;

				if ( jsonCustom != null )
				{
					// Create metric
					JsonMetric metric = new()
					{
						Data = jsonReport.Data,
						Key = jsonCustom.Key,
						Title = jsonCustom.Title,
						Type = jsonCustom.Type
					};

					list.Add( metric );
				}
			}
		}

		return list;
	}
}

//
