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

using System.Text.Json.Serialization;
using Plugin.Firebase.Firestore;

using DXLib.UI;
using DXLib.UI.Card;

using DXLib.Data.Model;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Represents a school, club, or other organization. Organization contains one or more teams and is the root of the
 * object model.
 */
public class Organization : DXImageModel
{
	/* Constants */
	public const string CollectionKey = "Organizations";

	// Types
	public const string ClubKey = "club";
	public const string HighSchoolKey = "high";

	// Custom color
	public static readonly Color DefaultColor = DXColors.Action;

    /* Properties */

    // Required
    [FirestoreProperty("IsSample")] public bool IsSample { get; set; }
    [FirestoreProperty("IsDebug")] public bool IsDebug { get; set; }

    [FirestoreProperty("Type")] public string Type { get; set; }
    [FirestoreProperty("Name")] public string Name { get; set; }
    [FirestoreProperty("Abbreviation")] public string Abbreviation { get; set; }

    // Optional
    [FirestoreProperty("Mascot")] public string Mascot { get; set; }
    [FirestoreProperty("Location")] public string Location { get; set; }
    [FirestoreProperty("Notes")] public string Notes { get; set; }

    [FirestoreProperty("ColorValue")] public long? ColorValue { get; set; }

    // MaxPreps link
    [FirestoreProperty("MaxPreps")] public MP2School MaxPreps { get; set; }

    // Parent (FK)
    [FirestoreProperty("UserId")] public string UserId { get; set; }

	/* Ignored */
	public string DebugId => UniqueId[ ..5 ];

	public string TypeName => DXString.GetLookupValue( "organization.type", Type );
	public Color Color { get => ColorValue.HasValue ? Color.FromUint( (uint)ColorValue ) : DefaultColor; set => ColorValue = value.ToUint(); }

	public bool IsClub => Type == ClubKey;
	public bool IsHighSchool => Type == HighSchoolKey;

	public bool HasMaxPreps => MaxPreps != null;

	// Children
	[JsonIgnore] public List<Team> Teams { get; private set; }

	// Parent (pseudo)
	[JsonIgnore] public User User { get; set; }

	/* Methods */
	public Organization()
	{
		BaseCollectionKey = CollectionKey;

		// Allocate containers
		Teams = [];

		// Defaults
		IsSample = false;
		IsDebug = false;

		ColorValue = DefaultColor.ToUint();
	}

	// Tests equality based on unique identifier
	public override bool Equals( object obj )
	{
		return (obj is Organization org) && org.UniqueId.Equals( UniqueId );
	}

	// Generates unique hash code (required)
	public override int GetHashCode()
	{
		return UniqueId.GetHashCode();
	}

	// Determines if list of organizations contains specified org
	public static bool Contains( List<Organization> list, string uniqueId )
	{
		return list.Any( org => org.UniqueId == uniqueId );
	}

	// Must be called any time user navigates down org object tree
	public static void SetScope( Organization org )
	{
		DXPreferences.Set( "scope.org", org?.UniqueId );

		// All cards and forms read-only for sample org
		DXCard.GlobalLock = org is { IsSample: true } && !Shell.CurrentUser.IsAdmin;
	}

	/* Populate */

	// Populates all child Team objects (and their Seasons)
	public async Task Populate( User user, bool deep = false )
	{
		User = user;

		// Must read ALL teams (even it not accessible)
		Teams = await ReadChildren<Team>( Team.CollectionKey, "OrganizationId" );
																														DXProfiler.Mark( Abbreviation );
		List<Team> teams = Team.CanView( user, this );

		// But only need to populate accessible ones
		foreach ( Team team in teams )
		{
			team.Organization = this;

			await team.Populate( deep );
		}
	}

	// Returns pre-populated Team matching specified unique identifier
	public Team GetTeam( string uniqueId ) { return Teams.Find( t => t.UniqueId == uniqueId ); }

	// Returns Team matching specified name
	public Team GetTeamByName( string name )
	{
		string canonical = DXUtils.ToCanonical( name );

		// Find match
		return (from team in Teams let canonical2 = DXUtils.ToCanonical( team.Name ) where canonical2.Equals( canonical ) select team).FirstOrDefault();
	}

	// Returns all seasons, from all teams, for this organization
	public List<Season> GetSeasons( Season exclude = null )
	{
		List<Season> seasons = [];

		// Add seasons from each team
		foreach ( Team team in Teams )
		{
			seasons.AddRange( team.GetSeasons( exclude ) );
		}

		return seasons;
	}

	// Returns combined roster of players from all seasons for this organization
	public List<Player> GetPlayers()
	{
		List<Player> players = [];

		// Combine rosters (MUST be pre-populated)
		foreach ( Team team in Teams )
		{
			players.AddRange( team.GetAllPlayers() );
		}

		return players;
	}

	/* Permissions */

	// Returns list of all Organizations user has permission to view
	public static List<Organization> CanView( User user )
	{
		return user.Organizations;
	}

	// Determines if user has permission to create Organizations
	public static bool CanCreate( User user )
	{
        // Director/Coach/Statistician can
        return user.Level switch
        {
	        User.LevelType.Director or 
	        User.LevelType.Coach or 
	        User.LevelType.Statistician => true,
	        
	        // No-one else can
	        _ => false
        };
	}

	// Determines if user has permission to edit Organizations
	public bool CanEdit( User user )
	{
		// Only owner has access
		return IsSample || user.IsAdmin || UserId.Equals( user.UniqueId );
	}

	// Determines if user has permission to analyze Organization stats
	public bool CanAnalyze( User user )
	{
		// Everyone can at least analyze 1 player
		return IsSample || (user.Level > User.LevelType.Media);
	}

    /* Analysis */

	// Returns list of all matches for this Organization
	public List<Match> GetMatches()
	{
		List<Match> matches = [];
	
		// Aggregate matches from all teams and seasons
		foreach ( Team team in Teams )
		{
				matches.AddRange( team.GetMatches() );
		}
	
		return matches;
	}

	// Aggregates all data for analyzing the scope of this Organization
	public async Task<DataStats> Aggregate()
	{
		DataStats stats = new();
	
		// Build stat list from each team in org
		foreach ( Team team in Teams )
		{
			stats.Add( await team.Aggregate() );
		}
	
		return stats;
	}
	
	// Aggregates all raw summary data for scope of this Organization
	public async Task<StatSummary> AggregateRaw()
	{
		StatSummary summary = new();
	
		// Aggregate summary data for each team in org
		foreach ( Team team in Teams )
		{
			summary.Add( await team.AggregateRaw() );
		}
	
		return summary;
	}

	/* CRUD */

	// READ

	// Returns Organization matching specified unique identifier
	public static async Task<Organization> Read( string uniqueId )
	{
		return await Read<Organization>( CollectionKey, uniqueId );
	}

	// Returns list of all sample Organizations
	public static async Task<IEnumerable<Organization>> ReadSample()
	{
		return await ReadList<Organization>( CollectionKey, "IsSample", true );
	}

	// Returns list of all debug Organizations
	public static async Task<IEnumerable<Organization>> ReadDebug()
	{
		return await ReadList<Organization>( CollectionKey, "IsDebug", true );
	}

	// Returns list of all Organizations owned by specified user
	public static async Task<IEnumerable<Organization>> ReadOwned( string userId )
	{
		return await ReadList<Organization>( CollectionKey, "UserId", userId );
	}

	// UPDATE

	// Updates internal debug flag for this Organization
	public async Task UpdateDebug( bool debug )
	{
		IsDebug = debug;

		await Update( "IsDebug", IsDebug );
	}

	// DELETE

	// Performs cascading delete for this Organization
	public override async Task Delete( bool remove = true )
	{
		// Delete children
		foreach ( Team team in Teams )
		{
			await team.Delete();
		}

		// Delete any permissions to this org
		await User.DeletePermission( UniqueId, Permission.LevelType.ALL );

		// Remove from parent
		User.Organizations.Remove( this );

		// Delete self
		await base.Delete( remove );

		// MUST clear launch path
		DXPreferences.Set( "launch.path", null );
	}
}

//
