using System;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Runtime.Helpers;

namespace MCPForUnity.Editor.Tools.Vfx
{
    internal static class ParticleControl
    {
        public static object Create(JObject @params)
        {
            string target = @params["target"]?.ToString();
            if (string.IsNullOrWhiteSpace(target))
            {
                return new { success = false, message = "target is required for particle_create" };
            }

            GameObject go = ManageVfxCommon.FindTargetGameObject(@params);
            bool createdGameObject = false;
            bool addedParticleSystem = false;

            if (go == null)
            {
                string objectName = target;
                int slashIndex = target.LastIndexOf('/');
                if (slashIndex >= 0 && slashIndex < target.Length - 1)
                {
                    objectName = target.Substring(slashIndex + 1);
                }

                go = new GameObject(objectName);
                createdGameObject = true;

                if (!EditorApplication.isPlaying)
                {
                    Undo.RegisterCreatedObjectUndo(go, $"Create {objectName}");
                }
            }

            if (@params["position"] != null)
            {
                go.transform.position = ManageVfxCommon.ParseVector3(@params["position"]);
            }
            if (@params["rotation"] != null)
            {
                go.transform.eulerAngles = ManageVfxCommon.ParseVector3(@params["rotation"]);
            }
            if (@params["scale"] != null)
            {
                go.transform.localScale = ManageVfxCommon.ParseVector3(@params["scale"]);
            }

            var ps = go.GetComponent<ParticleSystem>();
            if (ps == null)
            {
                ps = go.AddComponent<ParticleSystem>();
                addedParticleSystem = true;

                // Apply sensible defaults so newly created particles aren't oversized.
                RendererHelpers.SetSensibleParticleDefaults(ps);
            }

            var renderer = go.GetComponent<ParticleSystemRenderer>();
            if (renderer != null)
            {
                RendererHelpers.EnsureMaterial(renderer);
            }

            // Allow caller overrides for playOnAwake and looping.
            var main = ps.main;
            if (@params["playOnAwake"] != null)
            {
                main.playOnAwake = @params["playOnAwake"].ToObject<bool>();
            }
            if (@params["looping"] != null)
            {
                main.loop = @params["looping"].ToObject<bool>();
            }

            EditorUtility.SetDirty(go);
            if (!EditorApplication.isPlaying)
            {
                UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
                    UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
            }

            return new
            {
                success = true,
                message = $"ParticleSystem ready on '{go.name}'",
                target = go.name,
                targetId = go.GetInstanceIDCompat(),
                createdGameObject,
                addedParticleSystem,
                assignedMaterial = renderer?.sharedMaterial?.name
            };
        }

        public static object EnableModule(JObject @params)
        {
            ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
            if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };

            string moduleName = @params["module"]?.ToString()?.ToLowerInvariant();
            bool enabled = @params["enabled"]?.ToObject<bool>() ?? true;

            if (string.IsNullOrEmpty(moduleName)) return new { success = false, message = "Module name required" };

            Undo.RecordObject(ps, $"Toggle {moduleName}");

            switch (moduleName.Replace("_", ""))
            {
                case "emission": var em = ps.emission; em.enabled = enabled; break;
                case "shape": var sh = ps.shape; sh.enabled = enabled; break;
                case "coloroverlifetime": var col = ps.colorOverLifetime; col.enabled = enabled; break;
                case "sizeoverlifetime": var sol = ps.sizeOverLifetime; sol.enabled = enabled; break;
                case "velocityoverlifetime": var vol = ps.velocityOverLifetime; vol.enabled = enabled; break;
                case "noise": var n = ps.noise; n.enabled = enabled; break;
                case "collision": var coll = ps.collision; coll.enabled = enabled; break;
                case "trails": var tr = ps.trails; tr.enabled = enabled; break;
                case "lights": var li = ps.lights; li.enabled = enabled; break;
                default: return new { success = false, message = $"Unknown module: {moduleName}" };
            }

            EditorUtility.SetDirty(ps);
            return new { success = true, message = $"Module '{moduleName}' {(enabled ? "enabled" : "disabled")}" };
        }

        public static object Control(JObject @params, string action)
        {
            ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
            if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };

            RendererHelpers.EnsureMaterialResult ensureResult = default;
            bool materialChecked = false;

            // Ensure material is assigned before playing
            if (action == "play" || action == "restart")
            {
                var renderer = ParticleCommon.FindParticleSystemRenderer(ps);
                if (renderer != null)
                {
                    ensureResult = RendererHelpers.EnsureMaterial(renderer);
                    materialChecked = true;
                }
            }

            bool withChildren = @params["withChildren"]?.ToObject<bool>() ?? true;

            switch (action)
            {
                case "play": ps.Play(withChildren); break;
                case "stop": ps.Stop(withChildren, ParticleSystemStopBehavior.StopEmitting); break;
                case "pause": ps.Pause(withChildren); break;
                case "restart": ps.Stop(withChildren, ParticleSystemStopBehavior.StopEmittingAndClear); ps.Play(withChildren); break;
                case "clear": ps.Clear(withChildren); break;
                default: return new { success = false, message = $"Unknown action: {action}" };
            }

            return new
            {
                success = true,
                message = $"ParticleSystem {action}",
                materialReplaced = materialChecked ? ensureResult.MaterialReplaced : false,
                replacementReason = materialChecked ? ensureResult.ReplacementReason : string.Empty,
            };
        }

        public static object AddBurst(JObject @params)
        {
            ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
            if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };

            // Ensure material is assigned
            var renderer = ParticleCommon.FindParticleSystemRenderer(ps);
            RendererHelpers.EnsureMaterialResult ensureResult = default;
            bool materialChecked = false;
            if (renderer != null)
            {
                ensureResult = RendererHelpers.EnsureMaterial(renderer);
                materialChecked = true;
            }

            Undo.RecordObject(ps, "Add Burst");
            var emission = ps.emission;

            float time = @params["time"]?.ToObject<float>() ?? 0f;
            int minCountRaw = @params["minCount"]?.ToObject<int>() ?? @params["count"]?.ToObject<int>() ?? 30;
            int maxCountRaw = @params["maxCount"]?.ToObject<int>() ?? @params["count"]?.ToObject<int>() ?? 30;
            short minCount = (short)Math.Clamp(minCountRaw, 0, short.MaxValue);
            short maxCount = (short)Math.Clamp(maxCountRaw, 0, short.MaxValue);
            int cycles = @params["cycles"]?.ToObject<int>() ?? 1;
            float interval = @params["interval"]?.ToObject<float>() ?? 0.01f;

            var burst = new ParticleSystem.Burst(time, minCount, maxCount, cycles, interval);
            burst.probability = @params["probability"]?.ToObject<float>() ?? 1f;

            int idx = emission.burstCount;
            var bursts = new ParticleSystem.Burst[idx + 1];
            emission.GetBursts(bursts);
            bursts[idx] = burst;
            emission.SetBursts(bursts);

            EditorUtility.SetDirty(ps);
            return new
            {
                success = true,
                message = $"Added burst at t={time}",
                burstIndex = idx,
                materialReplaced = materialChecked ? ensureResult.MaterialReplaced : false,
                replacementReason = materialChecked ? ensureResult.ReplacementReason : string.Empty,
            };
        }

        public static object ClearBursts(JObject @params)
        {
            ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
            if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };

            Undo.RecordObject(ps, "Clear Bursts");
            var emission = ps.emission;
            int count = emission.burstCount;
            emission.SetBursts(new ParticleSystem.Burst[0]);

            EditorUtility.SetDirty(ps);
            return new { success = true, message = $"Cleared {count} bursts" };
        }
    }
}
