using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;

namespace UnityAssistant
{
    /// <summary>
    /// Listens for Unity Editor selection changes and queues them so the
    /// GUMO desktop app can poll and attach them to the chat as context chips.
    ///
    /// Single click  → one item  (multi=false) → app adds that one item.
    /// Ctrl+click    → multiple  (multi=true)  → app adds all selected items.
    /// </summary>
    [InitializeOnLoad]
    public static class SelectionWatcher
    {
        // ConcurrentQueue is safe to write from main thread and read from
        // the HTTP listener thread pool without additional locking.
        private static readonly ConcurrentQueue<string> _pending = new ConcurrentQueue<string>();

        static SelectionWatcher()
        {
            Selection.selectionChanged += OnSelectionChanged;
        }

        private static void OnSelectionChanged()
        {
            var objects = Selection.objects;
            if (objects == null || objects.Length == 0) return;

            bool isMulti = objects.Length > 1;
            var sb = new StringBuilder();
            sb.Append("[");
            int written = 0;

            foreach (var obj in objects)
            {
                if (obj == null) continue;
                string assetPath = AssetDatabase.GetAssetPath(obj);
                if (written > 0) sb.Append(",");

                if (!string.IsNullOrEmpty(assetPath))
                {
                    // Project asset (script, prefab, animation, etc.)
                    sb.Append("{\"kind\":\"asset\"");
                    sb.Append(",\"name\":").Append(JsonUtil.Quote(obj.name));
                    sb.Append(",\"path\":").Append(JsonUtil.Quote(assetPath.Replace('\\', '/')));
                    sb.Append(",\"type\":").Append(JsonUtil.Quote(System.IO.Path.GetExtension(assetPath).TrimStart('.')));
                    sb.Append("}");
                }
                else if (obj is GameObject go)
                {
                    // Scene hierarchy object
                    sb.Append("{\"kind\":\"hierarchy\"");
                    sb.Append(",\"name\":").Append(JsonUtil.Quote(go.name));
                    sb.Append(",\"path\":").Append(JsonUtil.Quote(HierarchyTools.GetPath(go.transform)));
                    sb.Append(",\"type\":\"GameObject\"");
                    sb.Append(",\"components\":[");
                    var comps = go.GetComponents<Component>();
                    for (int c = 0; c < comps.Length; c++)
                    {
                        if (c > 0) sb.Append(",");
                        sb.Append(JsonUtil.Quote(comps[c] == null ? "MissingScript" : comps[c].GetType().Name));
                    }
                    sb.Append("]}");
                }
                else
                {
                    // Skip ScriptableObjects and other non-displayable types
                    continue;
                }
                written++;
            }
            sb.Append("]");

            if (written == 0) return;

            string json = "{\"multi\":" + (isMulti ? "true" : "false") + ",\"items\":" + sb + "}";
            _pending.Enqueue(json);

            // Keep the queue bounded so stale events don't accumulate when
            // the desktop app is disconnected.
            while (_pending.Count > 10)
                _pending.TryDequeue(out _);
        }

        /// <summary>
        /// Drain all pending selection events and return them as a JSON
        /// response. Safe to call from any thread.
        /// </summary>
        public static string DrainPending()
        {
            var events = new List<string>();
            while (_pending.TryDequeue(out var item))
                events.Add(item);

            if (events.Count == 0)
                return "{\"ok\":true,\"events\":[]}";

            var sb = new StringBuilder("{\"ok\":true,\"events\":[");
            for (int i = 0; i < events.Count; i++)
            {
                if (i > 0) sb.Append(",");
                sb.Append(events[i]);
            }
            sb.Append("]}");
            return sb.ToString();
        }
    }
}
