Editor/States/Changelog/ChangelogPreview.cs
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Editor;
using Sandbox;
using Sandbox.Diagnostics;

namespace Changelog.States.Changelog;

public sealed class ChangelogPreview : Widget
{
	private TextEdit TextEdit;
	private Button CopyBtn;
	private Button PublishBtn;
	
	private readonly Logger Log = new( "Changelog" );
	
	public ChangelogPreview( Widget parent = null ) : base( parent )
	{
		Layout = Layout.Column();

		TextEdit = new TextEdit( this )
		{
			PlaceholderText = "No changes yet",
			ReadOnly = true,
		};

		Layout.Add( TextEdit, 3 );

		var buttons = new Widget( this );
		buttons.Layout = Layout.Row();

		CopyBtn = new Button( "Copy", "copy_all", buttons )
		{
			Clicked = Copy,
			MouseRightPress = CopyMenu,
			ToolTip = "Copies the above changelog\n(right click: copy as format)",
			Tint = Theme.Primary,
		};

		PublishBtn = new Button( "Publish", "publish", buttons )
		{
			Clicked = Publish,
			ToolTip = "Copies the changelog and marks all non-hidden commits as published",
			Tint = Theme.Primary,
		};
		
		buttons.Layout.Add( CopyBtn );
		buttons.Layout.Add( PublishBtn );

		Layout.Add( buttons );
		
		UpdatePreview();
	}

	private void Copy() => Copy( true );
	private async void Copy( bool show )
	{
		EditorUtility.Clipboard.Copy( TextEdit.PlainText );
		
		if ( show ) {
			CopyBtn.Icon = "done";
			CopyBtn.Tint = Theme.Green.Darken( 0.5f );
		}

		await Task.Delay( 300 );
		if ( !show ) return;
		
		CopyBtn.Icon = "copy_all";
		CopyBtn.Tint = Theme.Primary;
	}

	private async void Publish()
	{
		Copy( false );

		if ( TryPublish() ) {
			GetAncestor<ChangelogWidget>()?.CommitListContainer.Toolbar.MarkLatest( null );
			PublishBtn.Icon = "done";
			PublishBtn.Tint = Theme.Green.Darken( 0.5f );
		}
		else {
			PublishBtn.Icon = "error_outline";
			PublishBtn.Tint = Theme.Red.Darken( 0.5f );
		}
		await Task.Delay( 300 );
		PublishBtn.Icon = "publish";
		PublishBtn.Tint = Theme.Primary;
	}

	private bool TryPublish()
	{
		if ( string.IsNullOrEmpty( TextEdit.PlainText ) ) {
			Log.Error( "No changes to publish" );
			return false;
		}
		
		var project = Project.Current;
		if ( project is null ) {
			Log.Error( "Current project is null, copied to clipboard only" );
			return false;
		}
		
		if ( project.Config.IsStandaloneOnly ) {
			Log.Error( "Enable whitelist in project settings to publish" );
			return false;
		}
		
		if ( project.Package.Org.Ident == "local" ) {
			Log.Error( "Select an organisation in project settings" );
			return false;
		}

		Log.Info("Changelog has been copied to your clipboard!");
		return true;
	}

	public void UpdatePreview( CommitListContainer list = null )
	{
		list ??= GetAncestor<ChangelogWidget>()?.CommitListContainer;

		TextEdit.PlainText = GeneratePreview( list );
	}

	private string GeneratePreview( CommitListContainer list )
	{
		var output = new StringBuilder();
		
		foreach (var commit in GetChanges( list )) {
			output.AppendLine( $" - {commit}" );
		}

		return output.ToString();
	}

	private IEnumerable<string> GetChanges( CommitListContainer list = null )
	{
		list ??= GetAncestor<ChangelogWidget>()?.CommitListContainer;
		
		return list?.Commits
			.Where( x => !( x.IsHidden || x.IsPublished ) )
			.Select( x => x.Name );
	}

	private void CopyMenu()
	{
		var menu = new ContextMenu();
		menu.AddHeading( "Copy As ..." );
		menu.AddOption( "Text / Markdown", "description", Copy );
		menu.AddOption( "HTML Bullets", "code", CopyHtml);
		menu.AddOption( "JS-style Array", "data_array", CopyArray );
		menu.OpenAtCursor( true );
	}

	private void CopyHtml()
	{
		var output = new StringBuilder( "<ul>\n" );

		foreach ( var change in GetChanges() ) {
			output.AppendLine( $"<li>{HttpUtility.HtmlEncode( change )}</li>" );
		}

		output.Append( "</ul>" );
		
		Copy( true );
		EditorUtility.Clipboard.Copy( output.ToString() );
	}

	private void CopyArray()
	{
		var output = new StringBuilder( "[\n" );

		foreach ( var change in GetChanges() ) {
			output.AppendLine( $"\"{HttpUtility.JavaScriptStringEncode( change )}\"," );
		}

		output.Append( "]" );
		
		Copy( true );
		EditorUtility.Clipboard.Copy( output.ToString() );
	}
}