using Editor.Wizards;

namespace Editor.LibraryManager;

class LibraryList : ListView
{
	public Action<Package> OnLibrarySelected { get; set; }
	public Action OnListLoaded { get; set; }
	public Package SelectedLibrary { get; private set; }

	public bool ShowInstalled { get; set; }
	public bool ShowAvailable { get; set; }

	public string Filter { get; set; } = "";
	public string Query { get; set; } = "";
	public string SortMode { get; set; } = "updated";
	public Package.FindResult LastResult { get; set; }

	public LibraryList( Widget parent ) : base( parent )
	{
		ItemContextMenu = ShowItemContext;
		ItemSelected = OnItemClicked;

		ItemSize = new Vector2( -1, 64 );
	}

	public void OnItemClicked( object value )
	{
		if ( value is LibraryProject lib )
		{
			OnLibrarySelected?.Invoke( lib.Project.Package );
			SelectedLibrary = lib.Project.Package;
		}

		if ( value is Package p )
		{
			OnLibrarySelected?.Invoke( p );
			SelectedLibrary = p;
		}
	}

	private void ShowItemContext( object obj )
	{
		LibraryProject project = obj as LibraryProject;
		Package package = obj as Package ?? project?.Project.Package;

		var m = new ContextMenu();

		if ( package is not null && package.Org.Ident != "local" )
		{
			m.AddOption( "View in Browser", "public", () => EditorUtility.OpenFolder( package.Url ) );
			m.AddSeparator();
		}

		if ( project is not null )
		{
			m.AddOption( "Project Properties", "tune", () => ProjectInspector.OpenForProject( project.Project ) );
			m.AddSeparator();
			m.AddOption( "Publish Project", "upload_file", () => PublishWizard.Open( project.Project ) );
			m.AddSeparator();
			m.AddOption( "Show in Explorer", "folder", () => EditorUtility.OpenFolder( project.Project.RootDirectory.FullName ) );
		}

		m.OpenAtCursor();
	}

	protected override void PaintItem( VirtualWidget item )
	{
		if ( item.Object is LibraryProject c )
			PaintItem( item, c.Project.Package );

		if ( item.Object is Package p )
			PaintItem( item, p );
	}

	private void PaintItem( VirtualWidget item, Package c )
	{
		var rect = item.Rect;

		if ( Paint.HasPressed )
			rect = rect.Shrink( 2, 2, 0, 0 );

		var library = LibrarySystem.All.Where( x => x.Project.Package.Ident == c.Ident && x.Project.Package.Org.Ident == c.Org.Ident ).FirstOrDefault();

		var pen = Theme.Text.WithAlpha( 0.6f );

		if ( Paint.HasMouseOver || Paint.HasSelected )
		{
			Paint.SetBrush( Theme.Primary.WithAlpha( Paint.HasMouseOver ? 0.8f : 0.5f ) );
			Paint.ClearPen();
			Paint.DrawRect( rect, 4 );

			pen = Theme.Text.WithAlpha( Paint.HasMouseOver ? 1 : 0.9f );
			Paint.ClearBrush();
		}

		rect = rect.Shrink( 4 );

		// Icon
		{
			var iconRect = rect;
			iconRect.Width = iconRect.Height;
			iconRect = iconRect.Shrink( 8 );

			Paint.SetBrushAndPen( Theme.Border.WithAlpha( 0.4f ) );

			if ( !string.IsNullOrWhiteSpace( c.Thumb ) && !c.Thumb.StartsWith( "/" ) )
			{
				Paint.Draw( iconRect, c.Thumb );
			}
			else
			{
				Paint.DrawRect( iconRect, 4 );
			}

			rect.Left = iconRect.Right + 8;
		}

		//header
		{
			rect.Top += 8;

			Paint.SetHeadingFont( 11, 450 );
			Paint.Pen = pen;
			var r = Paint.DrawText( rect, c.Title, TextFlag.LeftTop );

			r.Left = r.Right + 8;
			r.Right = rect.Right;
			r.Bottom -= 1;

			rect.Top = r.Bottom + 4;

			var installedVersion = library is not null ? $"• v{library?.Version?.Build}" : "";

			Paint.Pen = pen.WithAlphaMultiplied( 0.6f );
			Paint.SetDefaultFont();
			var drawnRect = Paint.DrawText( r, $"{c.Org.Title} {installedVersion}", TextFlag.LeftBottom );
			if ( !string.IsNullOrEmpty( installedVersion ) )
			{
				Paint.DrawIcon( Rect.FromPoints( drawnRect.TopRight, r.BottomRight ), "download_done", 12, TextFlag.LeftCenter );
			}
		}

		// body text
		{
			Paint.Pen = pen.WithAlphaMultiplied( 0.6f );
			Paint.SetDefaultFont();
			Paint.DrawText( rect, c.Summary, TextFlag.LeftTop );
		}
	}

	protected override void OnPaint()
	{
		Paint.ClearPen();
		Paint.SetBrush( Theme.ControlBackground );
		Paint.DrawRect( LocalRect, 4 );

		Paint.Antialiasing = true;
		Paint.TextAntialiasing = true;

		base.OnPaint();
	}

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

		if ( SetContentHash( GetContentHash(), 0.5f ) )
		{
			_ = UpdateAsync();
		}
	}

	async Task UpdateAsync()
	{
		if ( ShowInstalled )
		{
			// Get installed libraries, but fetch their cloud information as well.
			var packages = LibrarySystem.All.Where( x => string.IsNullOrEmpty( Filter ) || x.Project.Config.Title.Contains( Filter, StringComparison.InvariantCultureIgnoreCase ) );
			SetItems( packages );

			var firstLocalPackage = packages.FirstOrDefault();
			if ( firstLocalPackage is not null )
			{
				SelectItem( firstLocalPackage );
			}

			OnListLoaded?.Invoke();
			return;
		}

		// Fetch available libraries from cloud
		var search = "";
		if ( !string.IsNullOrEmpty( Filter ) ) search = Filter + " ";
		var query = $"{search}type:library sort:{SortMode} {Query}".Trim();
		var result = await Package.FindAsync( query, 200, 0 );
		SetItems( result.Packages );

		var firstPackage = result.Packages.FirstOrDefault();
		if ( firstPackage is not null )
		{
			SelectItem( firstPackage );
		}

		LastResult = result;
		OnListLoaded?.Invoke();
	}

	int GetContentHash()
	{
		return HashCode.Combine( Filter, Query, SortMode );
	}

}
