using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.AssetImporters;
using UnityEngine;
using UnityObject = UnityEngine.Object;

namespace AssetStoreTools.Validator.TestMethods.Utility
{
    internal static class ModelUtility
    {
        public class LogEntry
        {
            public string Message;
            public LogType Severity;
        }

#if !UNITY_2022_2_OR_NEWER
        // Rig fields
        private const string RigImportWarningsField = "m_RigImportWarnings";
        private const string RigImportErrorsField = "m_RigImportErrors";

        // Animation fields
        private const string AnimationImportWarningsField = "m_AnimationImportWarnings";
        private const string AnimationImportErrorsField = "m_AnimationImportErrors";

        private static Editor _modelImporterEditor = null;
#endif

        public static Dictionary<UnityObject, List<LogEntry>> GetImportLogs(params UnityObject[] models)
        {
#if UNITY_2022_2_OR_NEWER
            return GetImportLogsDefault(models);
#else
            return GetImportLogsLegacy(models);
#endif
        }

#if UNITY_2022_2_OR_NEWER
        private static Dictionary<UnityObject, List<LogEntry>> GetImportLogsDefault(params UnityObject[] models)
        {
            var modelsWithLogs = new Dictionary<UnityObject, List<LogEntry>>();

            foreach (var model in models)
            {
                var modelLogs = new List<LogEntry>();

                var importLog = AssetImporter.GetImportLog(AssetUtility.ObjectToAssetPath(model));

                if (importLog == null)
                    continue;

                var entries = importLog.logEntries.Where(x => x.flags.HasFlag(ImportLogFlags.Warning) || x.flags.HasFlag(ImportLogFlags.Error));
                foreach (var entry in entries)
                {
                    var severity = entry.flags.HasFlag(ImportLogFlags.Error) ? LogType.Error : LogType.Warning;
                    modelLogs.Add(new LogEntry() { Message = entry.message, Severity = severity });
                }

                if (modelLogs.Count > 0)
                    modelsWithLogs.Add(model, modelLogs);
            }

            return modelsWithLogs;
        }
#endif

#if !UNITY_2022_2_OR_NEWER

        private static Dictionary<UnityObject, List<LogEntry>> GetImportLogsLegacy(params UnityObject[] models)
        {
            var modelsWithLogs = new Dictionary<UnityObject, List<LogEntry>>();

            foreach (var model in models)
            {
                var modelLogs = new List<LogEntry>();

                // Load the Model Importer
                var modelImporter = AssetUtility.GetAssetImporter(model) as ModelImporter;

                var editorAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.GetName().Name.Equals("UnityEditor"));

                var modelImporterEditorType = editorAssembly.GetType("UnityEditor.ModelImporterEditor");

                // Load its Model Importer Editor
                Editor.CreateCachedEditorWithContext(new UnityObject[] { modelImporter }, model, modelImporterEditorType, ref _modelImporterEditor);

                // Find the base type
                var modelImporterEditorTypeBase = _modelImporterEditor.GetType().BaseType;

                // Get the tabs value
                var tabsArrayType = modelImporterEditorTypeBase.GetRuntimeProperties().FirstOrDefault(x => x.Name == "tabs");
                var tabsArray = (Array)tabsArrayType.GetValue(_modelImporterEditor);

                // Get the tabs (Model | Rig | Animation | Materials)
                var rigTab = tabsArray.GetValue(1);
                var animationTab = tabsArray.GetValue(2);

                var rigErrorsCheckSuccess = CheckFieldForSerializedProperty(rigTab, RigImportErrorsField, out var rigErrors);
                var rigWarningsCheckSuccess = CheckFieldForSerializedProperty(rigTab, RigImportWarningsField, out var rigWarnings);
                var animationErrorsCheckSuccess = CheckFieldForSerializedProperty(animationTab, AnimationImportErrorsField, out var animationErrors);
                var animationWarningsCheckSuccess = CheckFieldForSerializedProperty(animationTab, AnimationImportWarningsField, out var animationWarnings);

                if (!rigErrorsCheckSuccess || !rigWarningsCheckSuccess || !animationErrorsCheckSuccess || !animationWarningsCheckSuccess)
                    UnityEngine.Debug.LogWarning($"An error was encountered when checking import logs for model '{model.name}'");

                if (!string.IsNullOrEmpty(rigWarnings))
                    modelLogs.Add(new LogEntry() { Message = rigWarnings, Severity = LogType.Warning });
                if (!string.IsNullOrEmpty(rigErrors))
                    modelLogs.Add(new LogEntry() { Message = rigErrors, Severity = LogType.Error });
                if (!string.IsNullOrEmpty(animationWarnings))
                    modelLogs.Add(new LogEntry() { Message = animationWarnings, Severity = LogType.Warning });
                if (!string.IsNullOrEmpty(animationErrors))
                    modelLogs.Add(new LogEntry() { Message = animationErrors, Severity = LogType.Error });

                if (modelLogs.Count > 0)
                    modelsWithLogs.Add(model, modelLogs);
            }

            return modelsWithLogs;
        }

        private static bool CheckFieldForSerializedProperty(object source, string propertyName, out string message)
        {
            message = string.Empty;

            try
            {
                var propertyType = source.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
                var propertyValue = propertyType.GetValue(source) as SerializedProperty;
                message = propertyValue.stringValue;
                return true;
            }
            catch
            {
                return false;
            }
        }
#endif
    }
}