UI/SpawnMenu/Spawnlists/SpawnlistData.cs
namespace Sandbox;

using Sandbox.UI;
using System.Text.Json.Serialization;

/// <summary>
/// A spawnlist item -- lots of cleanup needed, docs, etc
/// </summary>
public class SpawnlistItem
{
	[JsonPropertyName( "ident" )]
	public string Ident { get; set; }

	[JsonPropertyName( "title" )]
	public string Title { get; set; }

	[JsonPropertyName( "icon" )]
	public string Icon { get; set; }

	public static string MakeIdent( string type, string path, string source = "local" )
	{
		// TODO: hate this special case
		if ( type == "dupe" )
			return $"dupe.{source}:{path}";

		return $"{type}:{path}";
	}

	public static (string Type, string Path, string Source) ParseIdent( string ident )
	{
		if ( string.IsNullOrEmpty( ident ) )
			return (null, null, "local");

		var colonIndex = ident.IndexOf( ':' );
		if ( colonIndex < 0 )
			return (ident, ident, "local");

		var prefix = ident[..colonIndex];
		var data = ident[(colonIndex + 1)..];

		// TODO: hate this special case
		if ( prefix.StartsWith( "dupe." ) )
		{
			var source = prefix["dupe.".Length..];
			return ("dupe", data, source);
		}

		return (prefix, data, "local");
	}
}

public class SpawnlistData
{
	/// <summary>
	/// Raised whenever a new spawnlist is created, so UI can refresh without needing a panel ancestor walk.
	/// </summary>
	public static event Action SpawnlistCreated;

	[JsonPropertyName( "name" )]
	public string Name { get; set; } = "#spawnmenu.spawnlist.untitled";

	[JsonPropertyName( "description" )]
	public string Description { get; set; } = "";

	[JsonPropertyName( "items" )]
	public List<SpawnlistItem> Items { get; set; } = new();

	public static SpawnlistData Create( string name )
	{
		var data = new SpawnlistData { Name = name };
		var entry = Storage.CreateEntry( "spawnlist" );
		entry.SetMeta( "name", name );
		Save( entry, data );
		SpawnlistCreated?.Invoke();
		return data;
	}

	public static void Save( Storage.Entry entry, SpawnlistData data )
	{
		entry.Files.WriteJson( "/spawnlist.json", data );
		entry.SetMeta( "name", data.Name );
		entry.SetMeta( "item_count", data.Items.Count.ToString() );
	}

	public static SpawnlistData Load( Storage.Entry entry )
	{
		if ( !entry.Files.FileExists( "/spawnlist.json" ) )
			return new SpawnlistData { Name = entry.GetMeta<string>( "name" ) ?? "#spawnmenu.spawnlist.untitled" };

		return entry.Files.ReadJson<SpawnlistData>( "/spawnlist.json" )
			?? new SpawnlistData { Name = "#spawnmenu.spawnlist.untitled" };
	}

	public static IEnumerable<Storage.Entry> GetAll()
	{
		return Storage.GetAll( "spawnlist" ).OrderByDescending( x => x.Created );
	}

	public static void Rename( Storage.Entry entry, string newName )
	{
		var data = Load( entry );
		data.Name = newName;
		Save( entry, data );
	}

	public static void Delete( Storage.Entry entry )
	{
		entry.Delete();
	}

	public static void Publish( Storage.Entry entry )
	{
		var options = new Modals.WorkshopPublishOptions { Title = "#spawnmenu.spawnlist.publish_title" };
		entry.Publish( options );
	}

	public static void AddItem( Storage.Entry entry, SpawnlistItem item )
	{
		var data = Load( entry );
		data.Items.Add( item );
		Save( entry, data );
	}

	public static void RemoveItem( Storage.Entry entry, int index )
	{
		var data = Load( entry );
		if ( index >= 0 && index < data.Items.Count )
		{
			data.Items.RemoveAt( index );
			Save( entry, data );
		}
	}

	public static void PopulateContextMenu( MenuPanel menu, SpawnlistItem item, Storage.Entry skipEntry = null )
	{
		var entries = GetAll()
			.Where( e => skipEntry is null || e.Id != skipEntry.Id )
			.ToList();

		if ( entries.Count > 0 )
		{
			menu.AddSubmenu( "📋", "#spawnmenu.spawnlist.add_to_submenu", sub =>
			{
				foreach ( var entry in entries )
				{
					var data = Load( entry );
					var capturedEntry = entry;
					sub.AddOption( "📋", data.Name, () => AddItem( capturedEntry, item ) );
				}
			} );

			menu.AddSpacer();
		}

		menu.AddOption( "➕", "#spawnmenu.spawnlist.create_new_option", () =>
		{
			Create( item.Title ?? "New Spawnlist" );
			var created = GetAll().FirstOrDefault();
			if ( created is not null )
				AddItem( created, item );
		} );

		var (type, path, source) = SpawnlistItem.ParseIdent( item.Ident );
		var spawner = ISpawner.Create( type, path, source );
		var fullIdent = spawner?.FullIdent;

		if ( !string.IsNullOrEmpty( fullIdent ) )
		{
			menu.AddOption( "🌐", "sbox.game", () =>
			{
				Game.Overlay.ShowPackageModal( fullIdent );
			} );
		}
	}
}