using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEditor;
using UnityEngine;
using VRC.SDKBase;
using BlackStartX.GestureManager.Editor.Modules.Vrc3;
using BlackStartX.GestureManager.Editor.Modules.Vrc3.Params;

[InitializeOnLoad]
internal static class GestureManagerExporter
{
    private static bool excludeAnimatorParameters = true;
    private static bool sortByDescriptor = true;

    static GestureManagerExporter()
    {
        var harmony = new Harmony("gay.lilyy.GestureManagerExporter");
        harmony.PatchAll();
    }

    [HarmonyPatch]
    private static class PatchParametersLayout
    {
        static MethodBase TargetMethod()
        {
            var nestedClass = typeof(ModuleVrc3).Assembly
                .GetType("BlackStartX.GestureManager.Editor.Modules.Vrc3.Vrc3Debug.Avatar.Vrc3AvatarDebugWindow+Static");

            return nestedClass?.GetMethod("ParametersLayout", BindingFlags.NonPublic | BindingFlags.Static);
        }

        static void Postfix(ModuleVrc3 module, float width)
        {
            excludeAnimatorParameters = GUILayout.Toggle(excludeAnimatorParameters, "Exclude Animator-Only Parameters", GUILayout.Width(width));
            sortByDescriptor = GUILayout.Toggle(sortByDescriptor, "Sort by Parameters File", GUILayout.Width(width));

            if (!GUILayout.Button("Export Parameters to JSON", GUILayout.Width(width))) return;

            var path = EditorUtility.SaveFilePanel(
                "Export Parameters",
                "Assets",
                "avatar_parameters.json",
                "json"
            );

            if (string.IsNullOrEmpty(path)) return;

            var parameters = new Dictionary<string, object>();

            var avatarDescriptor = module.AvatarDescriptor;

            foreach (var param in module.Params)
            {
                if (excludeAnimatorParameters) {
                    if (avatarDescriptor.expressionParameters.FindParameter(param.Key) == null) {
                        continue;
                    }
                }

                object value = param.Value.Type switch
                {
                    AnimatorControllerParameterType.Float => param.Value.FloatValue(),
                    AnimatorControllerParameterType.Int => param.Value.IntValue(),
                    AnimatorControllerParameterType.Bool => param.Value.BoolValue(),
                    AnimatorControllerParameterType.Trigger => param.Value.BoolValue(),
                    _ => throw new ArgumentOutOfRangeException(nameof(param.Value.Type), param.Value.Type, null)
                };

                parameters[param.Key] = new
                {
                    type = param.Value.TypeText,
                    value
                };
            }

            if (sortByDescriptor) {
                var orderedParams = new Dictionary<string, object>();

                var paramNamesInDescriptor = avatarDescriptor.expressionParameters.parameters
                    .Select(p => p.name)
                    .Where(name => parameters.ContainsKey(name));

                foreach (var name in paramNamesInDescriptor)
                    orderedParams[name] = parameters[name];

                var unmatchedParams = parameters.Keys
                    .Where(k => !orderedParams.ContainsKey(k))
                    .OrderBy(k => k);

                foreach (var name in unmatchedParams)
                    orderedParams[name] = parameters[name];

                parameters = orderedParams;
            }


            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                FloatFormatHandling = FloatFormatHandling.String, // We'll format floats ourselves
                Converters = new List<JsonConverter> { new Float2DecimalConverter() }
            };

            var json = JsonConvert.SerializeObject(parameters, settings);

            File.WriteAllText(path, json);

            EditorUtility.DisplayDialog("Export Complete", $"{parameters.Count} Parameters have been exported successfully!", "OK");
        }
    }
}

class Float2DecimalConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) =>
        objectType == typeof(float) || objectType == typeof(double);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is float f)
            writer.WriteRawValue(f.ToString("0.00"));
        else if (value is double d)
            writer.WriteRawValue(d.ToString("0.00"));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
        throw new NotImplementedException();
}
