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

using Plugin.Firebase.Firestore;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Represents an individual player on a roster within a parent Season.
 */
public class Player : RootModel
{
	/* Constants */
	public const string CollectionKey = "Players";

    /* Properties */

    // Required
    [FirestoreProperty("FirstName")] public string FirstName { get; set; }
    [FirestoreProperty("LastName")] public string LastName { get; set; }
    [FirestoreProperty("Number")] public string Number { get; set; }

    [FirestoreProperty("Deactivated")] public bool? Deactivated { get; set; }

    // Optional
    [FirestoreProperty("AltNumber")] public string AltNumber { get; set; }
    [FirestoreProperty("Nickname")] public string Nickname { get; set; }
    [FirestoreProperty("Height")] public string Height { get; set; }
    [FirestoreProperty("Year")] public string Year { get; set; }
    [FirestoreProperty("Notes")] public string Notes { get; set; }

    // Lineup position(s) (array)
    [FirestoreProperty("Positions")] public IList<string> Positions { get; set; }

    // Account link(s) (map, array of maps)
    [FirestoreProperty("Link")] public Link Link { get; set; }
    [FirestoreProperty("FanLinks")] public IList<Link> FanLinks { get; set; }

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

	/* Ignored */
	public string YearName => DXString.GetLookupValue( "player.year", Year );
	public string FullName => GetFirstLast();
	public override string ObjectName => FullName;

	public Color Color => Season.Color;

	public bool HasPhoto => !string.IsNullOrEmpty( ImageUrl );
	public bool HasNumber => !string.IsNullOrEmpty( Number );
	public bool HasAltNumber => !string.IsNullOrEmpty( AltNumber );
	public bool HasPositions => Positions is { Count: > 0 };

	public bool HasLink => Link is { HasValue: true };
	public bool HasFanLink => (FanLinks.Count > 0);

	public bool IsDeactivated => Deactivated is true;

	// Validates name uniqueness within roster
	public bool DuplicateFirst { get; set; }
	public bool DuplicateLast { get; set; }

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

	// ISV import use only
	public int ImportPlayerId { get; set; }

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

		// Allocate containers
		FanLinks = new List<Link>();
	}

	// Tests equality of two players based on unique identifier (OR Root identifier)
	public override bool Equals( object obj )
	{
		return (obj is Player player) && (player.UniqueId.Equals( UniqueId ) || player.RootId.Equals( RootId ));
	}
	
	// Generates unique hash code, required for list de-duping
	public override int GetHashCode()
	{
		return UniqueId.GetHashCode();
	}

	/* Number Utils */

	// Returns either primary or alternate jersey number
	public string GetNumber( bool alt )
	{
		return alt ? AltNumber : Number;
	}

	// Determines if specified number is alternate jersey number
	public bool IsAltNumber( string number )
	{
		return HasAltNumber && !string.IsNullOrEmpty( number ) && number.Equals( AltNumber );
	}

	/* Position Utils */

	// Returns first listed position for default selection
	public string FirstPosition( bool libero )
	{
		string position = HasPositions ? Positions[0] : null;

		// Libero may not be allowed
		if ( position is Lineup.LiberoKey && !libero )
		{
			// Find non-libero position
			foreach ( string pos in Positions )
			{
				if ( !pos.Equals( Lineup.LiberoKey ) )
				{
					return pos;
				}
			}

			// Default to OH
			return Lineup.OutsideKey;
		}

		return position;
	}

	// Returns concatenated list of positions 'Position1, Position2, ...'
	public string PositionList()
	{
		string list = string.Empty;

		// Build list
		for ( int i = 0; i < Positions.Count; i++ )
		{
			string key = Positions[i].ToLower();
			string item = DXString.GetLookupItem( "lineup.position", key ).Value;

			list += item;

			if ( i < (Positions.Count - 1) )
			{
				list += ", ";
			}
		}

		return list;
	}

	// Returns concatenated list of position keys
	public string PositionKeyList()
	{
		return Positions.Aggregate( string.Empty, ( current, t ) => current + $"{t.ToUpper()} " );
	}

	/* Name Utils */

	// Returns full name in format 'First Last'
	public string GetFirstLast( int maxLen = 0 )
	{
		string name = $"{FirstName} {LastName}";

		int len = Math.Min( maxLen, name.Length );

		// Optionally truncate
		return (maxLen > 0) ? name[ ..len ] : name;
	}

	// Returns full name in format 'Last, First'
	public string GetLastFirst( int maxLen = 0 )
	{
		string name = $"{LastName}, {FirstName}";

		int len = Math.Min( maxLen, name.Length );

		// Optionally truncate
		return (maxLen > 0) ? name[ ..len ] : name;
	}

	// Returns abbreviated version of name (first initial, full last)
	public string GetAbbrevFirst( int maxLen = 10 )
	{
		// First initial
		string first = FirstName[ ..1 ];

		// 'F. Bar'
		string abbrev = $"{first}. {LastName}";

		int len = Math.Min( abbrev.Length, maxLen );

		// Optionally truncate
		return (maxLen > 0) ? abbrev[ ..len ] : abbrev;
	}

	// Returns abbreviated version of name (full first, last initial)
	public string GetAbbrevLast( int maxLen = 8 )
	{
		string first = FirstName[ ..Math.Min( maxLen, FirstName.Length ) ];
		string last = LastName[ ..1 ];

		// 'Foo B.'
		return $"{first} {last}.";
	}

	// Returns full name combined with position label
	public string GetNamePosition( string position = null )
	{
		string pos = position ?? FirstPosition( true );
		string posStr = (pos == null) ? string.Empty : $"  ({pos.ToUpper()})";

		// 'Jane Smith (OH)'
		return $"{FullName}{posStr}";
	}

	// Returns uniform number combined with full name
	public string GetNumberName( bool symbol, int maxLen = 0 )
	{
		string number = Number.PadLeft( 2, '0' );
		string name = GetFirstLast( maxLen );

		string hash = symbol ? "#" : string.Empty;

		// '11 Jane Smith' or '#11 Jane Smith'
		return $"{hash}{number} {name}";
	}

	// Returns uniform number combined with abbreviated name
	public string GetAbbrevNumberName( bool symbol, int maxLen = 0 )
	{
		string number = Number.PadLeft( 1, '0' );
		string name = GetAbbrevFirst( maxLen );

		string hash = symbol ? "#" : string.Empty;

		// '11 J. Smith'
		return $"{hash}{number} {name}";
	}

	// Returns player display in format configured for analyze stat tables
	public string GetAnalyzeName( int maxLen = 0 )
	{
		return Shell.Settings.AnalyzePlayer switch
		{
			Settings.FirstLastKey => GetFirstLast( maxLen ),
			Settings.LastFirstKey => GetLastFirst( maxLen ),
			Settings.NumberNameKey => GetNumberName( false, maxLen ),

			_ => null,
		};
	}

	/* Permissions */

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

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

	// Determines if user has permission to analyze Player stats
	public bool CanAnalyze( User user )
	{
		return user.Level switch
		{
			// Director/coach always can
			User.LevelType.Director or 
			User.LevelType.Coach => true,
			
			// Stat/fan/player only can if have access to this player
			User.LevelType.Statistician or 
			User.LevelType.Fan or 
			User.LevelType.Player => user.HasPermission( this ),
			
			// No-one else can
			_ => Season.IsSample
		};
	}

	/* CRUD */

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

	// Updates career stat root for this Player
	public async Task UpdateRoot( Root root )
	{
		Root = root;

		await Update( "Root", Root );
	}

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

	// Updates status for Fan permission link(s)
	public async Task UpdateFanLinks( string username, Link.StatusType status )
	{
		await UpdateLinks( FanLinks, username, status, "FanLinks" );
	}

	// Performs cascading delete for this Player
	public override async Task Delete( bool remove = true )
	{
		// Delete permissions from any user with access
		await Permission.Delete( UniqueId, Link );

		// No children to delete

		// Remove from parent
		if ( remove )
		{
			Season.Players.Remove( this );
		}

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

	// Deletes specified Player permission link
	public override async Task DeleteLink( string username )
	{
		await DeleteLink( Link, username, "Link" );

		Link = null;
	}

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

	/* Import */

	// Creates new Player in specified season from existing object (batched)
	public static void Import( IWriteBatch batch, Season season, Player player )
	{
		Player newPlayer = new()
		{
			// Copy fields from existing object
			FirstName = player.FirstName,
			LastName = player.LastName,
			Number = player.Number,

			AltNumber = player.AltNumber,
			Nickname = player.Nickname,
			Height = player.Height,
			Notes = player.Notes,

			// Lineup positions
			Positions = player.Positions,

			// Account link(s)
			Link = player.Link,
			FanLinks = player.FanLinks,

			// Start with existing image
			ImageUrl = player.ImageUrl,

			// Connect to this season
			SeasonId = season.UniqueId,
			Season = season
		};

		// Add to parent
		season.Players.Add( newPlayer );

		// Connect roots
		newPlayer.ConnectPlayer( batch, player );

		// Persist
		newPlayer.Create( batch );
	}
}

//
