/* 
 * 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.Layout;
using DXLib.UI.Container;

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

using DXLib.UI.Control;
using DXLib.UI.Control.Button;
using DXLib.UI.Control.Checkbox;

using DXLib.Log;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Implements the internal admin interface used to search for a user, display general information and purchase history
 * for the user, and provide controls for updating user data fields.
 */
public class AdminTools : DXScroll
{
	/* Constants */
	private const double LayoutSpacing = 20;

	private const double InfoHt = 16;
	private const double HistoryHt = 14;
	private const double OrgsHt = 30;
	private const double SessionHt = 14;

	private const double UpdateHt = 60;
	private const double UpdateBtnWd = 75;

	/* Fields */
	private readonly DXVerticalLayout layout;

	// Layouts
	private DXGridLayout info;
	private DXGridLayout history;
	private DXGridLayout update;
	private DXGridLayout organization;
	private DXGridLayout session;

	// Search
	private DXTextField searchCtl;
	private DXButton searchBtn;

	// Migrate
	private DXTextField migrateCtl;
	private DXButton migrateBtn;
	
	// Version
	private DXStepper versionCtl;
	private DXButton versionBtn;

	// Level
	private DXStepper levelCtl;
	private DXButton levelBtn;

	// Role
	private DXSelectorField roleCtl;
	private DXButton roleBtn;

	// Add Team-Seasons
	private DXKeypadField purchaseCtl;
	private DXButton purchaseBtn;

	// Organization list
	private DXButton orgsBtn;

	private List<DXCheckbox> pendingOrgs;

	// Currently selected user
	private User searchUser;

	/* Methods */
	public AdminTools()
	{
		BackgroundColor = DXColors.Light4;
		
		Padding = 22;
		Margin = 0;
		
		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;
		
		Orientation = ScrollOrientation.Vertical;

		// Main layout
		layout = new DXVerticalLayout
		{
			Padding = 0,
			Spacing = 0,
			
			Horizontal = LayoutOptions.Fill,
			Vertical = LayoutOptions.Fill
		};

		// Always starts with search
		AddSearch();

		Content = layout;
	}

	/* Life Cycle */

	// Refreshes admin display for currently selected user
	private async Task Refresh()
	{
		Clear();

		await AddUser( searchUser );
	}

	// Removes all layouts
	private void Clear()
	{
		layout.Remove( info );
		layout.Remove( history );
		layout.Remove( update );
		layout.Remove( organization );
		layout.Remove( session );
	}

	// Initializes specified admin control
	private static void InitControl( DXFormControl control )
	{
		control.Init();
		control.SetState( DXFormControl.ControlState.Normal );
	}

	/* Add */

	// Adds controls for searching user to admin
	private void AddSearch()
	{
		// Textfield
		searchCtl = new DXTextField
		{
			Key = "search",
			Title = "login.email",
			Text = null,
			MinLength = 3,
			MaxLength = 64,
			Type = DXTextField.TextType.Email,
			Help = null,

			ControlChanged = OnSearchChanged
		};

		InitControl( searchCtl );

		// Button
		searchBtn = new DXButton
		{
			Text = "SEARCH",
			Type = DXButton.ButtonType.Action,
			ButtonWd = 80,

			Horizontal = LayoutOptions.Start,
			Vertical = LayoutOptions.Center,

			IsSticky = true,
			IsDisabled = true,

			ButtonTapped = OnSearchTapped
		};

		searchBtn.Init();
		
		// Add components
		layout.Add( searchCtl );
		layout.Add( searchBtn );
	}

	// Adds all controls used for admin of specified user
	private async Task AddUser( User user )
	{
		searchUser = user;
		
		// General info
		AddInfo();

		// Purchase history
		if ( PurchaseEngine.HasPurchased( user ) )
		{
			AddHistory();
		}

		// Update version, role, trial
		AddUpdate();

		// Organization list
		await AddOrganization();

		// Session history
		await AddSession();
	}

	// Adds label for individual cell to specified grid layout
	private static void AddLabel( DXGridLayout grid, string text, int column, bool bold, bool center = false )
	{
		DXLabel label = new()
		{
			Text = text,
			Font = bold ? DXFonts.RobotoBold : DXFonts.Roboto,
			FontSize = HistoryHt,
			HAlign = center ? TextAlignment.Center : TextAlignment.Start
		};

		grid.Add( label, column, grid.RowIndex );
	}
	
	// INFO

	// Adds all controls for displaying general user info
	private void AddInfo()
	{
		// Layout
		info = new DXGridLayout
		{
			Padding = new Thickness( 0, LayoutSpacing, 0, 0  ),
			RowSpacing = 8,
			ColumnSpacing = 0
		};

		// 2 columns
		info.AddStarColumn( 40 );		// 0: header
		info.AddStarColumn( 60 );		// 1: value

		// Created
		AddInfoRow( "Created", DXUtils.LabelFromDate( searchUser.Created ) );

		// Purchase info
		AddInfoRow( "Purchased", PurchaseEngine.GetPurchased( searchUser ) );
		AddInfoRow( "Used", PurchaseEngine.GetUsed( searchUser ) );
		AddInfoRow( "Remaining", PurchaseEngine.GetRemaining( searchUser ) );

		layout.Add( info );
	}

	// Adds header/value row to user info table
	private void AddInfoRow( string header, int value )
	{
		AddInfoRow( header, value.ToString() );
	}

	// Adds header/value row to user info table
	private void AddInfoRow( string header, string value )
	{
		info.AddFixedRow( InfoHt );

		// Add components
		AddLabel( info, header, 0, true );
		AddLabel( info, value, 1, false );
	}

	// HISTORY

	// Displays purchase history table for specified user
	private void AddHistory()
	{
		// Layout
		history = new DXGridLayout
		{
			Padding = DXUtils.Top( LayoutSpacing ),
			RowSpacing = 10,
			ColumnSpacing = 0
		};

		// 4 columns
		history.AddStarColumn( 40 );		// 0: date
		history.AddStarColumn( 20 );		// 1: count
		history.AddStarColumn( 20 );		// 2: used
		history.AddStarColumn( 20 );		// 3: admin

		// Header
		AddHistoryRow( "Date", "Count", "Used", "Admin", true );

		List<Purchase> purchases = searchUser.Purchases.OrderByDescending( p => p.Created ).ToList();
		
		// Newest purchase first
		foreach ( Purchase purchase in purchases )
		{
			string date = DXUtils.LabelFromDate( purchase.Created );
			string count = purchase.Count.ToString();
			string used = purchase.Used.ToString();
			string admin = (purchase.ProductId == Purchase.TeamSeasonsAdminId) ? "X" : "-";
		
			// Date, Count, Used, Admin
			AddHistoryRow( date, count, used, admin, false );
		}

		layout.Add( history );
	}

	// Adds individual row in purchase history table
	private void AddHistoryRow( string date, string count, string used, string admin, bool header )
	{
		history.AddFixedRow( HistoryHt );

		// Date, Count, Used, Admin
		AddLabel( history, date, 0, header );
		AddLabel( history, count, 1, header, true );
		AddLabel( history, used, 2, header, true );
		AddLabel( history, admin, 3, header, true );
	}

	// UPDATE

	// Adds all controls for updating individual user fields
	private void AddUpdate()
	{
		// Layout
		update = new DXGridLayout
		{
			Padding = DXUtils.Top( LayoutSpacing ),
			RowSpacing = 10,
			ColumnSpacing = 20
		};

		// 2 columns
		update.AddStarColumn( 70 );			// 0: field
		update.AddStarColumn( 30 );			// 1: button

		// Add each field
		AddMigrate();
		AddVersion();
		AddLevel();
		AddRole();
		AddPurchase();

		layout.Add( update );
	}

	// Adds account Migrate controls
	private void AddMigrate()
	{
		// Textfield
		migrateCtl = new DXTextField
		{
			Key = "migrate",
			TitleRaw = "New Email",
			Text = null,
			MinLength = 3,
			MaxLength = 64,
			Type = DXTextField.TextType.Email,
			Help = null,
				
			ControlChanged = OnMigrateChanged
		};

		InitControl( migrateCtl );

		// Update
		migrateBtn = new DXButton
		{
			Text = "UPDATE",
			Type = DXButton.ButtonType.Neutral,

			Horizontal = LayoutOptions.Start,
			Vertical = LayoutOptions.Center,

			IsDisabled = true,
			IsSticky = true,
		
			ButtonWd = UpdateBtnWd,
			ButtonTapped = OnMigrateTapped
		};

		migrateBtn.Init();
	
		update.AddFixedRow( UpdateHt );

		int row = update.RowIndex;

		// Add components
		update.Add( migrateCtl, 0, row );
		update.Add( migrateBtn, 1, row );
	}

	// Adds Version editing controls
	private void AddVersion()
	{
		// Version
		versionCtl = new DXStepper
		{
			Key = "version",
			Title = "settings.version",
			Number = searchUser.Version ?? 0,
			MinValue = 1,
			MaxValue = 99,
			Step = 1,
			IsLooping = true,
			Help = null,

			ControlChanged = () => { versionBtn.IsDisabled = false; }
		};

		InitControl( versionCtl );

		// Update
		versionBtn = new DXButton
		{
			Text = "UPDATE",
			Type = DXButton.ButtonType.Neutral,

			Horizontal = LayoutOptions.Start,
			Vertical = LayoutOptions.Center,

			IsDisabled = true,
			IsSticky = true,
			
			ButtonWd = UpdateBtnWd,
			ButtonTapped = OnVersionTapped
		};

		versionBtn.Init();
		
		update.AddFixedRow( UpdateHt );

		int row = update.RowIndex;

		// Add components
		update.Add( versionCtl, 0, row );
		update.Add( versionBtn, 1, row );
	}

	// Adds access Level editing controls
	private void AddLevel()
	{
		// Level
		levelCtl = new DXStepper
		{
			Key = "level",
			Title = "tools.level",
			Number = searchUser.LevelEnum,
			MinValue = -1,
			MaxValue = 5,
			Step = 1,
			IsLooping = true,
			Help = null,

			ControlChanged = () => { levelBtn.IsDisabled = false; }
		};

		InitControl( levelCtl );

		// Update
		levelBtn = new DXButton
		{
			Text = "UPDATE",
			Type = DXButton.ButtonType.Neutral,

			Horizontal = LayoutOptions.Start,
			Vertical = LayoutOptions.Center,

			IsDisabled = true,
			IsSticky = true,

			ButtonWd = UpdateBtnWd,
			ButtonTapped = OnLevelTapped
		};

		levelBtn.Init();
		
		update.AddFixedRow( UpdateHt );

		int row = update.RowIndex;

		// Add components
		update.Add( levelCtl, 0, row );
		update.Add( levelBtn, 1, row );
	}

	// Adds Role editing controls
	private void AddRole()
	{
		// Role
		roleCtl = new DXSelectorField
		{
			Key = "role",
			Title = "create.role",
			Items = "searchUser.role",
			SelectedItem = searchUser.Role,
			HideClear = true,
			Help = null,

			ControlChanged = () => { roleBtn.IsDisabled = false; }
		};

		InitControl( roleCtl );

		// Update
		roleBtn = new DXButton
		{
			Text = "UPDATE",
			Type = DXButton.ButtonType.Neutral,

			Horizontal = LayoutOptions.Start,
			Vertical = LayoutOptions.Center,

			IsDisabled = true,
			IsSticky = true,

			ButtonWd = UpdateBtnWd,
			ButtonTapped = OnRoleTapped
		};

		roleBtn.Init();
		
		update.AddFixedRow( UpdateHt );

		int row = update.RowIndex;

		// Add components
		update.Add( roleCtl, 0, row );
		update.Add( roleBtn, 1, row );
	}

	// Adds Team-Season admin purchase controls
	private void AddPurchase()
	{
		// Purchase
		purchaseCtl = new DXKeypadField
		{
			Key = "purchase",
			Title = "purchase.units",
			Number = 1,
			MinValue = 1,
			MaxValue = 99,
			Help = null
		};

		InitControl( purchaseCtl );

		// Add
		purchaseBtn = new DXButton
		{
			Text = "ADD",
			Type = DXButton.ButtonType.Neutral,

			Horizontal = LayoutOptions.Start,
			Vertical = LayoutOptions.Center,

			IsSticky = true,
			ButtonWd = UpdateBtnWd,
			ButtonTapped = OnPurchaseTapped
		};

		purchaseBtn.Init();
		
		update.AddFixedRow( UpdateHt );

		int row = update.RowIndex;

		// Add components
		update.Add( purchaseCtl, 0, row );
		update.Add( purchaseBtn, 1, row );
	}

	// ORG

	// Displays organization list for specified user
	private async Task AddOrganization()
	{
		pendingOrgs = [];

		// Layout
		organization = new DXGridLayout()
		{
			Padding = DXUtils.Top( LayoutSpacing ),
			RowSpacing = 0,
			ColumnSpacing = 0
		};

		// 3 columns
		organization.AddStarColumn( 60 );        // 0: name
		organization.AddStarColumn( 20 );        // 1: teams
		organization.AddStarColumn( 20 );        // 2: debug

		// Header
		AddOrgHeader();

		// Query org list
		await searchUser.Populate( false );

		// Alpha sort
		List<Organization> organizations = searchUser.Organizations.OrderBy( o => o.Name ).ToList();

		// Display row for each org (except sample)
		foreach ( Organization org in organizations )
		{
			if ( !org.IsSample )
			{
				AddOrgRow( org );
			}
		}

		// Update
		orgsBtn = new DXButton()
		{
			Text = "UPDATE",
			Type = DXButton.ButtonType.Neutral,

			Horizontal = LayoutOptions.End,
			Vertical = LayoutOptions.Center,

			IsDisabled = true,
			IsSticky = true,

			ButtonWd = UpdateBtnWd,
			ButtonTapped = OnOrgTapped
		};

		orgsBtn.Init();
		
		organization.AddFixedRow( UpdateHt );
		organization.Add( orgsBtn, 0, organization.RowIndex, 3, 1 );

		layout.Add( organization );
	}

	// Displays header row for session table
	private void AddOrgHeader()
	{
		organization.AddFixedRow( OrgsHt );

		// Name, Teams, Debug
		AddLabel( organization, "Organization", 0, true, false );
		AddLabel( organization, "Teams", 1, true, true );
		AddLabel( organization, "Debug", 2, true, true );
	}

	// Displays individual row in organization table
	private void AddOrgRow( Organization org )
	{
		organization.AddFixedRow( OrgsHt );

		string name = org.Name;
		string teams = org.Teams.Count.ToString();

		// Name, Teams
		AddLabel( organization, name, 0, false, false );
		AddLabel( organization, teams, 1, false, true );

		bool debug = org.IsDebug;

		// Debug
		DXCheckbox checkbox = new()
		{
			IsChecked = debug,
			Data = org,

			Margin = new Thickness( 8, -12, 0, 0 ),
			Horizontal = LayoutOptions.Center,
			Vertical = LayoutOptions.Center,

			ValueChanged = OnOrgChanged
		};

		organization.Add( checkbox, 2, organization.RowIndex );
	}

	// SESSION

	// Displays session history table for specified user
	private async Task AddSession()
	{
		// Layout
		session = new DXGridLayout()
		{
			Padding = DXUtils.Top( LayoutSpacing ),
			RowSpacing = 10,
			ColumnSpacing = 0
		};

		// 4 columns
		session.AddStarColumn( 25 );        // 0: date
		session.AddStarColumn( 25 );        // 1: version
		session.AddStarColumn( 25 );        // 2: platform
		session.AddStarColumn( 25 );        // 3: idiom

		// Header
		AddSessionRow( "Session", "Version", "Platform", "Idiom", true );

		List<Session> sessions = await Session.Read( searchUser.UniqueId, 5 );

		// Newest session first
		foreach ( Session s in sessions )
		{
			string date = DXUtils.LabelFromDate( s.Created );
			string version = s.AppVersion;
			string platform = s.Platform;
			string idiom = s.Idiom;

			// Date, Version, Platform, Idiom
			AddSessionRow( date, version, platform, idiom, false );
		}

		layout.Add( session );
	}

	// Adds individual row in session history table
	private void AddSessionRow( string date, string version, string platform, string idiom, bool header )
	{
		session.AddFixedRow( SessionHt );

		// Date, Count, Used, Admin
		AddLabel( session, date, 0, header );
		AddLabel( session, version, 1, header, true );
		AddLabel( session, platform, 2, header, true );
		AddLabel( session, idiom, 3, header, true );
	}

	/* Event Callbacks */

	// SEARCH

	// Updates search button state on field edit
	private void OnSearchChanged()
	{
		searchBtn.IsDisabled = !searchCtl.IsValid();
	}

	// Admin searching for user
	private async void OnSearchTapped( object sender )
	{
		DXSpinner.Start();
		
		Clear();

		string username = searchCtl.Text;

		// Search
		User user = await User.ReadByUsername( username );

		// No match found
		if ( user == null )
		{
			DXAlert.ShowOkRaw( "Admin Error", $"User '{username}' not found.", () => { searchBtn.Reset(); } ); 
		}
		// Success
		else
		{
			await AddUser( user );

			searchBtn.Reset();
		}

		DXSpinner.Stop();
	}
	
	// UPDATE
	
	// Disables migrate button if either old/new email invalid 
	private void OnMigrateChanged()
	{
		migrateBtn.IsDisabled = !searchCtl.IsValid() || !migrateCtl.IsValid();	
	}

	// Starts account migration from old to new email address
	private async void OnMigrateTapped( object sender )
	{
		DXSpinner.Start();

		string newEmail = migrateCtl.Text;
		
		// Search new user
		User newUser = await User.ReadByUsername( newEmail );

		// New account not created
		if ( newUser == null )
		{
			DXAlert.ShowOkRaw( "Migrate Error", $"New account '{newEmail}' not found.", ResetMigrate );
			return;
		}
		
		// New account should be empty
		if ( newUser.Permissions.Count > 0 )
		{
			DXAlert.ShowOkCancelRaw( "Migrate Error", $"New account '{newEmail}' already has Permissions. Proceed anyway?", async void () => { await Migrate( newUser ); }, ResetMigrate );
			return;
		}
		
		// Confirm
		DXAlert.ShowOkCancelRaw( "Migrate Account", $"Proceed with migration to {newEmail}?", async void () => { await Migrate( newUser ); }, ResetMigrate );
	}

	// Handles migration from old account to specified new account
	private async Task Migrate( User newUser )
	{
		try
		{
			User oldUser = searchUser;
			
			// Copy migration fields
			oldUser.AuthId = newUser.AuthId;
			oldUser.Username = newUser.Username;
			oldUser.FirstName = newUser.FirstName;
			oldUser.LastName = newUser.LastName;
	
			// Persist
			await oldUser.Update();
			
			// New user no longer needed
			await newUser.Delete();
			
			// Success
			DXAlert.ShowOkRaw( "Migrate Account", "Account migration was successful", ResetMigrate );

			searchCtl.Text = newUser.Username;
			
			// Display new account info
			OnSearchTapped( null );
		}
		catch ( Exception ex )
		{
			DXLog.Exception( "admin.migrate", ex );
			DXAlert.ShowError( "Migrate", $"Account migration failed: {ex.Message}", ResetMigrate );
		}
	}

	// Cancels migration process
	private void ResetMigrate()
	{
		migrateBtn.Reset();

		DXSpinner.Stop();
	}
	
	// Admin updating data version for user
	private async void OnVersionTapped( object sender )
	{
		DXSpinner.Start();

		byte version = (byte) versionCtl.Number;

		// Persist
		await searchUser.UpdateVersion( version );

		// Update display
		await Refresh();

		DXSpinner.Stop();
	}

	// Admin updating access level for user
	private async void OnLevelTapped( object sender )
	{
		DXSpinner.Start();

		User.LevelType level = (User.LevelType) levelCtl.Number;

		// Persist
		await searchUser.UpdateLevel( level );

		// Update display
		await Refresh();

		DXSpinner.Stop();
	}

	// Admin updating role for user
	private async void OnRoleTapped( object sender )
	{
		DXSpinner.Start();

		string role = roleCtl.GetString();

		// Persist
		await searchUser.UpdateRole( role );

		// Update display
		await Refresh();

		DXSpinner.Stop();
	}

	// Admin adding Team-Season(s) purchase for user
	private void OnPurchaseTapped( object sender )
	{
		string username = searchUser.Username;
		int count = (int) purchaseCtl.Number!;

		string msg = $"Add {count} Team-Season(s) for user '{username}'?";

		// Require confirmation
		DXAlert.ShowPositiveCancelRaw( "Add Purchase", msg, "tools.add", OnPurchaseConfirmed, () => { purchaseBtn.Reset(); } );
	}

	// Admin confirmed adding Team-Season(s)
	private async void OnPurchaseConfirmed()
	{
		DXSpinner.Start();

		int count = (int) purchaseCtl.Number!;

		// Persist
		await PurchaseEngine.Record( searchUser, Purchase.TeamSeasonsAdminId, null, count );

		// Update display
		await Refresh();

		DXSpinner.Stop();
	}

	// ORGS

	// Admin (un)checked org checkbox
	private void OnOrgChanged( DXCheckbox checkbox )
	{
		pendingOrgs.Add( checkbox );

		orgsBtn.IsDisabled = false;
	}

	// Admin tapped org Update button
	private async void OnOrgTapped( object sender )
	{
		DXSpinner.Start();

		// Process all pending updates
		foreach ( DXCheckbox pending in pendingOrgs )
		{
			if ( pending.Data is Organization org )
			{
				bool debug = (bool) pending.IsChecked!;

				// Persist
				await org.UpdateDebug( debug );
			}
		}

		// Update display
		await Refresh();

		DXSpinner.Stop();
	}
}

//
