Editor/sandmod.libraryplus/Widget/LibraryDetail.cs
using System.Linq;
using System.Threading.Tasks;
using Editor;
using Sandbox;
using Button = Editor.Button;
using Label = Editor.Label;

namespace LibraryPlus;

internal class LibraryDetail : Widget
{
	/// <summary>
	///     The "install" button
	/// </summary>
	private Button ActionButton;

	/// <summary>
	///     If this is installed, this will be the installed project
	/// </summary>
	private Library Installed;

	/// <summary>
	///     The package holding the description for this library
	/// </summary>
	private Package Package;

	/// <summary>
	///     The revision currently selected in the version list
	/// </summary>
	private Package.IRevision SelectedRevision;

	/// <summary>
	///     The "uninstall" button
	/// </summary>
	private Button UninstallButton;

	/// <summary>
	///     List of versions
	/// </summary>
	private ComboBox VersionList;

	public LibraryDetail( Package package )
	{
		Package = package;

		Layout = Layout.Column();
		Layout.Margin = 8;
		Layout.Spacing = 4;

		_ = FetchAndBuild();
	}

	private async Task FetchAndBuild()
	{
		if ( !this.IsValid() )
		{
			return;
		}

		Layout.Clear( true );

		if ( !Package.TryParseIdent( Package.FullIdent, out var ident ) )
		{
			return;
		}

		Installed = LibrarySystemPlus.All.FirstOrDefault( x =>
			x.Package.ReferenceIdent() == Package.ReferenceIdent() );

		// do we have this package installed? what is the version?

		if ( ident.org != "local" )
		{
			Package = await Package.FetchAsync( Package.ReferenceIdent(), false ) ?? Package;
			if ( !this.IsValid() )
			{
				return;
			}
		}

		// Header, todo, icon and title
		var titleLayout = Layout.Row();
		titleLayout.Alignment = TextFlag.Left;
		titleLayout.Spacing = 4;
		titleLayout.Add( new Label( Package.Title ) );

		if ( Package.Org.Ident != "local" )
		{
			// org icon and title
			titleLayout.Add( new Label.Small( $"by {Package.Org.Title}" ), 1 );
		}

		Layout.Add( titleLayout );

		Layout.Add( new Label.Small( Package.ReferenceIdent() ) );

		// all of the versions
		VersionList = new ComboBox( this );
		Layout.Add( VersionList );

		{
			ActionButton = new Button( "Install", this ) { Pressed = () => _ = OnActionPressed() };
			Layout.Add( ActionButton );
			ActionButton.Hide();
		}

		{
			UninstallButton = new Button( "Remove Library", "remove" )
			{
				Pressed = () => Dialog.AskConfirm( OnUninstall, $"Are you sure you want to remove {Package.Title}?",
					"Remove Library", "Uninstall" )
			};
			Layout.Add( UninstallButton );
			UninstallButton.Visible = Installed != null;
		}

		if ( Installed != null )
		{
			Layout.AddSpacingCell( 10 );
			var referenceLayout = Layout.Grid();
			referenceLayout.AddCell( 0, 0, new Label( "Reference" ) );
			referenceLayout.AddCell( 1, 0, new Label( "Version" ) );
			foreach ( var reference in Installed.Config.LibraryReferences.Select( ( entry, index ) =>
				         new { entry, index = index + 1 } ) )
			{
				referenceLayout.AddCell( 0, reference.index, new Label( reference.entry.Key ) );
				referenceLayout.AddCell( 1, reference.index, new Label(
					$">= {reference.entry.Value.Min}{(reference.entry.Value.Max != null ? $", <= {reference.entry.Value.Min}" : "")}" ) );
				var actionLayout = referenceLayout.AddCell( 2, reference.index, Layout.Row() );
				var editButton = actionLayout.Add( new IconButton( "edit" )
				{
					OnClick = () => ReferenceDialog.Open( "Edit Reference",
						data => LibrarySystemPlus.AddReference( Installed, data ),
						(reference.entry.Key, reference.entry.Value.Min, reference.entry.Value.Max) )
				} );
				editButton.ToolTip = "Edit Reference";
				var removeButton = actionLayout.Add( new IconButton( "remove" )
				{
					OnClick = () => Dialog.AskConfirm(
						() => LibrarySystemPlus.RemoveReference( Installed, reference.entry.Key ),
						$"Are you sure you want to remove {reference.entry.Key}?",
						"Remove Reference", "Remove" )
				} );
				removeButton.ToolTip = "Remove Reference";
			}

			var addButton = new Button( "Add Reference", "add" )
			{
				Pressed = () => ReferenceDialog.Open( "Add Reference",
					data => LibrarySystemPlus.AddReference( Installed, data ), "Add" )
			};
			referenceLayout.AddCell( 0, Installed.Config.LibraryReferences.Count + 1, addButton
				, 3 );

			Layout.Add( referenceLayout );
		}

		// information about this
		Layout.Add( new Label( Package.Description ) );

		Layout.AddStretchCell();

		if ( ident.org == "local" )
		{
			VersionList.Hide();
			return;
		}

		var versions = await Package.FetchVersions( $"{ident.org}.{ident.package}" );
		if ( !this.IsValid() )
		{
			return;
		}

		if ( versions != null )
		{
			if ( Installed != null )
			{
				SelectedRevision = versions.FirstOrDefault( x => x.VersionId == Installed.Version.Build );
			}

			SelectedRevision ??= versions.FirstOrDefault();

			foreach ( var v in versions )
			{
				var version = v;
				var subtitle = Installed != null && Installed.Version.Build == version.VersionId
					? " (Installed)"
					: "";
				VersionList.AddItem( $"{v.Created.DateTime} - {v.VersionId}{subtitle}", null,
					() => OnVersionSelected( v ), null, SelectedRevision == version );
			}

			OnVersionSelected( SelectedRevision );
		}
	}

	private void OnVersionSelected( Package.IRevision revision )
	{
		SelectedRevision = revision;
		// update action button - install/uninstall/update

		// ActionButton

		// Is this fucker installed?
		if ( Installed != null )
		{
			// is this the installed version?
			if ( revision.VersionId == Installed.Version.Build )
			{
				ActionButton.Hide();
			}
			else
			{
				ActionButton.Show();
				ActionButton.Icon = "get_app";
				ActionButton.Text = $"Update to {revision.VersionId}";
			}
		}
		else
		{
			ActionButton.Show();
			ActionButton.Icon = "get_app";
			ActionButton.Text = "Install";
		}
	}

	private async Task OnActionPressed()
	{
		LibrarySystemPlus.Install( Package.FullIdent, SelectedRevision.VersionId );

		// window closed
		if ( !IsValid )
		{
			return;
		}

		await FetchAndBuild();
	}

	private void OnUninstall()
	{
		UninstallButton.Enabled = false;
		Installed.Remove();
		_ = FetchAndBuild();
	}

	protected override Vector2 SizeHint()
	{
		return new Vector2( 300, 100 );
	}

	[EditorEvent.Frame]
	public void Frame()
	{
		if ( !Visible )
		{
			return;
		}

		SetContentHash( Installed?.GetHashCode() ?? 0 );
	}
}