using System;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;

namespace VibeCopilot.Editor
{
    // O actiune in scena, trimisa de AI ca JSON.
    [Serializable]
    public class VibeAction
    {
        public string action;     // create / delete / color / move / scale / rotate / cameraPos / cameraLook / cameraBg
        public string target;     // numele obiectului afectat
        public string primitive;  // cube / sphere / capsule / cylinder / plane / quad / empty
        public string name;       // nume pt obiectul creat
        public string parent;     // (optional) numele parintelui -> obiectul devine copil, pozitia e RELATIVA
        public string prompt;     // (doar generate3d) descrierea modelului 3D de generat
        public bool custom;       // (doar generate3d) userul a cerut EXPLICIT o varianta unica -> fara biblioteca/cache
        public string color;      // "red" sau "#ff0000"
        public float[] position;
        public float[] scale;
        public float[] rotation;
    }

    [Serializable]
    public class VibeActionList { public VibeAction[] actions; }

    // Executa actiunile in scena Unity. NU foloseste AI - e gratis.
    public static class VibeSceneActions
    {
        // True daca ULTIMUL ExecuteJson a rulat efectiv o actiune generate3d. Fereastra il foloseste
        // ca sa ascunda textul AI ("Generating...") si sa arate DOAR poza de concept — semnal sigur,
        // nu ghicit din text.
        public static bool LastRanGenerate3D;

        public static int ExecuteJson(string json)
        {
            LastRanGenerate3D = false;
            VibeActionList list = null;
            try { list = JsonUtility.FromJson<VibeActionList>(json); }
            catch { }
            if (list == null || list.actions == null) return 0;

            int count = 0;
            foreach (VibeAction a in list.actions)
            {
                try { if (ExecuteOne(a)) count++; }
                catch (Exception e) { Debug.LogWarning("[VibeCopilot] Action failed: " + e.Message); }
            }
            // MarkAllScenesDirty NU are voie in Play mode (arunca InvalidOperationException).
            // In Play mode nu salvam oricum scena, deci sarim peste -> nu mai crapa fluxul.
            if (count > 0 && !Application.isPlaying) EditorSceneManager.MarkAllScenesDirty();
            return count;
        }

        private static bool ExecuteOne(VibeAction a)
        {
            if (a == null || string.IsNullOrEmpty(a.action)) return false;
            switch (a.action.ToLowerInvariant())
            {
                case "create": return DoCreate(a);
                case "generate3d":
                case "generate_3d":
                case "model3d": return DoGenerate3D(a);
                case "delete": return DoDelete(a);
                case "color": return DoColor(a);
                case "move":
                case "position": return DoMove(a);
                case "scale": return DoScale(a);
                case "rotate":
                case "rotation": return DoRotate(a);
                case "camerapos": return DoCameraPos(a);
                case "cameralook": return DoCameraLook(a);
                case "camerabg": return DoCameraBg(a);
                default:
                    Debug.LogWarning("[VibeCopilot] Unknown action: " + a.action);
                    return false;
            }
        }

        // ---- Implementari ----

        // Genereaza un model 3D real (Tripo) si il pune in scena. Async (vezi VibeGen3D).
        private static bool DoGenerate3D(VibeAction a)
        {
            string prompt = !string.IsNullOrEmpty(a.prompt) ? a.prompt : a.name;
            if (string.IsNullOrEmpty(prompt)) return false;
            Vector3 pos = Valid(a.position) ? V3(a.position) : Vector3.zero;
            string nm = string.IsNullOrEmpty(a.name) ? "Model" : a.name;
            VibeGen3D.Generate(prompt, nm, pos, a.custom);
            LastRanGenerate3D = true;
            return true;
        }

        private static bool DoCreate(VibeAction a)
        {
            GameObject go;
            string prim = (a.primitive ?? "cube").ToLowerInvariant();
            if (prim == "empty") go = new GameObject();
            else if (prim == "camera") { go = new GameObject(); go.AddComponent<Camera>(); }
            else if (prim == "light") { go = new GameObject(); go.AddComponent<Light>().type = LightType.Point; }
            else go = GameObject.CreatePrimitive(ParsePrimitive(prim));

            go.name = string.IsNullOrEmpty(a.name) ? Capitalize(prim) : a.name;

            // Parentare: daca are un parinte si il gasim, il legam => pozitiile sunt RELATIVE la parinte
            // (asa membrele raman ancorate de corp si se misca impreuna).
            bool parented = false;
            if (!string.IsNullOrEmpty(a.parent))
            {
                GameObject p = Find(a.parent);
                if (p != null) { go.transform.SetParent(p.transform, false); parented = true; }
            }

            if (Valid(a.scale)) go.transform.localScale = V3(a.scale);
            if (Valid(a.rotation))
            {
                if (parented) go.transform.localEulerAngles = V3(a.rotation);
                else go.transform.eulerAngles = V3(a.rotation);
            }
            if (parented)
            {
                if (Valid(a.position)) go.transform.localPosition = V3(a.position);
            }
            else
            {
                // Obiect de sine statator: daca locul cerut e ocupat, il mutam langa (sa nu se suprapuna).
                Vector3 desired = Valid(a.position) ? V3(a.position) : go.transform.position;
                go.transform.position = FindFreeSpot(desired, go);
            }
            if (!string.IsNullOrEmpty(a.color)) SetColor(go, ParseColor(a.color));

            Undo.RegisterCreatedObjectUndo(go, "VibeCopilot: create " + go.name);
            Selection.activeGameObject = go;
            return true;
        }

        private static bool DoDelete(VibeAction a)
        {
            GameObject go = Find(a.target);
            if (go == null) return false;
            Undo.DestroyObjectImmediate(go);
            return true;
        }

        private static bool DoColor(VibeAction a)
        {
            GameObject go = Find(a.target);
            if (go == null) return false;
            SetColor(go, ParseColor(a.color));
            return true;
        }

        private static bool DoMove(VibeAction a)
        {
            GameObject go = Find(a.target);
            if (go == null || !Valid(a.position)) return false;
            Undo.RecordObject(go.transform, "VibeCopilot: move");
            go.transform.position = V3(a.position);
            return true;
        }

        private static bool DoScale(VibeAction a)
        {
            GameObject go = Find(a.target);
            if (go == null || !Valid(a.scale)) return false;
            Undo.RecordObject(go.transform, "VibeCopilot: scale");
            go.transform.localScale = V3(a.scale);
            return true;
        }

        private static bool DoRotate(VibeAction a)
        {
            GameObject go = Find(a.target);
            if (go == null || !Valid(a.rotation)) return false;
            Undo.RecordObject(go.transform, "VibeCopilot: rotate");
            go.transform.eulerAngles = V3(a.rotation);
            return true;
        }

        private static bool DoCameraPos(VibeAction a)
        {
            Camera cam = GetCamera();
            if (cam == null || !Valid(a.position)) return false;
            Undo.RecordObject(cam.transform, "VibeCopilot: camera position");
            cam.transform.position = V3(a.position);
            return true;
        }

        private static bool DoCameraLook(VibeAction a)
        {
            Camera cam = GetCamera();
            if (cam == null || !Valid(a.rotation)) return false;
            Undo.RecordObject(cam.transform, "VibeCopilot: camera rotation");
            cam.transform.eulerAngles = V3(a.rotation);
            return true;
        }

        private static bool DoCameraBg(VibeAction a)
        {
            Camera cam = GetCamera();
            if (cam == null || string.IsNullOrEmpty(a.color)) return false;
            Undo.RecordObject(cam, "VibeCopilot: camera background");
            cam.clearFlags = CameraClearFlags.SolidColor;
            cam.backgroundColor = ParseColor(a.color);
            return true;
        }

        // ---- Ajutoare ----

        private static GameObject Find(string name)
            => string.IsNullOrEmpty(name) ? null : GameObject.Find(name);

        // Gaseste un loc liber langa pozitia dorita, ca obiectele sa nu se suprapuna.
        private static Vector3 FindFreeSpot(Vector3 desired, GameObject self)
        {
            const float clearance = 1.5f; // sub atat = considerat "ocupat"
            const float step = 3f;        // cu cat il mutam cand e ocupat
            if (!Occupied(desired, clearance, self)) return desired;
            for (int i = 1; i <= 20; i++)
            {
                float dir = (i % 2 == 1) ? 1f : -1f;   // alternam dreapta/stanga
                int k = (i + 1) / 2;
                Vector3 p = desired + new Vector3(dir * step * k, 0f, 0f);
                if (!Occupied(p, clearance, self)) return p;
            }
            return desired; // n-am gasit loc liber; il lasam unde s-a cerut
        }

        private static bool Occupied(Vector3 pos, float clearance, GameObject self)
        {
            var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
            foreach (GameObject root in scene.GetRootGameObjects())
            {
                foreach (Renderer r in root.GetComponentsInChildren<Renderer>(true))
                {
                    if (r == null) continue;
                    GameObject go = r.gameObject;
                    if (self != null && (go == self || go.transform.IsChildOf(self.transform))) continue;
                    if (Vector3.Distance(go.transform.position, pos) < clearance) return true;
                }
            }
            return false;
        }

        private static Camera GetCamera()
        {
            Camera cam = Camera.main;
            if (cam == null) cam = UnityEngine.Object.FindAnyObjectByType<Camera>();
            return cam;
        }

        private static bool Valid(float[] v) => v != null && v.Length >= 3;
        private static Vector3 V3(float[] v) => new Vector3(v[0], v[1], v[2]);

        private static PrimitiveType ParsePrimitive(string p)
        {
            switch (p)
            {
                case "sphere": return PrimitiveType.Sphere;
                case "capsule": return PrimitiveType.Capsule;
                case "cylinder": return PrimitiveType.Cylinder;
                case "plane": return PrimitiveType.Plane;
                case "quad": return PrimitiveType.Quad;
                default: return PrimitiveType.Cube;
            }
        }

        private static Color ParseColor(string s)
        {
            if (string.IsNullOrEmpty(s)) return Color.white;
            Color c;
            if (ColorUtility.TryParseHtmlString(s, out c)) return c;
            if (!s.StartsWith("#") && ColorUtility.TryParseHtmlString("#" + s, out c)) return c;
            return Color.white;
        }

        private static void SetColor(GameObject go, Color c)
        {
            Renderer r = go.GetComponent<Renderer>();
            if (r == null || r.sharedMaterial == null) return;
            Material mat = new Material(r.sharedMaterial);
            mat.color = c;
            if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", c); // URP
            r.sharedMaterial = mat;
        }

        private static string Capitalize(string s)
            => string.IsNullOrEmpty(s) ? s : char.ToUpper(s[0]) + s.Substring(1);
    }
}
