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

using DXLib.UI;
using DXLib.UI.Alert;

using DXLib.UI.Form;
using DXLib.UI.Form.Control;

using DXLib.Data;
using DXLib.Utils;

namespace iStatVball3;

/*
 * CRUD data entry form for a Player.
 */
public sealed class PlayerForm : LinkForm
{
	/* Fields */
	private readonly Season season;

	// Career stat linking
	private RootSelector rootSelector;
	private RootRemover rootRemover;

	private RootTreeData rootSelected;
	private bool rootRemoved;

	/* Methods */
	public PlayerForm( Player player, Season season ) : base( player, "player" )
	{
		this.season = season;

		header.Title = HasData ? player.FullName : CreateHeader();
		imagePanel.Color = season.Color;

		// Can link to root player(s) for career stats
		CustomHeader = "link";

		UpdateRootBtn( HasData && player.IsRooted );

		/* Required */

		// First Name
		AddControl( new DXTextField
		{
			Key = "first",
			Title = "player.first",
			Text = player?.FirstName,
			MinLength = 1,
			MaxLength = 32,
			Type = DXTextField.TextType.CapitalizeWords,
			Hint = DXFormControl.HintType.RequiredMin,
			Help = "player.first"
		}, true, true );

		// Last Name
		AddControl( new DXTextField
		{
			Key = "last",
			Title = "player.last",
			Text = player?.LastName,
			MinLength = 1,
			MaxLength = 32,
			Type = DXTextField.TextType.CapitalizeWords,
			Hint = DXFormControl.HintType.RequiredMin,
			Help = "player.last"
		}, true, true );

		// Number (supports leading zeroes)
		AddControl( new DXNumericField
		{
			Key = "number",
			Title = "player.number",
			Text = player?.Number,
			MinValue = 0,
			MaxValue = 99,
			Hint = DXFormControl.HintType.RequiredRange,
			Help = "player.number"
		}, true, true );

		// Deactivated?
		if ( HasData )
		{
			bool deactive = HasData && player!.IsDeactivated;

			AddControl( new DXCheckboxField
			{
				Key = "deactive",
				Text = "player.deactive",
				Checked = deactive,
				Hint = DXFormControl.HintType.None,
				Help = "player.deactive"
			}, true, true );
		}

		/* Optional */

		// Alternate Number
		AddControl( new DXNumericField
		{
			Key = "alt",
			Title = "player.alt",
			Text = player?.AltNumber,
			MinValue = 0,
			MaxValue = 99,
			Hint = DXFormControl.HintType.RequiredRange,
			Help = "player.alt"
		}, false, false );

		// Position(s)
		AddControl( new DXMultiSelect
		{
			Key = "positions",
			Title = "player.positions",
			Items = "player.position",
			SelectedItems = player?.Positions,
			MaxSelections = 3,
			Hint = DXFormControl.HintType.RequiredMax,
			Help = "player.positions",
		}, false, false );

		// Nickname
		AddControl( new DXTextField
		{
			Key = "nickname",
			Title = "player.nickname",
			Text = player?.Nickname,
			MaxLength = 32,
			Type = DXTextField.TextType.CapitalizeWords,
			Help = "player.nickname"
		}, false, false );

		// Height
		AddControl( new DXHeightSelectorField
		{
			Key = "height",
			Title = "player.height",
			SelectedHeight = player?.Height,
			Hint = DXFormControl.HintType.None,
			Help = "player.height",
		}, false, false );

		// Year
		AddControl( new DXSelectorField
		{
			Key = "year",
			Title = "player.year",
			Items = "player.year",
			SelectedItem = player?.Year,
			Hint = DXFormControl.HintType.None,
			Help = "player.year",
		}, false, false );

		// Notes
		AddControl( new DXEditorField
		{
			Key = "notes",
			Title = "form.notes",
			Text = player?.Notes,
			MaxLength = 1024,
			Help = "player.notes"
		}, false, false );

		/* Links */

		if ( HasData )
		{
			bool connected = DXData.HasConnection();

			// Player link
			AddControl( new LinkField
			{
				IsDisabled = !HasData || !connected,

				Key = "link",
				Title = "player.link",
				Link = player?.Link,
				Help = "player.link"
			});

			// Fan link(s)
			AddControl( new LinkField
			{
				IsDisabled = !HasData || !connected,

				Key = "link.fans",
				Title = "player.link.fans",
				Links = player?.FanLinks,
				Help = "player.link.fans"
			});
		}

		// Image
		SetImage( player?.ImageUrl );

		// Control initialization
		Init();
	}

	// Validates that number unique on roster and main/alt numbers differ
	private bool ValidateNumber()
	{
		string number = GetString( "number", true );

		Player player = data as Player;

		// Number must be unique on roster
		foreach ( Player rosterPlayer in season.Players )
		{
			if ( !rosterPlayer.Equals( player ) && (rosterPlayer.Number == number) )
			{
				DXAlert.ShowError( "player.roster", "player.err.num" );

				return false;
			}
		}

		string alt = GetString( "alt", false );

		// Main/alternate numbers cannot be the same
		if ( number == alt )
		{
			DXAlert.ShowError( "player.roster", "player.err.alt" );

			return false;
		}

		// Valid
		return true;
	}

	// Check uniqueness of first/last names for every player on roster
	private void ValidateName()
	{
		foreach ( Player player in season.Players )
		{
			// Must reset first
			if ( player.DuplicateFirst )
			{
				player.DuplicateFirst = false;
			}
			if ( player.DuplicateLast )
			{
				player.DuplicateLast = false;
			}

			// Check each player against all others
			foreach ( Player checkPlayer in season.Players )
			{
				if ( !player.Equals( checkPlayer ) )
				{
					// First name
					if ( player.FirstName == checkPlayer.FirstName )
					{
						player.DuplicateFirst = true;
					}

					// Last name
					if ( player.LastName == checkPlayer.LastName )
					{
						player.DuplicateLast = true;
					}
				}
			}
		}
	}

	/* Abstracts */

	// Include root select/remove in changes
	protected override bool HasChanges()
	{
		return base.HasChanges() || rootSelector is { HasChanges: true } || rootRemover is { HasChanges: true };
	}

	/* Event Callbacks */

	// User confirmed delete
	protected override async void OnDeleteConfirmed()
	{
		base.OnDeleteConfirmed();

		if ( data is Player player )
		{
			// Cascading delete
			await player.Delete( true );

			// Check name uniqueness
			ValidateName();

			// Refresh UI
			Shell.Instance.HideForm();
		}
	}

	// Used confirmed cancel
	protected override void OnCancelConfirmed()
	{
		base.OnCancelConfirmed();

		Shell.Instance.HideForm();
	}

	// Used tapped save
	protected override async void OnSaveTapped()
	{
		// Validate jersey number
		if ( !ValidateNumber() )
		{
			return;
		}

		base.OnSaveTapped();

		// Retrieve fields
		string firstName = GetString( "first", true );
		string lastName = GetString( "last", true );
		string number = GetString( "number", true );
		bool deactive = GetBool( "deactive", true );

		string alt = GetString( "alt", false );
		string nickname = GetString( "nickname", false );
		string height = GetString( "height", false );
		string year = GetString( "year", false );
		string notes = GetString( "notes", false );

		IList<string> positions = GetKeys( "positions", false );

		// Links
		string link = GetLink( "link" );
		List<string> fanLinks = GetLinks( "link.fans" );

		// Update existing object
		if ( HasData )
		{
			if ( data is Player player )
			{
				player.FirstName = firstName;
				player.LastName = lastName;
				player.Number = number;
				player.Deactivated = deactive;

				player.AltNumber = alt;
				player.Nickname = nickname;
				player.Height = height;
				player.Year = year;
				player.Notes = notes;

				// Positions saved as key array
				player.Positions = positions;

				// Image
				await SaveImage( player );

				// Update player link
				player.Link = await UpdateLink( link, player.Link,
				CreateKeys( Permission.LevelType.Player, season.Team ) );

				// Update fan link(s)
				await UpdateLinks( player, fanLinks, player.FanLinks,
				CreateKeys( Permission.LevelType.Fan, season.Team ) );

				// Check name uniqueness (must occur inside write trans)
				ValidateName();

				// Update career stat links
				await SaveRoot( player );

				// Persist
				await player.Update();
			}
		}
		// Create new object
		else
		{
			// Player object for this season
			Player player = new()
			{
				FirstName = firstName,
				LastName = lastName,
				Number = number,
				Deactivated = deactive,

				AltNumber = alt,
				Nickname = nickname,
				Height = height,
				Year = year,
				Notes = notes,

				Positions = positions,

				// Set parent
				SeasonId = season.UniqueId,
				Season = season
			};

			// Image
			await SaveImage( player );

			// Add player link
			player.Link = await CreateLink( link, CreateKeys( Permission.LevelType.Player, season.Team ) );

			// Add fan link(s)
			await CreateLinks( player, fanLinks, CreateKeys( Permission.LevelType.Fan, season.Team ) );

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

			// Check name uniqueness (must occur inside write trans)
			ValidateName();

			// Create career stat link
			await SaveRoot( player );

			// Persist
			await player.Create();
		}

		// Refresh UI
		Shell.Instance.HideForm();
	}

	/* Root */

	// Updates root button color to indicate linked status
	private void UpdateRootBtn( bool rooted, bool disabled = false )
	{
		if ( IsReadOnly )
		{
			CustomHeaderBtn.IsVisible = false;
		}
		else
		{
			CustomHeaderColor = (rooted ? DXColors.Positive : DXColors.Action);

			CustomHeaderBtn.IsDisabled = disabled;
			CustomHeaderBtn.IsVisible = true;

			CustomHeaderBtn.Reset();
		}
	}

	// Root button tapped
	protected override void OnCustomHeaderTapped()
	{
		// Requires network
		if ( DXData.HasConnection() )
		{
			OnRootTapped();
		}
		else
		{
			DXAlert.ShowNetworkError( "net.err.root", null, CustomHeaderBtn.Reset );
		}
	}

	// User tapped root button
	private async void OnRootTapped()
	{
		Player player = HasData ? (data as Player) : null;

		bool rooted = player is { IsRooted: true };

		// Must sure root player not deleted
		if ( rooted )
        {
			rooted = await player.Root.IsValid( season.Team.Organization );
        }

		// Player has root link, show remove UI
		if ( rooted )
		{
			if ( rootRemover == null )
			{
				rootRemoved = false;

				// Create popup
				rootRemover = new RootRemover
				{
					Removed = OnRootRemoved,
					Closed = OnRootClosed
				};

				// Populate
				rootRemover.Init( player.Root );
			}

			rootRemover.Show( CustomHeaderBtn );
		}
		// No root link, present select UI
		else
		{
			if ( rootSelector == null )
			{
				rootSelected = null;

				// Create popup
				rootSelector = new RootSelector
				{
					Selected = OnRootSelected,
					Closed = OnRootClosed
				};

				// Populate
				await rootSelector.Init( season );
			}

			rootSelector.Show( CustomHeaderBtn );
		}
	}

	// Auto-populates some fields when new player linked to root
	private void PopulateRootFields( Player player )
	{
		DXTextField first = GetControl( "first", true ) as DXTextField;
		DXTextField last = GetControl( "last", true ) as DXTextField;
		DXNumericField number = GetControl( "number", true ) as DXNumericField;

		// Keep existing, else populate from root
		first!.Text ??= player.FirstName;
		last!.Text ??= player.LastName;
		number!.Text ??= player.Number;

		UpdateSave();
	}

	// Persists root changes for players on both ends of root link
	private async Task SaveRoot( Player player1 )
	{
		// Save root selection
		if ( rootSelected != null )
		{
			if ( rootSelected.Object is Player player2 )
			{
				Season season1 = season;
				Season season2 = rootSelected.Season;

				// Create new root for this player
				Root root1 = new()
				{
					TeamId = season2.TeamId,
					SeasonId = season2.UniqueId,
					ObjectId = player2.UniqueId
				};

				// Selected player has existing root
				if ( player2.IsRooted )
				{
					Root root2 = player2.Root;

					// Use their Root ID
					root1.RootId = root2.RootId;
				}
				// New root for both players
				else
				{
					string rootId = DXData.Guid();

					// New Root ID
					root1.RootId = rootId;

					// Selected player linked to this player
					Root root2 = new()
					{
						RootId = rootId,

						TeamId = season1.TeamId,
						SeasonId = season1.UniqueId,
						ObjectId = player1.UniqueId
					};

					// Persist
					await player2.UpdateRoot( root2 );
				}

				// This player root persisted later
				player1.Root = root1;
			}
		}
		// Save root removal
		else if ( rootRemoved )
		{
			if ( data is Player player )
			{
				Player player2 = player.Root.Player;
				Root root2 = player2?.Root;

				// Only delete destination root if points back to this player
				if ( (root2 != null) && player.Equals( root2.Player ) )
				{
					await player2.UpdateRoot( null );
				}

				player.Root = null;
			}
		}
	}

	// User selected player for root link
	private void OnRootSelected( RootTreeData root )
	{
		Season rootSeason = root.Season;

		if ( rootSeason != null )
		{
			if ( root.Object is Player player )
			{
				string title = DXString.Get( "root.title" );
				string teamSeason = $"{rootSeason.Team.Name} {rootSeason.Name}";

				// 'Link <player> #<number> from <team-season>?'
				string msg = DXString.Get( "root.link.msg", player.FullName, player.Number, teamSeason );

				// Confirm link
				DXAlert.ShowPositiveCancelRaw( title, msg, "root.link", () => OnRootConfirmed( root ), rootSelector.Reset );
			}
		}
	}

	// User confirmed root link
	private void OnRootConfirmed( RootTreeData root )
	{
		rootSelected = root;

		// Some fields auto-populated when linking to new player
		PopulateRootFields( rootSelected.Object as Player );

		// Update UI
		rootSelector.Dismiss();

		UpdateSave();
		UpdateRootBtn( true, true );
	}

	// User tapped remove link button
	private void OnRootRemoved()
	{
		if ( data is Player player )
		{
			string teamSeason = $"{season.Team.Name} {season.Name}";
			string title = DXString.Get( "root.title" );

			// 'Remove link to <player> #<number> on <team-season>?'
			string msg = DXString.Get( "root.remove.msg", player.FullName, player.Number, teamSeason );

			// Confirm removal
			DXAlert.ShowNegativeCancelRaw( title, msg, "root.remove", OnRemoveConfirmed );
		}
	}

	// User confirmed root removal
	private void OnRemoveConfirmed()
	{
		rootRemoved = true;
		rootSelected = null;

		// Close popup
		rootRemover.Dismiss();

		// Update UI
		CustomHeaderBtn.Reset();

		UpdateSave();
		UpdateRootBtn( false, true );
	}

	// User dismissed root popup
	private void OnRootClosed()
	{
		CustomHeaderBtn.Reset();
	}
}

//
