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

namespace MCPForUnity.Editor.Tools.Graphics
{
    internal static class GraphicsHelpers
    {
        private static bool? _hasVolumeSystem;
        private static Type _volumeType;
        private static Type _volumeProfileType;
        private static Type _volumeComponentType;
        private static Type _volumeParameterType;

        internal static bool HasVolumeSystem
        {
            get
            {
                if (_hasVolumeSystem == null) DetectPackages();
                return _hasVolumeSystem.Value;
            }
        }

        internal static bool HasURP =>
            RenderPipelineUtility.GetActivePipeline() == RenderPipelineUtility.PipelineKind.Universal;

        internal static bool HasHDRP =>
            RenderPipelineUtility.GetActivePipeline() == RenderPipelineUtility.PipelineKind.HighDefinition;

        internal static Type VolumeType
        {
            get
            {
                if (_hasVolumeSystem == null) DetectPackages();
                return _volumeType;
            }
        }

        internal static Type VolumeProfileType
        {
            get
            {
                if (_hasVolumeSystem == null) DetectPackages();
                return _volumeProfileType;
            }
        }

        internal static Type VolumeComponentType
        {
            get
            {
                if (_hasVolumeSystem == null) DetectPackages();
                return _volumeComponentType;
            }
        }

        internal static Type VolumeParameterType
        {
            get
            {
                if (_hasVolumeSystem == null) DetectPackages();
                return _volumeParameterType;
            }
        }

        private static void DetectPackages()
        {
            _volumeType = Type.GetType("UnityEngine.Rendering.Volume, Unity.RenderPipelines.Core.Runtime");
            _volumeProfileType = Type.GetType("UnityEngine.Rendering.VolumeProfile, Unity.RenderPipelines.Core.Runtime");
            _volumeComponentType = Type.GetType("UnityEngine.Rendering.VolumeComponent, Unity.RenderPipelines.Core.Runtime");
            _volumeParameterType = Type.GetType("UnityEngine.Rendering.VolumeParameter, Unity.RenderPipelines.Core.Runtime");
            _hasVolumeSystem = _volumeType != null && _volumeProfileType != null;
        }

        internal static Type ResolveVolumeComponentType(string effectName)
        {
            if (string.IsNullOrEmpty(effectName) || VolumeComponentType == null)
                return null;

            var derivedTypes = TypeCache.GetTypesDerivedFrom(VolumeComponentType);
            foreach (var t in derivedTypes)
            {
                if (t.IsAbstract) continue;
                if (string.Equals(t.Name, effectName, StringComparison.OrdinalIgnoreCase))
                    return t;
            }
            return null;
        }

        internal static List<Type> GetAvailableEffectTypes()
        {
            if (VolumeComponentType == null)
                return new List<Type>();
            var derivedTypes = TypeCache.GetTypesDerivedFrom(VolumeComponentType);
            return derivedTypes
                .Where(t => !t.IsAbstract && !t.IsGenericType)
                .OrderBy(t => t.Name)
                .ToList();
        }

        internal static Component FindVolume(JObject @params)
        {
            var p = new ToolParams(@params);
            string target = p.Get("target");
            if (string.IsNullOrEmpty(target))
            {
                var allVolumes = UnityFindObjectsCompat.FindAll(VolumeType);
                return allVolumes.Length > 0 ? allVolumes[0] as Component : null;
            }

            if (int.TryParse(target, out int instanceId))
            {
                var byId = GameObjectLookup.ResolveInstanceID(instanceId) as GameObject;
                if (byId != null) return byId.GetComponent(VolumeType);
            }

            var go = GameObject.Find(target);
            if (go != null) return go.GetComponent(VolumeType);

            return null;
        }

        internal static string GetPipelineName()
        {
            return RenderPipelineUtility.GetActivePipeline() switch
            {
                RenderPipelineUtility.PipelineKind.Universal => "Universal (URP)",
                RenderPipelineUtility.PipelineKind.HighDefinition => "High Definition (HDRP)",
                RenderPipelineUtility.PipelineKind.BuiltIn => "Built-in",
                RenderPipelineUtility.PipelineKind.Custom => "Custom",
                _ => "Unknown"
            };
        }

        internal static object ReadSerializedValue(SerializedProperty prop)
        {
            return prop.propertyType switch
            {
                SerializedPropertyType.Boolean => prop.boolValue,
                SerializedPropertyType.Integer => prop.type == "long" ? prop.longValue : (object)prop.intValue,
                SerializedPropertyType.Float => prop.floatValue,
                SerializedPropertyType.String => prop.stringValue,
                SerializedPropertyType.Enum => prop.enumValueIndex < prop.enumNames.Length
                    ? prop.enumNames[prop.enumValueIndex]
                    : (object)prop.enumValueIndex,
                SerializedPropertyType.ObjectReference => prop.objectReferenceValue != null
                    ? (object)new
                    {
                        name = prop.objectReferenceValue.name,
                        path = AssetDatabase.GetAssetPath(prop.objectReferenceValue)
                    }
                    : null,
                SerializedPropertyType.Color => new[] { prop.colorValue.r, prop.colorValue.g, prop.colorValue.b, prop.colorValue.a },
                SerializedPropertyType.Vector2 => new[] { prop.vector2Value.x, prop.vector2Value.y },
                SerializedPropertyType.Vector3 => new[] { prop.vector3Value.x, prop.vector3Value.y, prop.vector3Value.z },
                SerializedPropertyType.LayerMask => prop.intValue,
                _ => prop.propertyType.ToString()
            };
        }

        internal static bool SetSerializedValue(SerializedProperty prop, JToken value)
        {
            try
            {
                switch (prop.propertyType)
                {
                    case SerializedPropertyType.Boolean:
                        prop.boolValue = ParamCoercion.CoerceBool(value, false);
                        return true;
                    case SerializedPropertyType.Integer:
                        if (prop.type == "long")
                            prop.longValue = ParamCoercion.CoerceLong(value, 0);
                        else
                            prop.intValue = ParamCoercion.CoerceInt(value, 0);
                        return true;
                    case SerializedPropertyType.Float:
                        prop.floatValue = ParamCoercion.CoerceFloat(value, 0f);
                        return true;
                    case SerializedPropertyType.String:
                        prop.stringValue = value.ToString();
                        return true;
                    case SerializedPropertyType.Enum:
                        if (value.Type == JTokenType.String)
                        {
                            for (int i = 0; i < prop.enumNames.Length; i++)
                            {
                                if (string.Equals(prop.enumNames[i], value.ToString(), StringComparison.OrdinalIgnoreCase))
                                { prop.enumValueIndex = i; return true; }
                            }
                        }
                        prop.enumValueIndex = ParamCoercion.CoerceInt(value, 0);
                        return true;
                    case SerializedPropertyType.ObjectReference:
                        if (value.Type == JTokenType.String)
                        {
                            string path = value.ToString();
                            var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
                            if (asset != null) { prop.objectReferenceValue = asset; return true; }
                        }
                        else if (value.Type == JTokenType.Object)
                        {
                            string path = value["path"]?.ToString();
                            if (!string.IsNullOrEmpty(path))
                            {
                                var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
                                if (asset != null) { prop.objectReferenceValue = asset; return true; }
                            }
                        }
                        else if (value.Type == JTokenType.Null)
                        {
                            prop.objectReferenceValue = null;
                            return true;
                        }
                        return false;
                    case SerializedPropertyType.Color:
                        if (value is JArray colorArr && colorArr.Count >= 3)
                        {
                            prop.colorValue = new Color(
                                (float)colorArr[0], (float)colorArr[1], (float)colorArr[2],
                                colorArr.Count >= 4 ? (float)colorArr[3] : 1f);
                            return true;
                        }
                        return false;
                    case SerializedPropertyType.Vector2:
                        if (value is JArray v2Arr && v2Arr.Count >= 2)
                        {
                            prop.vector2Value = new Vector2((float)v2Arr[0], (float)v2Arr[1]);
                            return true;
                        }
                        return false;
                    case SerializedPropertyType.Vector3:
                        if (value is JArray v3Arr && v3Arr.Count >= 3)
                        {
                            prop.vector3Value = new Vector3((float)v3Arr[0], (float)v3Arr[1], (float)v3Arr[2]);
                            return true;
                        }
                        return false;
                    case SerializedPropertyType.LayerMask:
                        prop.intValue = ParamCoercion.CoerceInt(value, 0);
                        return true;
                    default:
                        return false;
                }
            }
            catch { return false; }
        }

        internal static void MarkDirty(UnityEngine.Object obj)
        {
            if (obj == null) return;
            EditorUtility.SetDirty(obj);
            if (obj is Component comp)
            {
                var prefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
                if (prefabStage != null)
                    UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(prefabStage.scene);
                else
                    UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(comp.gameObject.scene);
            }
        }
    }
}
