#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;

namespace IGP.UnitySDK.Editor
{
    /// <summary>
    /// Reflection-based runner for package runtime tests.
    /// Keeps the editor assembly decoupled from the Test Framework assembly.
    /// </summary>
    public static class IGPTestRunner
    {
        private const string TestsAssemblyName = "IGP.UnitySDK.Tests";

        [MenuItem("IGP SDK/Run All Runtime Tests")]
        public static void RunAllTests()
        {
            RunTests(methodName => true, "All Runtime Tests");
        }

        [MenuItem("IGP SDK/Run Room Tests")]
        public static void RunRoomTests()
        {
            RunTests(methodName => methodName.Contains("Room", StringComparison.OrdinalIgnoreCase), "Room Tests");
        }

        [MenuItem("IGP SDK/Run Session Tests")]
        public static void RunSessionTests()
        {
            RunTests(methodName => methodName.Contains("Session", StringComparison.OrdinalIgnoreCase)
                || methodName.Contains("LaunchTicket", StringComparison.OrdinalIgnoreCase), "Session Tests");
        }

        private static void RunTests(Func<string, bool> filter, string label)
        {
            var testsAssembly = AppDomain.CurrentDomain
                .GetAssemblies()
                .FirstOrDefault(assembly => assembly.GetName().Name == TestsAssemblyName);

            if (testsAssembly == null)
            {
                Debug.LogWarning($"[IGP Tests] Assembly '{TestsAssemblyName}' not found. Import the test assembly first.");
                return;
            }

            var passed = 0;
            var total = 0;

            foreach (var testType in testsAssembly.GetTypes().Where(ContainsTestMethods))
            {
                var instance = Activator.CreateInstance(testType);
                if (instance == null)
                {
                    continue;
                }

                var setupMethod = testType.GetMethod("SetUp", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                var teardownMethod = testType.GetMethod("TearDown", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

                foreach (var method in EnumerateTestMethods(testType).Where(method => filter(method.Name)))
                {
                    total++;

                    try
                    {
                        setupMethod?.Invoke(instance, null);
                        method.Invoke(instance, null);
                        passed++;
                        Debug.Log($"[IGP Tests] PASS {testType.Name}.{method.Name}");
                    }
                    catch (TargetInvocationException invocationException)
                    {
                        var cause = invocationException.InnerException?.Message ?? invocationException.Message;
                        Debug.LogError($"[IGP Tests] FAIL {testType.Name}.{method.Name}: {cause}");
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError($"[IGP Tests] FAIL {testType.Name}.{method.Name}: {ex.Message}");
                    }
                    finally
                    {
                        teardownMethod?.Invoke(instance, null);
                    }
                }
            }

            Debug.Log($"[IGP Tests] {label} completed: {passed}/{total} passed");
        }

        private static IEnumerable<MethodInfo> EnumerateTestMethods(Type testType)
        {
            return testType
                .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(IsTestMethod);
        }

        private static bool ContainsTestMethods(Type testType)
        {
            return EnumerateTestMethods(testType).Any();
        }

        private static bool IsTestMethod(MethodInfo method)
        {
            return method.GetCustomAttributes(true).Any(attribute =>
            {
                var attributeName = attribute.GetType().FullName;
                return attributeName == "NUnit.Framework.TestAttribute"
                    || attributeName == "UnityEngine.TestTools.UnityTestAttribute";
            });
        }
    }
}
#endif
