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

namespace MCPForUnity.Editor.Tools.Physics
{
    internal static class PhysicsSettingsOps
    {
        public static object Ping(JObject @params)
        {
            var gravity3d = UnityEngine.Physics.gravity;
            var gravity2d = Physics2D.gravity;
            var simMode = UnityPhysicsCompat.GetPhysicsSimulationMode().ToString();

            return new
            {
                success = true,
                message = "Physics tool ready.",
                data = new
                {
                    gravity3d = new[] { gravity3d.x, gravity3d.y, gravity3d.z },
                    gravity2d = new[] { gravity2d.x, gravity2d.y },
                    simulationMode = simMode,
                    defaultSolverIterations = UnityEngine.Physics.defaultSolverIterations,
                    defaultSolverVelocityIterations = UnityEngine.Physics.defaultSolverVelocityIterations,
                    bounceThreshold = UnityEngine.Physics.bounceThreshold,
                    sleepThreshold = UnityEngine.Physics.sleepThreshold,
                    defaultContactOffset = UnityEngine.Physics.defaultContactOffset,
                    queriesHitTriggers = UnityEngine.Physics.queriesHitTriggers
                }
            };
        }

        public static object GetSettings(JObject @params)
        {
            var p = new ToolParams(@params);
            string dimension = (p.Get("dimension") ?? "3d").ToLowerInvariant();

            if (dimension == "2d")
            {
                var g = Physics2D.gravity;
                return new
                {
                    success = true,
                    message = "Physics2D settings retrieved.",
                    data = new
                    {
                        dimension = "2d",
                        gravity = new[] { g.x, g.y },
                        velocityIterations = Physics2D.velocityIterations,
                        positionIterations = Physics2D.positionIterations,
                        queriesHitTriggers = Physics2D.queriesHitTriggers,
                        queriesStartInColliders = Physics2D.queriesStartInColliders,
                        callbacksOnDisable = Physics2D.callbacksOnDisable,
                        autoSyncTransforms = UnityPhysicsCompat.GetPhysics2DAutoSyncTransforms()
                    }
                };
            }

            if (dimension != "3d")
                return new ErrorResponse($"Invalid dimension: '{dimension}'. Use '3d' or '2d'.");

            var g3 = UnityEngine.Physics.gravity;
            var simMode = UnityPhysicsCompat.GetPhysicsSimulationMode().ToString();

            return new
            {
                success = true,
                message = "Physics3D settings retrieved.",
                data = new
                {
                    dimension = "3d",
                    gravity = new[] { g3.x, g3.y, g3.z },
                    defaultContactOffset = UnityEngine.Physics.defaultContactOffset,
                    sleepThreshold = UnityEngine.Physics.sleepThreshold,
                    defaultSolverIterations = UnityEngine.Physics.defaultSolverIterations,
                    defaultSolverVelocityIterations = UnityEngine.Physics.defaultSolverVelocityIterations,
                    bounceThreshold = UnityEngine.Physics.bounceThreshold,
                    defaultMaxAngularSpeed = UnityEngine.Physics.defaultMaxAngularSpeed,
                    queriesHitTriggers = UnityEngine.Physics.queriesHitTriggers,
                    queriesHitBackfaces = UnityEngine.Physics.queriesHitBackfaces,
                    simulationMode = simMode,
                    autoSyncTransforms = UnityPhysicsCompat.GetPhysicsAutoSyncTransforms()
                }
            };
        }

        public static object SetSettings(JObject @params)
        {
            var p = new ToolParams(@params);
            string dimension = (p.Get("dimension") ?? "3d").ToLowerInvariant();
            var settings = p.GetRaw("settings") as JObject;

            if (settings == null || settings.Count == 0)
                return new ErrorResponse("'settings' parameter is required and must be a non-empty object.");

            if (dimension != "3d" && dimension != "2d")
                return new ErrorResponse($"Invalid dimension: '{dimension}'. Use '3d' or '2d'.");

            if (dimension == "2d")
                return SetSettings2D(settings);

            return SetSettings3D(settings);
        }

        private static readonly HashSet<string> Valid3DKeys = new HashSet<string>
        {
            "gravity", "defaultcontactoffset", "sleepthreshold",
            "defaultsolveriterations", "defaultsolvervelocityiterations",
            "bouncethreshold", "defaultmaxangularspeed",
            "querieshittriggers", "querieshitbackfaces", "simulationmode",
            "autosynctransforms"
        };

        private static object SetSettings3D(JObject settings)
        {
            // Validate all keys before applying any changes
            var unknown = new List<string>();
            foreach (var prop in settings.Properties())
            {
                if (!Valid3DKeys.Contains(prop.Name.ToLowerInvariant()))
                    unknown.Add(prop.Name);
            }
            if (unknown.Count > 0)
                return new ErrorResponse(
                    $"Unknown 3D physics setting(s): {string.Join(", ", unknown)}.");

            var changed = new List<string>();

            foreach (var prop in settings.Properties())
            {
                string key = prop.Name.ToLowerInvariant();
                switch (key)
                {
                    case "gravity":
                    {
                        var arr = prop.Value as JArray;
                        if (arr == null || arr.Count < 3)
                            return new ErrorResponse("3D gravity requires [x, y, z] array.");
                        UnityEngine.Physics.gravity = new Vector3(
                            arr[0].Value<float>(), arr[1].Value<float>(), arr[2].Value<float>());
                        changed.Add("gravity");
                        break;
                    }
                    case "defaultcontactoffset":
                        UnityEngine.Physics.defaultContactOffset = prop.Value.Value<float>();
                        changed.Add("defaultContactOffset");
                        break;
                    case "sleepthreshold":
                        UnityEngine.Physics.sleepThreshold = prop.Value.Value<float>();
                        changed.Add("sleepThreshold");
                        break;
                    case "defaultsolveriterations":
                        UnityEngine.Physics.defaultSolverIterations = prop.Value.Value<int>();
                        changed.Add("defaultSolverIterations");
                        break;
                    case "defaultsolvervelocityiterations":
                        UnityEngine.Physics.defaultSolverVelocityIterations = prop.Value.Value<int>();
                        changed.Add("defaultSolverVelocityIterations");
                        break;
                    case "bouncethreshold":
                        UnityEngine.Physics.bounceThreshold = prop.Value.Value<float>();
                        changed.Add("bounceThreshold");
                        break;
                    case "defaultmaxangularspeed":
                        UnityEngine.Physics.defaultMaxAngularSpeed = prop.Value.Value<float>();
                        changed.Add("defaultMaxAngularSpeed");
                        break;
                    case "querieshittriggers":
                        UnityEngine.Physics.queriesHitTriggers = prop.Value.Value<bool>();
                        changed.Add("queriesHitTriggers");
                        break;
                    case "querieshitbackfaces":
                        UnityEngine.Physics.queriesHitBackfaces = prop.Value.Value<bool>();
                        changed.Add("queriesHitBackfaces");
                        break;
                    case "simulationmode":
                    {
                        string modeStr = prop.Value.ToString();
                        if (!System.Enum.TryParse<UnityPhysicsCompat.SimulationMode>(modeStr, true, out var mode)
                            || mode == UnityPhysicsCompat.SimulationMode.Unknown)
                        {
                            return new ErrorResponse(
                                $"Invalid simulationMode: '{modeStr}'. Valid: FixedUpdate, Update, Script.");
                        }
                        if (!UnityPhysicsCompat.TrySetPhysicsSimulationMode(mode))
                        {
                            return new ErrorResponse(
                                $"simulationMode '{modeStr}' is not supported on this Unity version.");
                        }
                        changed.Add("simulationMode");
                        break;
                    }
                    case "autosynctransforms":
                        if (UnityPhysicsCompat.TrySetPhysicsAutoSyncTransforms(prop.Value.Value<bool>()))
                        {
                            changed.Add("autoSyncTransforms");
                        }
                        break;
                }
            }

            MarkDynamicsManagerDirty();

            return new
            {
                success = true,
                message = $"Updated {changed.Count} physics 3D setting(s).",
                data = new { changed }
            };
        }

        private static readonly HashSet<string> Valid2DKeys = new HashSet<string>
        {
            "gravity", "velocityiterations", "positioniterations",
            "querieshittriggers", "queriesstartincolliders",
            "callbacksondisable", "autosynctransforms"
        };

        private static object SetSettings2D(JObject settings)
        {
            // Validate all keys before applying any changes
            var unknown = new List<string>();
            foreach (var prop in settings.Properties())
            {
                if (!Valid2DKeys.Contains(prop.Name.ToLowerInvariant()))
                    unknown.Add(prop.Name);
            }
            if (unknown.Count > 0)
                return new ErrorResponse(
                    $"Unknown 2D physics setting(s): {string.Join(", ", unknown)}.");

            var changed = new List<string>();

            foreach (var prop in settings.Properties())
            {
                string key = prop.Name.ToLowerInvariant();
                switch (key)
                {
                    case "gravity":
                    {
                        var arr = prop.Value as JArray;
                        if (arr == null || arr.Count < 2)
                            return new ErrorResponse("2D gravity requires [x, y] array.");
                        Physics2D.gravity = new Vector2(
                            arr[0].Value<float>(), arr[1].Value<float>());
                        changed.Add("gravity");
                        break;
                    }
                    case "velocityiterations":
                        Physics2D.velocityIterations = prop.Value.Value<int>();
                        changed.Add("velocityIterations");
                        break;
                    case "positioniterations":
                        Physics2D.positionIterations = prop.Value.Value<int>();
                        changed.Add("positionIterations");
                        break;
                    case "querieshittriggers":
                        Physics2D.queriesHitTriggers = prop.Value.Value<bool>();
                        changed.Add("queriesHitTriggers");
                        break;
                    case "queriesstartincolliders":
                        Physics2D.queriesStartInColliders = prop.Value.Value<bool>();
                        changed.Add("queriesStartInColliders");
                        break;
                    case "callbacksondisable":
                        Physics2D.callbacksOnDisable = prop.Value.Value<bool>();
                        changed.Add("callbacksOnDisable");
                        break;
                    case "autosynctransforms":
                        if (UnityPhysicsCompat.TrySetPhysics2DAutoSyncTransforms(prop.Value.Value<bool>()))
                        {
                            changed.Add("autoSyncTransforms");
                        }
                        break;
                }
            }

            MarkPhysics2DSettingsDirty();

            return new
            {
                success = true,
                message = $"Updated {changed.Count} physics 2D setting(s).",
                data = new { changed }
            };
        }

        private static void MarkDynamicsManagerDirty()
        {
            var assets = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/DynamicsManager.asset");
            if (assets != null && assets.Length > 0)
                EditorUtility.SetDirty(assets[0]);
        }

        private static void MarkPhysics2DSettingsDirty()
        {
            var assets = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/Physics2DSettings.asset");
            if (assets != null && assets.Length > 0)
                EditorUtility.SetDirty(assets[0]);
        }
    }
}
