Editor/CloudAssetPanel.cs
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace GeneralGame.Editor;

/// <summary>
/// Panel for browsing cloud assets from the S&box community as a tree view.
/// Shows categories with auto-loaded items and supports per-category search.
/// </summary>
public class CloudAssetPanel : Widget, IBrowserPanel
{
    private Widget _toolbar;
    private LineEdit _searchEdit;
    private IconButton _closeBtn;
    private IconButton _searchBtn;
    private IconButton _clearSearchBtn;
    private IconButton _refreshBtn;
    private IconButton _moveLeftBtn;
    private IconButton _moveRightBtn;
    private TreeView _treeView;
    private Label _statusLabel;

    private const int InitialItemsPerCategory = 10;
    private const int SearchItemsPerCategory = 20;
    private bool _isSearchMode = false;
    private string _currentSearchQuery = "";

    public Action OnCloseRequested { get; set; }
    public Action OnMoveLeftRequested { get; set; }
    public Action OnMoveRightRequested { get; set; }

    /// <summary>
    /// Called when a cloud category/folder is clicked (for syncing with preview panels)
    /// </summary>
    public Action<List<Package>> OnCloudAssetsLoaded;

    public bool ShowCloseButton
    {
        get => _closeBtn?.Visible ?? false;
        set
        {
            if (_closeBtn != null)
                _closeBtn.Visible = value;
        }
    }

    public bool ShowMoveLeftButton
    {
        get => _moveLeftBtn?.Visible ?? false;
        set
        {
            if (_moveLeftBtn != null)
                _moveLeftBtn.Visible = value;
        }
    }

    public bool ShowMoveRightButton
    {
        get => _moveRightBtn?.Visible ?? false;
        set
        {
            if (_moveRightBtn != null)
                _moveRightBtn.Visible = value;
        }
    }

    public CloudAssetPanel(Widget parent) : base(parent)
    {
        SetSizeMode(SizeMode.CanGrow, SizeMode.CanGrow);
        CreateUI();
        BuildCategoryTree();

        // Auto-load initial items for all categories
        _ = LoadAllCategoriesAsync();
    }

    private void CreateUI()
    {
        Layout = Layout.Column();
        Layout.Spacing = 4;
        Layout.Margin = 4;

        // Toolbar
        _toolbar = Layout.Add(new Widget(this));
        _toolbar.Layout = Layout.Row();
        _toolbar.Layout.Spacing = 4;
        _toolbar.FixedHeight = 28;

        // Cloud icon
        var cloudIcon = _toolbar.Layout.Add(new Label("cloud", this));
        cloudIcon.SetStyles("font-family: Material Icons; font-size: 16px; color: #888;");
        cloudIcon.FixedWidth = 20;

        // Search input
        _searchEdit = _toolbar.Layout.Add(new LineEdit(this));
        _searchEdit.PlaceholderText = "Search cloud...";
        _searchEdit.ReturnPressed += DoSearch;

        // Search button
        _searchBtn = _toolbar.Layout.Add(new IconButton("search"));
        _searchBtn.ToolTip = "Search";
        _searchBtn.Background = Color.Transparent;
        _searchBtn.OnClick = DoSearch;

        // Clear search button
        _clearSearchBtn = _toolbar.Layout.Add(new IconButton("close"));
        _clearSearchBtn.ToolTip = "Clear Search";
        _clearSearchBtn.Background = Color.Transparent;
        _clearSearchBtn.OnClick = ClearSearch;
        _clearSearchBtn.Visible = false;

        // Refresh button
        _refreshBtn = _toolbar.Layout.Add(new IconButton("refresh"));
        _refreshBtn.ToolTip = "Refresh All";
        _refreshBtn.Background = Color.Transparent;
        _refreshBtn.OnClick = RefreshAll;

        _toolbar.Layout.AddStretchCell();

        _moveLeftBtn = _toolbar.Layout.Add(new IconButton("chevron_left"));
        _moveLeftBtn.ToolTip = "Move Panel Left";
        _moveLeftBtn.Background = Color.Transparent;
        _moveLeftBtn.OnClick = () => OnMoveLeftRequested?.Invoke();
        _moveLeftBtn.Visible = false;

        _moveRightBtn = _toolbar.Layout.Add(new IconButton("chevron_right"));
        _moveRightBtn.ToolTip = "Move Panel Right";
        _moveRightBtn.Background = Color.Transparent;
        _moveRightBtn.OnClick = () => OnMoveRightRequested?.Invoke();
        _moveRightBtn.Visible = false;

        // Close button
        _closeBtn = _toolbar.Layout.Add(new IconButton("close"));
        _closeBtn.ToolTip = "Close Panel";
        _closeBtn.Background = Color.Transparent;
        _closeBtn.OnClick = () => OnCloseRequested?.Invoke();
        _closeBtn.Visible = false;

        // Tree view
        _treeView = Layout.Add(new TreeView(this));
        _treeView.SetSizeMode(SizeMode.CanGrow, SizeMode.CanGrow);
        _treeView.MultiSelect = false;
        _treeView.ItemSpacing = 1;
        _treeView.IndentWidth = 16;

        _treeView.ItemActivated += OnItemActivated;
        _treeView.OnSelectionChanged += OnSelectionChanged;

        // Status label at bottom
        _statusLabel = Layout.Add(new Label("Loading...", this));
        _statusLabel.SetStyles("color: #888; font-size: 10px; padding: 2px;");
        _statusLabel.FixedHeight = 18;
    }

    private List<CloudCategoryNode> _categoryNodes = new();

    private void BuildCategoryTree()
    {
        _treeView.Clear();
        _categoryNodes.Clear();

        // Add category folders - filter format: "type:model", "type:material", etc.
        var models = new CloudCategoryNode("model", "Models", "view_in_ar", this);
        var materials = new CloudCategoryNode("material", "Materials", "texture", this);
        var sounds = new CloudCategoryNode("sound", "Sounds", "audiotrack", this);
        var maps = new CloudCategoryNode("map", "Maps", "landscape", this);

        _categoryNodes.Add(models);
        _categoryNodes.Add(materials);
        _categoryNodes.Add(sounds);
        _categoryNodes.Add(maps);

        foreach (var node in _categoryNodes)
        {
            _treeView.AddItem(node);
        }

        _treeView.Update();
    }

    /// <summary>
    /// Load initial items for all categories on startup
    /// </summary>
    private async Task LoadAllCategoriesAsync()
    {
        _statusLabel.Text = "Loading categories...";
        int totalLoaded = 0;

        foreach (var categoryNode in _categoryNodes)
        {
            try
            {
                // Query format: "type:model", "type:material", etc.
                var query = $"type:{categoryNode.TypeFilter}";
                var result = await Package.FindAsync(query, InitialItemsPerCategory, 0);

                if (result?.Packages != null)
                {
                    categoryNode.SetPackagesAndRefresh(result.Packages.ToList());
                    totalLoaded += categoryNode.Packages.Count;
                }
            }
            catch (Exception ex)
            {
                Log.Warning($"[CloudAssetPanel] Failed to load {categoryNode.DisplayName}: {ex.Message}");
            }
        }

        _treeView.Update();
        _statusLabel.Text = $"Loaded {totalLoaded} cloud assets";
    }

    private async void DoSearch()
    {
        var query = _searchEdit.Text?.Trim() ?? "";
        if (string.IsNullOrEmpty(query))
        {
            ClearSearch();
            return;
        }

        _currentSearchQuery = query;
        _isSearchMode = true;
        _searchBtn.Enabled = false;
        _clearSearchBtn.Visible = true;
        _statusLabel.Text = $"Searching \"{query}\"...";

        try
        {
            int totalFound = 0;

            // Search in each category
            foreach (var categoryNode in _categoryNodes)
            {
                try
                {
                    // Combine search query with type filter
                    var searchQuery = $"{query} type:{categoryNode.TypeFilter}";
                    var result = await Package.FindAsync(searchQuery, SearchItemsPerCategory, 0);

                    if (result?.Packages != null && result.Packages.Any())
                    {
                        categoryNode.SetPackagesAndRefresh(result.Packages.ToList());
                        totalFound += result.Packages.Count();
                        // Auto-expand categories that have search results
                        _treeView.Open(categoryNode);
                    }
                    else
                    {
                        categoryNode.SetPackagesAndRefresh(new List<Package>());
                        // Collapse empty categories
                        _treeView.Close(categoryNode);
                    }
                }
                catch (Exception ex)
                {
                    Log.Warning($"Search error in {categoryNode.DisplayName}: {ex.Message}");
                }
            }

            _treeView.Update();
            _statusLabel.Text = $"Found {totalFound} results for \"{query}\"";

            // Notify with all found packages
            var allPackages = _categoryNodes.SelectMany(c => c.Packages).ToList();
            if (allPackages.Any())
            {
                OnCloudAssetsLoaded?.Invoke(allPackages);
            }
        }
        catch (Exception ex)
        {
            _statusLabel.Text = $"Search error: {ex.Message}";
            Log.Warning($"Cloud search error: {ex.Message}");
        }
        finally
        {
            _searchBtn.Enabled = true;
        }
    }

    private void ClearSearch()
    {
        _searchEdit.Text = "";
        _currentSearchQuery = "";
        _isSearchMode = false;
        _clearSearchBtn.Visible = false;

        // Reload all categories with default items
        foreach (var node in _categoryNodes)
        {
            node.ClearPackages();
        }
        _ = LoadAllCategoriesAsync();
    }

    private async void RefreshAll()
    {
        foreach (var node in _categoryNodes)
        {
            node.ClearPackages();
        }

        if (_isSearchMode && !string.IsNullOrEmpty(_currentSearchQuery))
        {
            DoSearch();
        }
        else
        {
            await LoadAllCategoriesAsync();
        }
    }

    private void OnItemActivated(object item)
    {
        if (item is CloudCategoryNode categoryNode)
        {
            _treeView.Toggle(categoryNode);
        }
        else if (item is CloudPackageNode packageNode)
        {
            // Open package page in browser
            var url = $"https://sbox.game/{packageNode.FullIdent}";
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
            {
                FileName = url,
                UseShellExecute = true
            });
        }
        else if (item is CloudLoadMoreNode loadMoreNode)
        {
            _ = LoadMoreForCategory(loadMoreNode.Category);
        }
    }

    private async Task LoadMoreForCategory(CloudCategoryNode categoryNode)
    {
        if (categoryNode.IsLoadingMore)
            return;

        categoryNode.IsLoadingMore = true;
        var currentCount = categoryNode.Packages.Count;
        _statusLabel.Text = $"Loading more {categoryNode.DisplayName.ToLower()}...";

        try
        {
            string query;
            if (_isSearchMode && !string.IsNullOrEmpty(_currentSearchQuery))
            {
                query = $"{_currentSearchQuery} type:{categoryNode.TypeFilter}";
            }
            else
            {
                query = $"type:{categoryNode.TypeFilter}";
            }

            var result = await Package.FindAsync(query, 20, currentCount);

            if (result?.Packages != null && result.Packages.Any())
            {
                categoryNode.AppendPackagesAndRefresh(result.Packages.ToList());
                _treeView.Update();
                _statusLabel.Text = $"Loaded {categoryNode.Packages.Count} {categoryNode.DisplayName.ToLower()}";
                OnCloudAssetsLoaded?.Invoke(categoryNode.Packages);
            }
            else
            {
                _statusLabel.Text = $"No more {categoryNode.DisplayName.ToLower()} found";
            }
        }
        catch (Exception ex)
        {
            _statusLabel.Text = $"Error: {ex.Message}";
            Log.Warning($"Load more error: {ex.Message}");
        }
        finally
        {
            categoryNode.IsLoadingMore = false;
        }
    }

    private void OnSelectionChanged(object[] items)
    {
        var item = items.FirstOrDefault();

        if (item is CloudCategoryNode categoryNode)
        {
            if (categoryNode.Packages.Count > 0)
            {
                _statusLabel.Text = $"{categoryNode.Packages.Count} {categoryNode.DisplayName.ToLower()}";
                OnCloudAssetsLoaded?.Invoke(categoryNode.Packages);
            }
        }
        else if (item is CloudPackageNode packageNode)
        {
            _statusLabel.Text = $"{packageNode.Title} by {packageNode.Author}";
        }
    }
}

/// <summary>
/// Tree node representing a cloud asset category (Models, Materials, etc.)
/// </summary>
internal class CloudCategoryNode : TreeNode
{
    public string TypeFilter { get; }
    public string DisplayName { get; }
    public string IconName { get; }
    public bool IsLoadingMore { get; set; }
    public List<Package> Packages { get; private set; } = new();

    private CloudAssetPanel _panel;

    // Always show expand arrow - categories always have potential children
    public override bool HasChildren => true;
    public override string Name => DisplayName;

    public CloudCategoryNode(string typeFilter, string displayName, string icon, CloudAssetPanel panel)
    {
        TypeFilter = typeFilter;
        DisplayName = displayName;
        IconName = icon;
        _panel = panel;
        Value = this;
    }

    public void ClearPackages()
    {
        Packages.Clear();
        Clear();
        Dirty();
    }

    public void SetPackagesAndRefresh(List<Package> packages)
    {
        Packages = packages;

        // Clear and rebuild children
        Clear();

        foreach (var pkg in Packages)
        {
            AddItem(new CloudPackageNode(pkg));
        }

        // Always add "Load More" node
        AddItem(new CloudLoadMoreNode(this));

        Dirty();
    }

    public void AppendPackagesAndRefresh(List<Package> newPackages)
    {
        Packages.AddRange(newPackages);

        // Clear and rebuild children
        Clear();

        foreach (var pkg in Packages)
        {
            AddItem(new CloudPackageNode(pkg));
        }

        // Add "Load More" node
        AddItem(new CloudLoadMoreNode(this));

        Dirty();
    }

    protected override void BuildChildren()
    {
        // Children are built via SetPackagesAndRefresh/AppendPackagesAndRefresh
        // This is called by TreeView when expanding - we already have children built
    }

    public override void OnPaint(VirtualWidget item)
    {
        PaintSelection(item);

        var rect = item.Rect;

        // Draw folder icon
        var iconColor = Theme.Yellow;
        if (IsLoadingMore)
            iconColor = Theme.Primary;

        Paint.SetPen(iconColor);
        Paint.DrawIcon(rect, IconName, 16, TextFlag.LeftCenter);

        rect.Left += 22;

        // Draw name
        Paint.SetPen(Theme.Text);
        Paint.SetDefaultFont(9, item.Selected ? 600 : 400);
        Paint.DrawText(rect, DisplayName, TextFlag.LeftCenter);

        // Draw count
        var countText = $"({Packages.Count})";
        Paint.SetPen(Theme.Text.WithAlpha(0.5f));
        Paint.SetDefaultFont(8, 400);
        var countRect = new Rect(item.Rect.Right - 50, item.Rect.Top, 46, item.Rect.Height);
        Paint.DrawText(countRect, countText, TextFlag.RightCenter);
    }
}

/// <summary>
/// Tree node for "Load More" action
/// </summary>
internal class CloudLoadMoreNode : TreeNode
{
    public CloudCategoryNode Category { get; }

    public override bool HasChildren => false;
    public override string Name => "Load More...";

    public CloudLoadMoreNode(CloudCategoryNode category)
    {
        Category = category;
        Value = this;
    }

    public override void OnPaint(VirtualWidget item)
    {
        var rect = item.Rect;

        // Draw load more icon
        Paint.SetPen(Theme.Primary.WithAlpha(0.7f));
        Paint.DrawIcon(rect, "add_circle_outline", 14, TextFlag.LeftCenter);

        rect.Left += 20;

        // Draw text
        Paint.SetPen(Theme.Primary);
        Paint.SetDefaultFont(8, 400);
        Paint.DrawText(rect, "Load More...", TextFlag.LeftCenter);
    }
}

/// <summary>
/// Tree node representing a single cloud package/asset
/// </summary>
internal class CloudPackageNode : TreeNode
{
    public Package Package { get; }
    public string FullIdent { get; }
    public string Title { get; }
    public string Author { get; }
    public string Thumb { get; }

    public override bool HasChildren => false;
    public override string Name => Title;

    public CloudPackageNode(Package package)
    {
        Package = package;
        FullIdent = package.FullIdent;
        Title = package.Title ?? package.FullIdent;
        Author = package.Org?.Title ?? "Unknown";
        Thumb = package.Thumb;
        Value = this;
    }

    public override void OnPaint(VirtualWidget item)
    {
        PaintSelection(item);

        var rect = item.Rect;

        // Draw package thumbnail if available, otherwise use type icon
        if (!string.IsNullOrEmpty(Thumb) && Thumb.StartsWith("http"))
        {
            var iconRect = rect.Shrink(0, 2);
            iconRect.Width = 16;
            iconRect.Height = 16;

            Paint.Draw(iconRect, Thumb);
            rect.Left += 20;
        }
        else
        {
            // Fallback to type icon
            var icon = GetIconForType(Package.TypeName);
            var iconColor = GetColorForType(Package.TypeName);

            Paint.SetPen(iconColor);
            Paint.DrawIcon(rect, icon, 14, TextFlag.LeftCenter);
            rect.Left += 20;
        }

        // Draw title
        Paint.SetPen(Theme.Text);
        Paint.SetDefaultFont(9, item.Selected ? 600 : 400);

        var title = Title;
        if (title.Length > 30)
            title = title.Substring(0, 28) + "..";
        Paint.DrawText(rect, title, TextFlag.LeftCenter);
    }

    public override bool OnDragStart()
    {
        var drag = new Drag(TreeView);
        drag.Data.Text = FullIdent;

        // Set preview image if available
        if (!string.IsNullOrEmpty(Thumb))
        {
            drag.Data.Url = new Uri(Thumb);
        }

        drag.Execute();
        return true;
    }

    public override bool OnContextMenu()
    {
        var menu = new ContextMenu(null);

        menu.AddOption("Open in Browser", "open_in_new", () =>
        {
            var url = $"https://sbox.game/{FullIdent}";
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
            {
                FileName = url,
                UseShellExecute = true
            });
        });

        menu.AddOption("Copy Identifier", "content_copy", () =>
        {
            EditorUtility.Clipboard.Copy(FullIdent);
        });

        menu.OpenAtCursor();
        return true;
    }

    private static string GetIconForType(string typeName)
    {
        return typeName switch
        {
            "model" => "view_in_ar",
            "material" => "texture",
            "sound" => "audiotrack",
            "map" => "landscape",
            _ => "cloud_download"
        };
    }

    private static Color GetColorForType(string typeName)
    {
        return typeName switch
        {
            "model" => new Color(0.9f, 0.6f, 0.3f),
            "material" => new Color(0.9f, 0.4f, 0.6f),
            "sound" => new Color(0.4f, 0.7f, 1.0f),
            "map" => new Color(0.9f, 0.9f, 0.3f),
            _ => Theme.Text.WithAlpha(0.7f)
        };
    }
}