Editor/IconGridPanel.cs
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace GeneralGame.Editor;

/// <summary>
/// Panel displaying folder contents or cloud assets as large icons in a grid layout.
/// </summary>
public class IconGridPanel : Widget, IBrowserPanel
{
    private Widget _toolbar;
    private Label _pathLabel;
    private IconButton _closeBtn;
    private IconButton _upBtn;
    private IconButton _moveLeftBtn;
    private IconButton _moveRightBtn;
    private ScrollArea _scrollArea;
    private IconGridCanvas _gridCanvas;

    private string _currentFolder;
    private bool _isShowingCloud;
    private FileSystemWatcher _watcher;

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

    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 IconGridPanel(Widget parent) : base(parent)
    {
        SetSizeMode(SizeMode.CanGrow, SizeMode.CanGrow);
        CreateUI();
        SetupFileWatcher();
    }

    private void SetupFileWatcher()
    {
        _watcher = new FileSystemWatcher();
        _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite;
        _watcher.Changed += OnFileSystemChanged;
        _watcher.Created += OnFileSystemChanged;
        _watcher.Deleted += OnFileSystemChanged;
        _watcher.Renamed += OnFileSystemChanged;
    }

    private void OnFileSystemChanged(object sender, FileSystemEventArgs e)
    {
        // Queue refresh on main thread
        MainThread.Queue(() =>
        {
            if (!string.IsNullOrEmpty(_currentFolder) && Directory.Exists(_currentFolder))
            {
                _gridCanvas?.LoadFolder(_currentFolder);
            }
        });
    }

    public override void OnDestroyed()
    {
        base.OnDestroyed();
        _watcher?.Dispose();
    }

    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;

        _upBtn = _toolbar.Layout.Add(new IconButton("arrow_upward"));
        _upBtn.ToolTip = "Go to Parent Folder";
        _upBtn.Background = Color.Transparent;
        _upBtn.OnClick = GoUp;
        _upBtn.Enabled = false;

        _pathLabel = _toolbar.Layout.Add(new Label("Select a folder...", this));
        _pathLabel.SetStyles("color: #888; font-size: 11px;");

        _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;

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

        // Scroll area with custom canvas
        _scrollArea = Layout.Add(new ScrollArea(this));
        _scrollArea.SetSizeMode(SizeMode.CanGrow, SizeMode.CanGrow);

        _gridCanvas = new IconGridCanvas(_scrollArea);
        _gridCanvas.OnFolderNavigate = ShowFolder;
        _scrollArea.Canvas = _gridCanvas;
    }

    /// <summary>
    /// Show local folder contents
    /// </summary>
    public void ShowFolder(string folderPath)
    {
        if (string.IsNullOrEmpty(folderPath) || !Directory.Exists(folderPath))
            return;

        _currentFolder = Path.GetFullPath(folderPath);
        _isShowingCloud = false;

        // Update file watcher to monitor this folder
        try
        {
            _watcher.EnableRaisingEvents = false;
            _watcher.Path = _currentFolder;
            _watcher.EnableRaisingEvents = true;
        }
        catch { /* Ignore watcher errors */ }

        // Update path label
        var displayPath = _currentFolder;
        var assetsPath = Project.Current?.GetAssetsPath();
        var codePath = Project.Current?.GetCodePath();
        var corePath = global::Editor.FileSystem.Root?.GetFullPath("/core/");
        var citizenPath = global::Editor.FileSystem.Root?.GetFullPath("/addons/citizen/assets/");

        if (!string.IsNullOrEmpty(assetsPath) && displayPath.StartsWith(assetsPath, StringComparison.OrdinalIgnoreCase))
            displayPath = "Assets" + displayPath.Substring(assetsPath.Length);
        else if (!string.IsNullOrEmpty(codePath) && displayPath.StartsWith(codePath, StringComparison.OrdinalIgnoreCase))
            displayPath = "Code" + displayPath.Substring(codePath.Length);
        else if (!string.IsNullOrEmpty(corePath) && displayPath.StartsWith(corePath, StringComparison.OrdinalIgnoreCase))
            displayPath = "Core" + displayPath.Substring(corePath.Length);
        else if (!string.IsNullOrEmpty(citizenPath) && displayPath.StartsWith(citizenPath, StringComparison.OrdinalIgnoreCase))
            displayPath = "Citizen" + displayPath.Substring(citizenPath.Length);

        _pathLabel.Text = displayPath.Replace("\\", "/");

        // Enable up button - allow navigation up unless we're at a root folder
        var parentDir = Directory.GetParent(_currentFolder);
        bool isAtRoot = false;

        if (!string.IsNullOrEmpty(assetsPath) && _currentFolder.Equals(assetsPath.TrimEnd('\\', '/'), StringComparison.OrdinalIgnoreCase))
            isAtRoot = true;
        else if (!string.IsNullOrEmpty(codePath) && _currentFolder.Equals(codePath.TrimEnd('\\', '/'), StringComparison.OrdinalIgnoreCase))
            isAtRoot = true;
        else if (!string.IsNullOrEmpty(corePath) && _currentFolder.Equals(corePath.TrimEnd('\\', '/'), StringComparison.OrdinalIgnoreCase))
            isAtRoot = true;
        else if (!string.IsNullOrEmpty(citizenPath) && _currentFolder.Equals(citizenPath.TrimEnd('\\', '/'), StringComparison.OrdinalIgnoreCase))
            isAtRoot = true;

        _upBtn.Enabled = parentDir != null && !isAtRoot;

        _gridCanvas.LoadFolder(_currentFolder);
    }

    /// <summary>
    /// Show cloud packages
    /// </summary>
    public void ShowCloudPackages(List<Package> packages, string title = "Cloud Assets")
    {
        _currentFolder = null;
        _isShowingCloud = true;
        _upBtn.Enabled = false;
        _pathLabel.Text = $"Cloud: {title} ({packages.Count})";

        // Disable file watcher for cloud view
        _watcher.EnableRaisingEvents = false;

        _gridCanvas.LoadCloudPackages(packages);
    }

    private void GoUp()
    {
        if (_isShowingCloud || string.IsNullOrEmpty(_currentFolder)) return;
        var parentDir = Directory.GetParent(_currentFolder);
        if (parentDir != null)
            ShowFolder(parentDir.FullName);
    }
}

/// <summary>
/// Canvas that draws all icons itself - supports both local files and cloud packages
/// </summary>
internal class IconGridCanvas : Widget
{
    private List<GridItem> _items = new();
    private int _hoveredIndex = -1;
    private int _selectedIndex = -1;

    public Action<string> OnFolderNavigate;

    private const float ItemWidth = 80;
    private const float ItemHeight = 100;
    private const float IconSize = 64;
    private const float Spacing = 8;
    private const float Padding = 8;

    public IconGridCanvas(Widget parent) : base(parent)
    {
        MinimumHeight = 100;
        AcceptDrops = true;
    }

    /// <summary>
    /// Load local folder contents
    /// </summary>
    public void LoadFolder(string folderPath)
    {
        _currentFolder = folderPath;
        _items.Clear();
        _hoveredIndex = -1;
        _selectedIndex = -1;

        try
        {
            // Folders
            foreach (var dir in Directory.GetDirectories(folderPath).OrderBy(d => Path.GetFileName(d), StringComparer.OrdinalIgnoreCase))
            {
                var dirInfo = new DirectoryInfo(dir);
                if (dirInfo.Attributes.HasFlag(FileAttributes.Hidden)) continue;
                if (dirInfo.Name.Equals("obj", StringComparison.OrdinalIgnoreCase)) continue;
                if (dirInfo.Name.StartsWith(".")) continue;

                _items.Add(new GridItem
                {
                    Path = dir,
                    Name = dirInfo.Name,
                    IsFolder = true,
                    IsCloud = false
                });
            }

            // Files
            foreach (var file in Directory.GetFiles(folderPath).OrderBy(f => Path.GetFileName(f), StringComparer.OrdinalIgnoreCase))
            {
                var fileInfo = new FileInfo(file);
                if (fileInfo.Attributes.HasFlag(FileAttributes.Hidden)) continue;
                if (fileInfo.Name.Contains(".generated", StringComparison.OrdinalIgnoreCase)) continue;
                if (fileInfo.Name.EndsWith(".meta", StringComparison.OrdinalIgnoreCase)) continue;
                if (fileInfo.Name.StartsWith(".")) continue;
                if (fileInfo.Name.EndsWith("_c") && File.Exists(file[..^2])) continue;

                var asset = AssetSystem.FindByPath(file);
                Pixmap thumb = null;
                try { thumb = asset?.GetAssetThumb(); } catch { }

                _items.Add(new GridItem
                {
                    Path = file,
                    Name = fileInfo.Name,
                    IsFolder = false,
                    IsCloud = false,
                    Asset = asset,
                    Thumbnail = thumb,
                    Extension = fileInfo.Extension.ToLowerInvariant()
                });
            }
        }
        catch (Exception ex)
        {
            Log.Warning($"Error loading folder: {ex.Message}");
        }

        UpdateSize();
        Update();
    }

    /// <summary>
    /// Load cloud packages
    /// </summary>
    public void LoadCloudPackages(List<Package> packages)
    {
        _items.Clear();
        _hoveredIndex = -1;
        _selectedIndex = -1;

        foreach (var pkg in packages)
        {
            _items.Add(new GridItem
            {
                Name = pkg.Title ?? pkg.FullIdent,
                IsFolder = false,
                IsCloud = true,
                Package = pkg,
                Author = pkg.Org?.Title ?? "Unknown",
                CloudThumb = pkg.Thumb
            });
        }

        UpdateSize();
        Update();
    }

    private void UpdateSize()
    {
        if (_items == null || _items.Count == 0)
        {
            MinimumHeight = 100;
            return;
        }

        float width = 400;
        try { width = Parent?.Width ?? 400; } catch { }
        if (width <= 0) width = 400;

        int columns = Math.Max(1, (int)((width - Padding * 2 + Spacing) / (ItemWidth + Spacing)));
        int rows = (_items.Count + columns - 1) / columns;

        float height = Padding * 2 + rows * ItemHeight + Math.Max(0, rows - 1) * Spacing;
        MinimumHeight = Math.Max(100, height);
    }

    protected override void OnPaint()
    {
        if (_items == null || _items.Count == 0)
        {
            Paint.SetPen(Theme.Text.WithAlpha(0.3f));
            Paint.SetDefaultFont(10, 400);
            Paint.DrawText(LocalRect, "Select a folder or cloud category", TextFlag.Center);
            return;
        }

        float width = Width;
        if (width <= 0) width = 400;

        int columns = Math.Max(1, (int)((width - Padding * 2 + Spacing) / (ItemWidth + Spacing)));

        for (int i = 0; i < _items.Count; i++)
        {
            int col = i % columns;
            int row = i / columns;

            float x = Padding + col * (ItemWidth + Spacing);
            float y = Padding + row * (ItemHeight + Spacing);

            var itemRect = new Rect(x, y, ItemWidth, ItemHeight);
            DrawItem(i, itemRect);
        }
    }

    private void DrawItem(int index, Rect rect)
    {
        var item = _items[index];

        // Background
        if (index == _selectedIndex)
        {
            Paint.SetBrush(Theme.Primary.WithAlpha(0.3f));
            Paint.DrawRect(rect, 4);
        }
        else if (index == _hoveredIndex)
        {
            Paint.SetBrush(Color.White.WithAlpha(0.05f));
            Paint.DrawRect(rect, 4);
        }

        // Icon area
        var iconRect = new Rect(
            rect.Left + (rect.Width - IconSize) / 2,
            rect.Top + 4,
            IconSize,
            IconSize
        );

        if (item.IsCloud)
        {
            // Cloud item - draw thumbnail if available, otherwise icon
            if (!string.IsNullOrEmpty(item.CloudThumb) && item.CloudThumb.StartsWith("http"))
            {
                Paint.Draw(iconRect, item.CloudThumb);
            }
            else
            {
                Paint.SetBrush(Theme.WidgetBackground);
                Paint.DrawRect(iconRect, 4);

                var icon = GetIconForPackageType(item.Package?.TypeName ?? "model");
                var color = GetColorForPackageType(item.Package?.TypeName ?? "model");
                Paint.SetPen(color);
                Paint.DrawIcon(iconRect, icon, 48, TextFlag.Center);
            }
        }
        else if (item.Thumbnail != null)
        {
            // Local file with thumbnail
            Paint.Draw(iconRect, item.Thumbnail);
        }
        else
        {
            // Local file/folder without thumbnail
            var icon = item.IsFolder ? "folder" : GetIconForExtension(item.Extension);
            var color = item.IsFolder ? Theme.Yellow : GetColorForExtension(item.Extension, item.Asset);
            Paint.SetPen(color);
            Paint.DrawIcon(iconRect, icon, IconSize, TextFlag.Center);
        }

        // Name
        var textRect = new Rect(rect.Left + 2, iconRect.Bottom + 2, rect.Width - 4, 16);
        Paint.SetPen(Theme.Text);
        Paint.SetDefaultFont(8, 400);

        var name = item.Name;
        if (name.Length > 12) name = name.Substring(0, 10) + "..";
        Paint.DrawText(textRect, name, TextFlag.Center);

        // Author for cloud items
        if (item.IsCloud && !string.IsNullOrEmpty(item.Author))
        {
            var authorRect = new Rect(rect.Left + 2, textRect.Bottom, rect.Width - 4, 12);
            Paint.SetPen(Theme.Text.WithAlpha(0.5f));
            Paint.SetDefaultFont(7, 400);

            var author = item.Author;
            if (author.Length > 14) author = author.Substring(0, 12) + "..";
            Paint.DrawText(authorRect, author, TextFlag.Center);
        }
    }

    private int GetItemAtPosition(Vector2 pos)
    {
        if (_items == null || _items.Count == 0)
            return -1;

        float width = Width;
        if (width <= 0) return -1;

        int columns = Math.Max(1, (int)((width - Padding * 2 + Spacing) / (ItemWidth + Spacing)));

        int col = (int)((pos.x - Padding) / (ItemWidth + Spacing));
        int row = (int)((pos.y - Padding) / (ItemHeight + Spacing));

        if (col < 0 || col >= columns) return -1;

        int index = row * columns + col;
        if (index < 0 || index >= _items.Count) return -1;

        // Check if actually inside item bounds
        float x = Padding + col * (ItemWidth + Spacing);
        float y = Padding + row * (ItemHeight + Spacing);
        var itemRect = new Rect(x, y, ItemWidth, ItemHeight);
        if (!itemRect.IsInside(pos)) return -1;

        return index;
    }

    private Vector2 _dragStartPos;
    private bool _isDragging;
    private int _dragItemIndex = -1;

    protected override void OnMouseMove(MouseEvent e)
    {
        base.OnMouseMove(e);
        int newHover = GetItemAtPosition(e.LocalPosition);
        if (newHover != _hoveredIndex)
        {
            _hoveredIndex = newHover;
            Update();
        }

        // Handle drag
        if (e.ButtonState.HasFlag(MouseButtons.Left) && !_isDragging && _dragItemIndex >= 0)
        {
            var delta = e.LocalPosition - _dragStartPos;
            if (delta.Length > 5)
            {
                _isDragging = true;
                StartDrag(_dragItemIndex);
            }
        }
    }

    protected override void OnMouseLeave()
    {
        base.OnMouseLeave();
        _hoveredIndex = -1;
        Update();
    }

    protected override void OnMousePress(MouseEvent e)
    {
        base.OnMousePress(e);
        if (e.LeftMouseButton)
        {
            int index = GetItemAtPosition(e.LocalPosition);
            _dragStartPos = e.LocalPosition;
            _isDragging = false;
            _dragItemIndex = index;

            if (index >= 0)
            {
                _selectedIndex = index;
                var item = _items[index];

                if (!item.IsCloud && !item.IsFolder && item.Asset != null)
                    EditorUtility.InspectorObject = item.Asset;

                Update();
            }
        }
    }

    private void StartDrag(int index)
    {
        if (index < 0 || index >= _items.Count) return;
        var item = _items[index];

        var drag = new Drag(this);

        if (item.IsCloud)
        {
            // Cloud item - set package FullIdent for scene drop
            drag.Data.Text = item.Package?.FullIdent ?? "";

            // Set thumbnail URL if available
            var thumb = item.Package?.Thumb;
            if (!string.IsNullOrEmpty(thumb))
            {
                drag.Data.Url = new Uri(thumb);
            }
        }
        else if (item.IsFolder)
        {
            drag.Data.Url = new Uri("file:///" + item.Path);
        }
        else if (item.Asset != null)
        {
            drag.Data.Text = item.Asset.RelativePath;
            drag.Data.Url = new Uri("file:///" + item.Asset.AbsolutePath);
        }
        else
        {
            drag.Data.Text = item.Path;
            drag.Data.Url = new Uri("file:///" + item.Path);
        }

        drag.Execute();
    }

    protected override void OnDoubleClick(MouseEvent e)
    {
        base.OnDoubleClick(e);
        if (e.LeftMouseButton)
        {
            int index = GetItemAtPosition(e.LocalPosition);
            if (index >= 0)
            {
                var item = _items[index];

                if (item.IsCloud)
                {
                    // Open cloud package in browser
                    var url = $"https://sbox.game/{item.Package?.FullIdent}";
                    System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
                    {
                        FileName = url,
                        UseShellExecute = true
                    });
                }
                else if (item.IsFolder)
                {
                    OnFolderNavigate?.Invoke(item.Path);
                }
                else if (item.Asset != null)
                {
                    item.Asset.OpenInEditor();
                }
            }
        }
    }

    /// <summary>
    /// Handle drag hover - show appropriate drop cursor
    /// </summary>
    public override void OnDragHover(DragEvent ev)
    {
        base.OnDragHover(ev);

        ev.Action = DropAction.Ignore;

        // Only accept drops if we have a current folder to drop into
        if (string.IsNullOrEmpty(_currentFolder) || !Directory.Exists(_currentFolder))
            return;

        // Check if dragging over a folder item
        int index = GetItemAtPosition(ev.LocalPosition);
        if (index >= 0 && _items[index].IsFolder)
        {
            // Dropping onto a folder
            ev.Action = ev.HasCtrl ? DropAction.Copy : DropAction.Move;
            if (_hoveredIndex != index)
            {
                _hoveredIndex = index;
                Update();
            }
        }
        else if (ev.Data.HasFileOrFolder)
        {
            // Dropping into current folder
            ev.Action = ev.HasCtrl ? DropAction.Copy : DropAction.Move;
        }
    }

    /// <summary>
    /// Handle dropping files from Windows Explorer or other sources
    /// </summary>
    public override void OnDragDrop(DragEvent ev)
    {
        base.OnDragDrop(ev);

        if (!ev.Data.HasFileOrFolder)
            return;

        if (string.IsNullOrEmpty(_currentFolder) || !Directory.Exists(_currentFolder))
            return;

        // Check if dropping onto a folder item
        int index = GetItemAtPosition(ev.LocalPosition);
        string targetFolder = _currentFolder;

        if (index >= 0 && _items[index].IsFolder)
        {
            targetFolder = _items[index].Path;
        }

        // Process the drop
        bool isCopy = ev.HasCtrl;
        ev.Action = isCopy ? DropAction.Copy : DropAction.Move;

        foreach (var file in ev.Data.Files)
        {
            if (string.IsNullOrEmpty(file))
                continue;

            // Don't drop onto itself
            if (file.Equals(targetFolder, StringComparison.OrdinalIgnoreCase))
                continue;

            try
            {
                var fileName = Path.GetFileName(file);
                var destPath = Path.Combine(targetFolder, fileName);

                // Don't overwrite if destination is same as source
                if (Path.GetFullPath(file).Equals(Path.GetFullPath(destPath), StringComparison.OrdinalIgnoreCase))
                    continue;

                if (Directory.Exists(file))
                {
                    // It's a directory
                    if (isCopy)
                        CopyDirectory(file, destPath);
                    else
                        EditorUtility.RenameDirectory(file, destPath);
                }
                else if (File.Exists(file))
                {
                    // Check if it's a registered asset
                    var asset = AssetSystem.FindByPath(file);

                    if (asset != null && !asset.IsDeleted)
                    {
                        // Use EditorUtility for proper asset handling
                        if (isCopy)
                            EditorUtility.CopyAssetToDirectory(asset, targetFolder);
                        else
                            EditorUtility.MoveAssetToDirectory(asset, targetFolder);
                    }
                    else
                    {
                        // Regular file, not an asset
                        if (isCopy)
                            File.Copy(file, destPath, overwrite: false);
                        else
                            File.Move(file, destPath, overwrite: false);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error($"Failed to {(isCopy ? "copy" : "move")} '{file}': {ex.Message}");
            }
        }

        // Refresh the view
        LoadFolder(_currentFolder);
    }

    /// <summary>
    /// Recursively copy a directory
    /// </summary>
    private static void CopyDirectory(string sourceDir, string destDir)
    {
        Directory.CreateDirectory(destDir);

        foreach (var file in Directory.GetFiles(sourceDir))
        {
            File.Copy(file, Path.Combine(destDir, Path.GetFileName(file)));
        }

        foreach (var dir in Directory.GetDirectories(sourceDir))
        {
            CopyDirectory(dir, Path.Combine(destDir, Path.GetFileName(dir)));
        }
    }

    protected override void OnContextMenu(ContextMenuEvent e)
    {
        int index = GetItemAtPosition(e.LocalPosition);
        var menu = new ContextMenu(this);

        // If clicking on empty space, show folder-level context menu
        if (index < 0)
        {
            if (!string.IsNullOrEmpty(_currentFolder))
            {
                var createMenu = menu.AddMenu("Create", "add");
                AssetCreator.AddOptions(createMenu, _currentFolder);

                menu.AddSeparator();
                menu.AddOption("Open in Explorer", "folder_open", () => EditorUtility.OpenFolder(_currentFolder));
                menu.AddOption("Refresh", "refresh", () => LoadFolder(_currentFolder));
            }
            menu.OpenAtCursor();
            e.Accepted = true;
            return;
        }

        var item = _items[index];

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

            menu.AddOption("Copy Identifier", "content_copy", () =>
            {
                EditorUtility.Clipboard.Copy(item.Package?.FullIdent ?? "");
            });
        }
        else if (item.IsFolder)
        {
            menu.AddOption("Open", "folder_open", () => OnFolderNavigate?.Invoke(item.Path));
            menu.AddOption("Open in Explorer", "launch", () => EditorUtility.OpenFolder(item.Path));

            menu.AddSeparator();

            menu.AddOption("Rename", "edit", () => StartRename(index));

            menu.AddSeparator();

            menu.AddOption("Copy Path", "content_copy", () => EditorUtility.Clipboard.Copy(item.Path));

            menu.AddSeparator();

            // Create submenu inside folder
            var createMenu = menu.AddMenu("Create", "add");
            AssetCreator.AddOptions(createMenu, item.Path);

            menu.AddSeparator();

            menu.AddOption("Delete", "delete", () => DeleteFolder(item.Path, item.Name));
        }
        else
        {
            // Open options
            if (item.Asset != null)
            {
                menu.AddOption("Open in Editor", "edit", () => item.Asset.OpenInEditor());
            }
            else
            {
                menu.AddOption("Open", "open_in_new", () => EditorUtility.OpenFolder(item.Path));
            }
            menu.AddOption("Show in Explorer", "folder_open", () => EditorUtility.OpenFileFolder(item.Path));

            menu.AddSeparator();

            // Copy options
            if (item.Asset != null)
            {
                menu.AddOption("Copy Relative Path", "content_paste_go", () => EditorUtility.Clipboard.Copy(item.Asset.RelativePath));
            }
            menu.AddOption("Copy Absolute Path", "content_paste", () => EditorUtility.Clipboard.Copy(item.Path));

            // Asset-type specific options (Create Material, Create Texture, etc.)
            AssetContextMenuHelper.AddAssetTypeOptions(menu, item.Asset);

            menu.AddSeparator();

            // Edit options
            menu.AddOption("Rename", "edit", () => StartRename(_items.IndexOf(item)));
            menu.AddOption("Duplicate", "file_copy", () => DuplicateFile(item.Path));

            menu.AddSeparator();

            // Create submenu for quick asset creation in same folder
            var parentFolder = Path.GetDirectoryName(item.Path);
            if (!string.IsNullOrEmpty(parentFolder))
            {
                var createMenu = menu.AddMenu("Create", "add");
                AssetCreator.AddOptions(createMenu, parentFolder);
                menu.AddSeparator();
            }

            menu.AddOption("Delete", "delete", () => DeleteFile(item.Path, item.Name));
        }

        menu.OpenAtCursor();
        e.Accepted = true;
    }

    private string _currentFolder;

    private void StartRename(int index)
    {
        // For now, use a simple input dialog approach
        // In a full implementation, you'd want inline renaming
        if (index < 0 || index >= _items.Count) return;
        var item = _items[index];

        var dialog = new LineEditDialog("Rename", item.Name);
        dialog.OnConfirm = (newName) =>
        {
            if (string.IsNullOrWhiteSpace(newName) || newName == item.Name)
                return;

            try
            {
                var directory = Path.GetDirectoryName(item.Path);
                var newPath = Path.Combine(directory, newName);

                if (item.IsFolder)
                {
                    Directory.Move(item.Path, newPath);
                }
                else
                {
                    // Preserve extension if not provided
                    if (!Path.HasExtension(newName))
                    {
                        newName += item.Extension;
                        newPath = Path.Combine(directory, newName);
                    }
                    File.Move(item.Path, newPath);
                }

                LoadFolder(_currentFolder);
            }
            catch (Exception ex)
            {
                Log.Error($"Failed to rename: {ex.Message}");
            }
        };
        dialog.Show();
    }


    private void DuplicateFile(string filePath)
    {
        try
        {
            var directory = Path.GetDirectoryName(filePath);
            var nameWithoutExt = Path.GetFileNameWithoutExtension(filePath);
            var extension = Path.GetExtension(filePath);

            var newName = $"{nameWithoutExt}_copy{extension}";
            var newPath = Path.Combine(directory, newName);

            var counter = 1;
            while (File.Exists(newPath))
            {
                newName = $"{nameWithoutExt}_copy{counter++}{extension}";
                newPath = Path.Combine(directory, newName);
            }

            File.Copy(filePath, newPath);
            LoadFolder(_currentFolder);
        }
        catch (Exception ex)
        {
            Log.Error($"Failed to duplicate file: {ex.Message}");
        }
    }

    private void DeleteFile(string filePath, string fileName)
    {
        var confirm = new PopupWindow(
            "Delete File",
            $"Are you sure you want to delete '{fileName}'?",
            "Cancel",
            new Dictionary<string, Action>()
            {
                { "Delete", () =>
                    {
                        try
                        {
                            File.Delete(filePath);
                            LoadFolder(_currentFolder);
                        }
                        catch (Exception ex)
                        {
                            Log.Error($"Failed to delete file: {ex.Message}");
                        }
                    }
                }
            }
        );
        confirm.Show();
    }

    private void DeleteFolder(string folderPath, string folderName)
    {
        var confirm = new PopupWindow(
            "Delete Folder",
            $"Are you sure you want to delete '{folderName}'?\nAll contents will be deleted.",
            "Cancel",
            new Dictionary<string, Action>()
            {
                { "Delete", () =>
                    {
                        try
                        {
                            Directory.Delete(folderPath, recursive: true);
                            LoadFolder(_currentFolder);
                        }
                        catch (Exception ex)
                        {
                            Log.Error($"Failed to delete folder: {ex.Message}");
                        }
                    }
                }
            }
        );
        confirm.Show();
    }

    private static string GetIconForExtension(string ext)
    {
        return ext switch
        {
            ".vmdl" or ".fbx" or ".obj" or ".gltf" or ".glb" => "view_in_ar",
            ".png" or ".jpg" or ".jpeg" or ".tga" or ".vtex" or ".psd" => "image",
            ".vmat" => "texture",
            ".wav" or ".mp3" or ".ogg" or ".vsnd" => "audiotrack",
            ".cs" or ".razor" => "code",
            ".scss" or ".css" => "style",
            ".shader" or ".shdrgrph" => "gradient",
            ".prefab" => "inventory_2",
            ".scene" => "landscape",
            ".json" or ".xml" => "data_object",
            ".txt" or ".md" => "description",
            ".item" => "category",
            _ => "description"
        };
    }

    private static Color GetColorForExtension(string ext, Asset asset)
    {
        if (asset?.AssetType?.Color != null && asset.AssetType.Color != default)
            return asset.AssetType.Color;

        return ext switch
        {
            ".cs" => new Color(0.4f, 0.7f, 1.0f),
            ".razor" => new Color(0.6f, 0.4f, 0.9f),
            ".shader" => new Color(0.3f, 0.9f, 0.5f),
            ".vmdl" => new Color(0.9f, 0.6f, 0.3f),
            ".vmat" => new Color(0.9f, 0.4f, 0.6f),
            ".prefab" => new Color(0.3f, 0.8f, 0.9f),
            ".scene" => new Color(0.9f, 0.9f, 0.3f),
            _ => Theme.Text.WithAlpha(0.7f)
        };
    }

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

    private static Color GetColorForPackageType(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)
        };
    }

    private class GridItem
    {
        public string Path;
        public string Name;
        public bool IsFolder;
        public bool IsCloud;
        public Asset Asset;
        public Pixmap Thumbnail;
        public string Extension;
        public Package Package;
        public string Author;
        public string CloudThumb; // URL for cloud package thumbnail
    }
}

/// <summary>
/// Simple popup for entering text (used for renaming)
/// </summary>
internal class LineEditDialog : PopupWidget
{
    private LineEdit _lineEdit;
    public Action<string> OnConfirm;

    public LineEditDialog(string title, string initialText) : base(null)
    {
        Layout = Layout.Row();
        Layout.Margin = 4;
        Layout.Spacing = 4;

        _lineEdit = Layout.Add(new LineEdit(this));
        _lineEdit.Text = initialText;
        _lineEdit.MinimumWidth = 200;
        _lineEdit.SelectAll();
        _lineEdit.ReturnPressed += () =>
        {
            OnConfirm?.Invoke(_lineEdit.Text);
            Close();
        };

        var okBtn = Layout.Add(new Button("OK", this));
        okBtn.Clicked = () =>
        {
            OnConfirm?.Invoke(_lineEdit.Text);
            Close();
        };

        _lineEdit.Focus();
    }

    public new void Show()
    {
        OpenAtCursor();
        _lineEdit.Focus();
    }
}