using System;
using System.Linq;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

namespace VibeCopilot.Editor
{
    // Dupa ce AI-ul scrie un script, asta creeaza automat un obiect (cub)
    // si ii ataseaza scriptul - DUPA ce Unity termina de compilat.
    // Nu foloseste niciun AI: e pura automatizare Unity, deci e gratis.
    public static class VibeAutoAttach
    {
        private const string QueueKey = "VibeCopilot.AttachQueue";
        private const string EnabledKey = "VibeCopilot.AutoCreate";

        public static bool Enabled
        {
            get => EditorPrefs.GetBool(EnabledKey, true);
            set => EditorPrefs.SetBool(EnabledKey, value);
        }

        // Pune un script "la coada". target = obiectul existent pe care se pune
        // ("new" sau gol = creeaza obiect nou). Se proceseaza dupa recompilare.
        public static void Queue(string className, string target)
        {
            if (string.IsNullOrEmpty(className)) return;
            // Format intrare: "className|target". '|' nu apare in nume de clase/obiecte uzuale.
            string entry = className + "|" + (string.IsNullOrEmpty(target) ? "new" : target);
            string existing = EditorPrefs.GetString(QueueKey, "");
            EditorPrefs.SetString(QueueKey,
                string.IsNullOrEmpty(existing) ? entry : existing + ";" + entry);
        }

        // Unity cheama asta automat dupa fiecare recompilare reusita.
        [DidReloadScripts]
        private static void OnScriptsReloaded()
        {
            string queue = EditorPrefs.GetString(QueueKey, "");
            if (string.IsNullOrEmpty(queue)) return;
            EditorPrefs.DeleteKey(QueueKey); // golim ca sa nu reprocesam

            EditorApplication.delayCall += () =>
            {
                foreach (string entry in queue.Split(';'))
                {
                    if (string.IsNullOrEmpty(entry)) continue;
                    string[] parts = entry.Split('|');
                    string className = parts[0].Trim();
                    string target = parts.Length > 1 ? parts[1].Trim() : "new";
                    if (!string.IsNullOrEmpty(className)) TryAttach(className, target);
                }
            };
        }

        private static void TryAttach(string className, string target)
        {
            Type type = FindType(className);
            if (type == null || !typeof(MonoBehaviour).IsAssignableFrom(type))
                return; // nu e un script atasabil (ex: o clasa de date)

            // 1) Daca AI-ul a cerut un obiect EXISTENT si il gasim -> punem scriptul pe el.
            if (!string.IsNullOrEmpty(target) &&
                !target.Equals("new", StringComparison.OrdinalIgnoreCase))
            {
                GameObject existing = GameObject.Find(target);
                // Tinta e o camera dar n-o gasim dupa nume -> folosim camera principala reala.
                if (existing == null && target.ToLowerInvariant().Contains("camera"))
                    existing = MainCameraObject();
                if (existing != null) { Attach(existing, type, className); return; }
            }

            // 2) Scripturile de CAMERA / URMARIRE merg MEREU pe camera principala (nu pe un cub nou).
            string lc = className.ToLowerInvariant();
            if (lc.Contains("camera") || lc.Contains("follow"))
            {
                GameObject cam = MainCameraObject();
                if (cam != null) { Attach(cam, type, className); return; }
            }

            // 3) Altfel cream un obiect nou (cub) si punem scriptul pe el.
            GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
            go.name = className;
            Undo.RegisterCreatedObjectUndo(go, "VibeCopilot: create " + className);
            Undo.AddComponent(go, type);
            Selection.activeGameObject = go;

            Debug.Log("[VibeCopilot] Created object '" + className +
                      "' and attached the script. Press Play to see it!");
        }

        private static void Attach(GameObject go, Type type, string className)
        {
            if (go.GetComponent(type) == null) Undo.AddComponent(go, type);
            Selection.activeGameObject = go;
            Debug.Log("[VibeCopilot] Attached '" + className + "' to '" + go.name + "'.");
        }

        // Camera principala reala (dupa tag MainCamera; altfel prima camera gasita).
        private static GameObject MainCameraObject()
        {
            Camera cam = Camera.main;
            if (cam == null) cam = UnityEngine.Object.FindAnyObjectByType<Camera>();
            return cam != null ? cam.gameObject : null;
        }

        private static Type FindType(string className)
        {
            // Cautam tipul dupa nume in toate assembly-urile incarcate.
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                try { var t = asm.GetType(className); if (t != null) return t; }
                catch { }
            }
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                try
                {
                    var t = asm.GetTypes().FirstOrDefault(x => x.Name == className);
                    if (t != null) return t;
                }
                catch { }
            }
            return null;
        }
    }
}
