using System.Collections.Generic;
using UnityEngine;
using VRC.SDK3.Avatars.ScriptableObjects;
using VRC.SDKBase;
#if UNITY_EDITOR
using UnityEditor;
using System.Linq;
using VRC.SDK3.Avatars.Components;
#endif

namespace gay.lilyy.aacshared.runtimecomponents
{
    [System.Serializable]
    public class PresetParameter
    {
        public string name;
        public VRCExpressionParameters.ValueType valueType;
        public float setTo;
        public bool shouldChange = false;
    }

    [AddComponentMenu("LillithRosePup/Parameter Preset")]
    public class ParameterPresetDefinition : MonoBehaviour, IEditorOnly
    {
        public string Name = "New Preset";
        public string Group = "Default";
        [HideInInspector] // custom inspector
        public List<PresetParameter> Parameters;

        // unity needs this to show the enable/disable box
        void Start() { }
    }

#if UNITY_EDITOR
    [CustomEditor(typeof(ParameterPresetDefinition))]
    public class ParameterPresetDefinitionInspector : Editor
    {
        private ParameterPresetDefinition preset;
        private bool parametersLoaded = false;

        private void OnEnable()
        {
            preset = (ParameterPresetDefinition)target;
            LoadMissingParameters();
        }

        private void LoadMissingParameters()
        {
            VRCAvatarDescriptor aviDesc = preset.gameObject.GetComponent<VRCAvatarDescriptor>();
            if (aviDesc == null || aviDesc.expressionParameters == null || parametersLoaded)
                return;

            // Initialize Parameters list if it's null
            if (preset.Parameters == null)
                preset.Parameters = new List<PresetParameter>();

            // Get parameters from the Group's ParametersFile
            var sourceParameters = aviDesc.expressionParameters.parameters;
            if (sourceParameters == null)
                return;

            // Create a dictionary of source parameters for quick lookup
            var sourceParamDict = sourceParameters.ToDictionary(p => p.name, p => p);

            // Create a set of source parameter names
            var sourceParameterNames = new HashSet<string>(sourceParamDict.Keys);

            // Remove parameters that exist in preset but not in source
            bool hasChanges = false;
            preset.Parameters.RemoveAll(presetParam =>
            {
                if (!sourceParameterNames.Contains(presetParam.name))
                {
                    hasChanges = true;
                    return true; // Remove this parameter
                }
                return false; // Keep this parameter
            });

            // Add missing parameters and update existing ones
            foreach (var sourceParam in sourceParameters)
            {
                var existingParam = preset.Parameters.FirstOrDefault(p => p.name == sourceParam.name);

                if (existingParam == null)
                {
                    // Add new parameter
                    var clonedParam = new PresetParameter
                    {
                        name = sourceParam.name,
                        valueType = sourceParam.valueType,
                        setTo = sourceParam.defaultValue,
                        shouldChange = false,
                    };

                    preset.Parameters.Add(clonedParam);
                    hasChanges = true;
                }
                else
                {
                    // Update existing parameter to match source type
                    if (existingParam.valueType != sourceParam.valueType)
                    {
                        existingParam.valueType = sourceParam.valueType;
                        hasChanges = true;
                    }
                }
            }

            if (hasChanges)
            {
                EditorUtility.SetDirty(preset);
                Debug.Log($"Updated parameters in preset '{preset.Name}' to match source parameters file");
            }

            parametersLoaded = true;
        }

        private int GetPresetIndex()
        {
            VRCAvatarDescriptor aviDesc = preset.gameObject.GetComponent<VRCAvatarDescriptor>();
            if (aviDesc == null)
                return -1;

            var presets = aviDesc.GetComponentsInChildren<ParameterPresetDefinition>().ToList().Where(p => p.Group == preset.Group).ToList();
            for (int i = 0; i < presets.Count; i++)
            {
                if (presets[i] == preset)
                {
                    return i + 1; // 1-based index matching ParameterPreset.cs
                }
            }
            return -1;
        }

        public override void OnInspectorGUI()
        {
            // Ensure parameters are loaded when inspector is drawn
            LoadMissingParameters();

            // Draw the default inspector
            DrawDefaultInspector();

            // Display preset index
            int presetIndex = GetPresetIndex();
            if (presetIndex > 0)
            {
                EditorGUILayout.Space();
                EditorGUILayout.HelpBox($"Preset Index: {presetIndex}\n(This preset will be activated when the 'Preset {preset.Group}' parameter equals {presetIndex})", MessageType.Info);
            }

            if (GUILayout.Button("Copy Parameter Name")) {
                GUIUtility.systemCopyBuffer = "Preset " + preset.Group;
                EditorGUILayout.LabelField("Parameter Name Copied to Clipboard", EditorStyles.boldLabel);
            }

            // Custom parameter list rendering
            if (preset.Parameters != null && preset.Parameters.Count > 0)
            {
                EditorGUILayout.Space();
                EditorGUILayout.LabelField("Parameters", EditorStyles.boldLabel);

                for (int i = 0; i < preset.Parameters.Count; i++)
                {
                    var param = preset.Parameters[i];
                    EditorGUILayout.BeginHorizontal();

                    // Parameter name
                    EditorGUILayout.LabelField(param.name, GUILayout.Width(150));

                    // Parameter type (read-only)
                    EditorGUILayout.LabelField($"Type: {param.valueType}", GUILayout.Width(100));

                    // Should change toggle
                    param.shouldChange = EditorGUILayout.Toggle(param.shouldChange, GUILayout.Width(60));

                    // Set to value (only show if shouldChange is true)
                    if (param.shouldChange)
                    {
                        switch (param.valueType)
                        {
                            case VRCExpressionParameters.ValueType.Bool:
                                param.setTo = EditorGUILayout.Toggle(param.setTo > 0.5f) ? 1f : 0f;
                                break;
                            case VRCExpressionParameters.ValueType.Int:
                                param.setTo = EditorGUILayout.IntField((int)param.setTo);
                                break;
                            case VRCExpressionParameters.ValueType.Float:
                                param.setTo = EditorGUILayout.FloatField(param.setTo);
                                break;
                        }
                    }
                    else
                    {
                        EditorGUILayout.LabelField("(unchanged)", GUILayout.Width(80));
                    }

                    EditorGUILayout.EndHorizontal();
                }
            }

            // Add a button to manually reload parameters
            EditorGUILayout.Space();
            if (GUILayout.Button("Reload Parameters from Group"))
            {
                parametersLoaded = false;
                LoadMissingParameters();
            }

            // Add a button to clear all parameters
            if (GUILayout.Button("Clear All Parameters"))
            {
                if (EditorUtility.DisplayDialog("Clear Parameters",
                    "Are you sure you want to clear all parameters from this preset?",
                    "Yes", "No"))
                {
                    preset.Parameters.Clear();
                    EditorUtility.SetDirty(preset);
                }
            }
        }
    }
#endif
}
