/* 
 * 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.Utils;

namespace iStatVball3;

/*
 * Represents one team within a parent Organization. A team has one or more child Seasons.
 */
public class Team : LinkModel
{
	/* Constants */
	public const string CollectionKey = "Teams";

    /* Properties */

    // Required
    [FirestoreProperty("Gender")] public string Gender { get; set; }
    [FirestoreProperty("Level")] public string Level { get; set; }
    [FirestoreProperty("Name")] public string Name { get; set; }
    [FirestoreProperty("Abbreviation")] public string Abbreviation { get; set; }

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

    // Integration
    [FirestoreProperty("VarsityBoundId")] public string VarsityBoundId { get; set; }

    // Account Links (array of maps)
    [FirestoreProperty("MediaLinks")] public IList<Link> MediaLinks { get; set; }

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

    // Owner (FK)
    [FirestoreProperty("OwnerId")] public string OwnerId { get; set; }

	/* Ignored */
	public string GenderName => DXString.GetLookupValue( "team.gender", Gender );

	public string FullName => $"{Organization.Name} {Name}";
	public string ShortName => Organization.Name;
	public string AbbrevName => Abbreviation ?? Organization.Abbreviation;

	public Color Color => Organization.Color;
	public bool IsSample => Organization.IsSample;

	public bool HasMediaLink => (MediaLinks.Count > 0);

	// Children
	[JsonIgnore] public List<Season> Seasons { get; private set; }

	// Parent
	public Organization Organization { get; set; }

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

		// Allocate containers
		MediaLinks = new List<Link>();
		Seasons = [];
	}

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

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

	// Maps level to label for club and high school teams
	public static string GetLevelName( bool club, string level )
	{ 
		return (level == null) ? null : DXString.GetLookupValue( (club ? "team.level.club" : "team.level.hs"), level ); 
	}

	// Returns most current season for team
	public Season GetCurrentSeason()
	{
		List<Season> seasons = GetSeasons( null, true );

		// Newest/only season is most current
		return (seasons.Count == 0) ? null : seasons[0];
	}
		
	// Determines if specified season is most current for this team
	public bool IsCurrentSeason( Season season )
	{
		Season current = GetCurrentSeason();

		return ((current != null) && current.Equals( season ));
	}

	/* Populate */

	// Populates all child Season objects
	public async Task Populate( bool deep = false )
	{
		Seasons = await ReadChildren<Season>( Season.CollectionKey, "TeamId" );
																														DXProfiler.Mark( "Team" );
		// Do NOT go any deeper unless forced
		foreach ( Season season in Seasons )
		{
			if ( deep )
			{
				await season.PopulateFull();
			}

			season.Team = this;
		}
	}

	// Returns pre-populated Season matching specified unique identifier
	public Season GetSeason( string uniqueId ) { return Seasons.Find( s => s.UniqueId == uniqueId ); }

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

		// Find match
		foreach ( Season season in Seasons )
		{
			string canonical2 = DXUtils.ToCanonical( season.Name );

			if ( canonical2.Equals( canonical ) )
			{
				return season;
			}
		}

		// No match
		return null;
	}

	// Returns all seasons for this team, optionally excluding a season
	public List<Season> GetSeasons( Season exclude, bool sorted = false )
	{
		// Add seasons unless excluded
		List<Season> seasons = Seasons.Where( season => (exclude == null) || !season.Equals( exclude ) ).ToList();
		
		// Optionally sort chronologically
		return sorted ? seasons.OrderByDescending( s => s.StartDate ).ToList() : seasons;
	}

	// Returns combined roster for all seasons for this team
	public List<Player> GetAllPlayers()
	{
		List<Player> players = [];

		// Combine rosters (MUST be pre-populated)
		foreach ( Season season in Seasons )
		{
			players.AddRange( season.Players );
		}

		return players;
	}

	/* Permissions */

	// Returns list of Teams within organization user has permission to view
	public static List<Team> CanView( User user, Organization organization )
	{
		List<Team> list = [];

		// Sample/debug teams always visible
		if ( organization.IsSample || (organization.IsDebug && user.IsAdmin) )
		{
			list.AddRange( organization.Teams );
		}
		// Build list of permitted teams
		else
		{
			foreach ( Permission permission in user.Permissions )
			{
				Team team = organization.GetTeam( permission.TeamId );

				// Might have multiple permissions
				if ( (team != null) && !list.Contains( team ) )
				{
					list.Add( team );
				}
			}
		}

		return list;
	}

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

	// Determines if user has permission to edit Teams
	public static bool CanEdit( User user )
	{
		return user.Level switch
		{
			// Only director/coach/stat
			User.LevelType.Director or 
			User.LevelType.Coach or 
			User.LevelType.Statistician => true,
			
			// No-one else can
			_ => false
		};
	}

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

    /* Analyze */

	// Returns list of all matches for this Team
	public List<Match> GetMatches()
	{
		List<Match> matches = [];
	
		// Aggregate matches from all seasons
		foreach ( Season season in Seasons )
		{
			matches.AddRange( season.GetMatches() );
		}
		
		return matches;
	}
	
	// Accumulates all data for analyzing the scope of this Team
	public async Task<DataStats> Aggregate()
	{
		DataStats stats = new();
	
		// Build stat list from each season for team
		foreach ( Season season in Seasons )
		{
			stats.Add( await season.Aggregate() );
		}
	
		return stats;
	}
	
	// Aggregates all raw summary data for scope of this Team
	public async Task<StatSummary> AggregateRaw()
	{
		StatSummary summary = new();
	
		// Aggregate summary data for each season for team
		foreach ( Season season in Seasons )
		{
			summary.Add( await season.AggregateRaw() );
		}
	
		return summary;
	}

	/* CRUD */

	// Reads and returns Team matching specified unique identifier
	public static async Task<Team> Read( string uniqueId )
	{
		return await Read<Team>( CollectionKey, uniqueId );
	}

	// Updates status for specified permission link
	public override async Task UpdateLink( string username, Link.StatusType status )
	{
		await UpdateLinks( MediaLinks, username, status, "MediaLinks" );
	}

	// Performs cascading delete for this Team
	public override async Task Delete( bool remove = true )
	{
		// Delete all permissions for this user
		await Permission.DeleteOwner( this );

		// Delete permissions from any user with access to team
		await Permission.Delete( UniqueId, MediaLinks );

		// Delete children
		foreach ( Season season in Seasons )
		{
			await season.Delete();
		}

		// Remove from parent
		if ( remove )
		{
			Organization.Teams.Remove( this );
		}

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

	// Deletes specified permission link from list
	public override async Task DeleteLinks( string username )
	{
		await DeleteLinks( MediaLinks, username, "MediaLinks" );
	}
}

//
