using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Runtime.Helpers;
using Newtonsoft.Json.Linq;
using UnityEditor;

namespace MCPForUnity.Editor.Tools
{
    [McpForUnityTool("unity_reflect", AutoRegister = false, Group = "docs")]
    public static class UnityReflect
    {
        private static Dictionary<string, Type[]> _assemblyTypeCache;
        private static readonly object CacheLock = new();
        private static readonly ConcurrentDictionary<Type, string[]> ExtensionMethodCache = new();

        private static readonly string[] NamespacePrefixes =
        {
            "UnityEngine.",
            "UnityEditor.",
            "UnityEngine.UI.",
            "Unity.Cinemachine.",
            "UnityEngine.AI.",
            "UnityEngine.Rendering.Universal.",
            "UnityEngine.Rendering.HighDefinition.",
            "UnityEngine.InputSystem.",
            "UnityEngine.ProBuilder.",
            "UnityEngine.Tilemaps.",
            "UnityEngine.EventSystems.",
            "UnityEngine.Rendering.",
            "UnityEngine.SceneManagement.",
            "UnityEngine.Animations.",
            "UnityEngine.Playables.",
            "UnityEngine.UIElements."
        };

        private static readonly Dictionary<Type, string> FriendlyTypeNames = new()
        {
            { typeof(void), "void" },
            { typeof(int), "int" },
            { typeof(float), "float" },
            { typeof(bool), "bool" },
            { typeof(string), "string" },
            { typeof(double), "double" },
            { typeof(long), "long" },
            { typeof(object), "object" },
            { typeof(byte), "byte" },
            { typeof(short), "short" },
            { typeof(char), "char" },
            { typeof(decimal), "decimal" },
            { typeof(uint), "uint" },
            { typeof(ulong), "ulong" },
            { typeof(ushort), "ushort" },
            { typeof(sbyte), "sbyte" }
        };

        [InitializeOnLoadMethod]
        private static void OnLoad()
        {
            AssemblyReloadEvents.afterAssemblyReload += InvalidateCache;
        }

        private static void InvalidateCache()
        {
            lock (CacheLock)
            {
                _assemblyTypeCache = null;
            }
            ExtensionMethodCache.Clear();
        }

        private static Dictionary<string, Type[]> GetAssemblyTypeCache()
        {
            lock (CacheLock)
            {
                if (_assemblyTypeCache != null)
                    return _assemblyTypeCache;

                _assemblyTypeCache = new Dictionary<string, Type[]>();
                foreach (var asm in UnityAssembliesCompat.GetLoadedAssemblies())
                {
                    try
                    {
                        _assemblyTypeCache[asm.FullName] = asm.GetExportedTypes();
                    }
                    catch
                    {
                        // Some assemblies throw on GetExportedTypes
                    }
                }
                return _assemblyTypeCache;
            }
        }

        public static object HandleCommand(JObject @params)
        {
            if (EditorApplication.isCompiling)
                return new ErrorResponse("Cannot reflect while Unity is compiling. Wait for domain reload to complete.");

            if (@params == null)
                return new ErrorResponse("Parameters cannot be null.");

            var p = new ToolParams(@params);

            var actionResult = p.GetRequired("action");
            if (!actionResult.IsSuccess)
                return new ErrorResponse(actionResult.ErrorMessage);

            string action = actionResult.Value.ToLowerInvariant();

            try
            {
                switch (action)
                {
                    case "get_type":
                        return GetTypeInfo(p);
                    case "get_member":
                        return GetMemberInfo(p);
                    case "search":
                        return SearchTypes(p);
                    default:
                        return new ErrorResponse(
                            $"Unknown action: '{action}'. Supported actions: get_type, get_member, search.");
                }
            }
            catch (Exception ex)
            {
                return new ErrorResponse(ex.Message, new { stackTrace = ex.StackTrace });
            }
        }

        // === get_type ===
        private static object GetTypeInfo(ToolParams p)
        {
            var classResult = p.GetRequired("class_name", "'class_name' parameter is required for get_type.");
            if (!classResult.IsSuccess)
                return new ErrorResponse(classResult.ErrorMessage);

            string className = classResult.Value;
            string normalizedName = NormalizeGenericName(className);

            // Check for ambiguity first (only for short names without namespace)
            if (!normalizedName.Contains('.') && !normalizedName.Contains('`'))
            {
                var matches = FindAllTypesByShortName(normalizedName);
                if (matches.Count > 1)
                {
                    return new SuccessResponse($"Ambiguous type name '{className}'.", new
                    {
                        found = true,
                        ambiguous = true,
                        query = className,
                        matches = matches.Select(t => t.FullName).OrderBy(n => n).ToArray(),
                        hint = "Use the fully qualified name (e.g., 'UnityEngine.UI.Button') to disambiguate."
                    });
                }
            }

            var type = ResolveType(normalizedName);
            if (type == null)
            {
                return new SuccessResponse($"Type '{className}' not found.", new
                {
                    found = false,
                    query = className
                });
            }

            // Open generic type definitions (e.g. List<T>) segfault Mono on Unity 2021.3
            // in mono_metadata_generic_param_equal_internal when any member reflection is
            // performed. Return minimal info using only safe property accesses.
            if (type.IsGenericTypeDefinition)
            {
                return new SuccessResponse($"Type info for '{type.Name}'.", new
                {
                    found = true,
                    name = type.Name,
                    full_name = type.FullName,
                    @namespace = type.Namespace,
                    assembly = type.Assembly.GetName().Name,
                    is_generic_type_definition = true,
                    hint = "Open generic type — consult docs for member details."
                });
            }

            var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;

            var methods = type.GetMethods(flags)
                .Where(m => !m.IsSpecialName)
                .Select(m => m.Name)
                .Distinct()
                .OrderBy(n => n)
                .ToArray();

            var properties = type.GetProperties(flags)
                .Select(pr => pr.Name)
                .Distinct()
                .OrderBy(n => n)
                .ToArray();

            var fields = type.GetFields(flags)
                .Select(f => f.Name)
                .Distinct()
                .OrderBy(n => n)
                .ToArray();

            var events = type.GetEvents(flags)
                .Select(e => e.Name)
                .Distinct()
                .OrderBy(n => n)
                .ToArray();

            var obsoleteMembers = GetObsoleteMembers(type, flags);
            var extensionMethods = FindExtensionMethods(type);

            var interfaces = type.GetInterfaces()
                .Select(i => FormatTypeName(i))
                .OrderBy(n => n)
                .ToArray();

            return new SuccessResponse($"Type info for '{FormatTypeName(type)}'.", new
            {
                found = true,
                name = FormatTypeName(type),
                full_name = type.FullName,
                @namespace = type.Namespace,
                assembly = type.Assembly.GetName().Name,
                base_class = type.BaseType != null ? FormatTypeName(type.BaseType) : null,
                interfaces,
                is_abstract = type.IsAbstract,
                is_sealed = type.IsSealed,
                is_static = type.IsAbstract && type.IsSealed,
                is_enum = type.IsEnum,
                is_interface = type.IsInterface,
                members = new
                {
                    methods,
                    properties,
                    fields,
                    events
                },
                extension_methods = extensionMethods,
                obsolete_members = obsoleteMembers
            });
        }

        // === get_member ===
        private static object GetMemberInfo(ToolParams p)
        {
            var classResult = p.GetRequired("class_name", "'class_name' parameter is required for get_member.");
            if (!classResult.IsSuccess)
                return new ErrorResponse(classResult.ErrorMessage);

            var memberResult = p.GetRequired("member_name", "'member_name' parameter is required for get_member.");
            if (!memberResult.IsSuccess)
                return new ErrorResponse(memberResult.ErrorMessage);

            string className = classResult.Value;
            string memberName = memberResult.Value;
            string normalizedName = NormalizeGenericName(className);

            if (!normalizedName.Contains('.') && !normalizedName.Contains('`'))
            {
                var matches = FindAllTypesByShortName(normalizedName);
                if (matches.Count > 1)
                {
                    return new SuccessResponse($"Ambiguous type name '{className}'.", new
                    {
                        found = true,
                        ambiguous = true,
                        query = className,
                        matches = matches.Select(t => t.FullName).OrderBy(n => n).ToArray(),
                        hint = "Use the fully qualified name to disambiguate before requesting member details."
                    });
                }
            }

            var type = ResolveType(normalizedName);
            if (type == null)
            {
                return new SuccessResponse($"Type '{className}' not found.", new
                {
                    found = false,
                    query = className
                });
            }

            if (type.IsGenericTypeDefinition)
            {
                return new SuccessResponse(
                    $"Open generic type '{type.Name}' — consult docs for member details.", new
                    {
                        found = false,
                        type_name = type.FullName,
                        member_name = memberName,
                        is_generic_type_definition = true,
                        hint = "Open generic type — consult docs for member details."
                    });
            }

            // Use flags without DeclaredOnly to find inherited members
            var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;

            // Try methods first
            var methods = type.GetMethods(flags)
                .Where(m => !m.IsSpecialName && m.Name == memberName)
                .ToArray();

            if (methods.Length > 0)
            {
                var overloads = methods.Select(m => FormatMethodDetail(m)).ToArray();
                return new SuccessResponse($"Member '{memberName}' on '{FormatTypeName(type)}'.", new
                {
                    found = true,
                    type_name = FormatTypeName(type),
                    member_name = memberName,
                    member_type = "method",
                    overload_count = overloads.Length,
                    overloads
                });
            }

            // Try properties
            var prop = type.GetProperty(memberName, flags);
            if (prop != null)
            {
                return new SuccessResponse($"Member '{memberName}' on '{FormatTypeName(type)}'.", new
                {
                    found = true,
                    type_name = FormatTypeName(type),
                    member_name = memberName,
                    member_type = "property",
                    property_type = FormatTypeName(prop.PropertyType),
                    can_read = prop.CanRead,
                    can_write = prop.CanWrite,
                    is_static = (prop.GetMethod ?? prop.SetMethod)?.IsStatic ?? false,
                    is_obsolete = prop.GetCustomAttribute<ObsoleteAttribute>() != null,
                    declaring_type = prop.DeclaringType != type ? FormatTypeName(prop.DeclaringType) : null
                });
            }

            // Try fields
            var field = type.GetField(memberName, flags);
            if (field != null)
            {
                return new SuccessResponse($"Member '{memberName}' on '{FormatTypeName(type)}'.", new
                {
                    found = true,
                    type_name = FormatTypeName(type),
                    member_name = memberName,
                    member_type = "field",
                    field_type = FormatTypeName(field.FieldType),
                    is_static = field.IsStatic,
                    is_readonly = field.IsInitOnly,
                    is_constant = field.IsLiteral,
                    constant_value = field.IsLiteral ? field.GetRawConstantValue() : null,
                    is_obsolete = field.GetCustomAttribute<ObsoleteAttribute>() != null,
                    declaring_type = field.DeclaringType != type ? FormatTypeName(field.DeclaringType) : null
                });
            }

            // Try events
            var evt = type.GetEvent(memberName, flags);
            if (evt != null)
            {
                return new SuccessResponse($"Member '{memberName}' on '{FormatTypeName(type)}'.", new
                {
                    found = true,
                    type_name = FormatTypeName(type),
                    member_name = memberName,
                    member_type = "event",
                    event_handler_type = FormatTypeName(evt.EventHandlerType),
                    is_obsolete = evt.GetCustomAttribute<ObsoleteAttribute>() != null,
                    declaring_type = evt.DeclaringType != type ? FormatTypeName(evt.DeclaringType) : null
                });
            }

            // Try extension methods as a last resort
            var extMethods = FindExtensionMethodInfos(type, memberName);
            if (extMethods.Length > 0)
            {
                var overloads = extMethods.Select(m => FormatMethodDetail(m)).ToArray();
                return new SuccessResponse($"Extension method '{memberName}' on '{FormatTypeName(type)}'.", new
                {
                    found = true,
                    type_name = FormatTypeName(type),
                    member_name = memberName,
                    member_type = "extension_method",
                    overload_count = overloads.Length,
                    overloads,
                    declaring_type = FormatTypeName(extMethods[0].DeclaringType)
                });
            }

            return new SuccessResponse($"Member '{memberName}' not found on '{FormatTypeName(type)}'.", new
            {
                found = false,
                type_name = FormatTypeName(type),
                member_name = memberName
            });
        }

        // === search ===
        private static object SearchTypes(ToolParams p)
        {
            var queryResult = p.GetRequired("query", "'query' parameter is required for search.");
            if (!queryResult.IsSuccess)
                return new ErrorResponse(queryResult.ErrorMessage);

            string query = queryResult.Value;
            string scope = p.Get("scope", "unity").ToLowerInvariant();

            if (scope != "unity" && scope != "packages" && scope != "project" && scope != "all")
            {
                return new ErrorResponse(
                    $"Invalid scope: '{scope}'. Supported: unity, packages, project, all.");
            }

            var cache = GetAssemblyTypeCache();
            string queryLower = query.ToLowerInvariant();

            var candidates = new List<(Type type, int rank)>();

            foreach (var kvp in cache)
            {
                var asm = kvp.Value.Length > 0 ? kvp.Value[0].Assembly : null;
                if (asm == null) continue;

                string asmName = asm.GetName().Name;
                if (!MatchesScope(asmName, scope))
                    continue;

                foreach (var t in kvp.Value)
                {
                    if (t.Name == null) continue;

                    string nameLower = t.Name.ToLowerInvariant();
                    string fullNameLower = t.FullName?.ToLowerInvariant() ?? nameLower;

                    if (nameLower == queryLower || fullNameLower == queryLower)
                        candidates.Add((t, 0)); // Exact match
                    else if (nameLower.StartsWith(queryLower))
                        candidates.Add((t, 1)); // Starts with
                    else if (nameLower.Contains(queryLower) || fullNameLower.Contains(queryLower))
                        candidates.Add((t, 2)); // Contains
                }
            }

            var results = candidates
                .OrderBy(c => c.rank)
                .ThenBy(c => c.type.FullName)
                .Take(25)
                .Select(c => new
                {
                    name = c.type.Name,
                    full_name = c.type.FullName,
                    @namespace = c.type.Namespace,
                    assembly = c.type.Assembly.GetName().Name,
                    is_class = c.type.IsClass,
                    is_enum = c.type.IsEnum,
                    is_interface = c.type.IsInterface,
                    is_struct = c.type.IsValueType && !c.type.IsEnum
                })
                .ToArray();

            return new SuccessResponse($"Found {results.Length} type(s) matching '{query}' (scope: {scope}).", new
            {
                query,
                scope,
                count = results.Length,
                results,
                truncated = candidates.Count > 25
            });
        }

        // --- Type Resolution ---

        private static Type ResolveType(string className)
        {
            // Use the shared UnityTypeResolver which handles caching,
            // namespace prefixes, player-over-editor priority, and TypeCache fallback.
            var type = UnityTypeResolver.ResolveAny(className);
            if (type != null) return type;

            // UnityTypeResolver doesn't try our extended namespace prefixes,
            // so fall back to assembly cache scan for edge cases.
            var cache = GetAssemblyTypeCache();
            foreach (var prefix in NamespacePrefixes)
            {
                string fullName = prefix + className;
                foreach (var kvp in cache)
                {
                    type = Array.Find(kvp.Value, t => t.FullName == fullName);
                    if (type != null) return type;
                }
            }

            return null;
        }

        private static List<Type> FindAllTypesByShortName(string shortName)
        {
            var matches = new List<Type>();
            foreach (var kvp in GetAssemblyTypeCache())
            {
                foreach (var t in kvp.Value)
                {
                    if (t.Name == shortName)
                        matches.Add(t);
                }
            }
            return matches;
        }

        // --- Generic Name Normalization ---

        private static string NormalizeGenericName(string name)
        {
            // Parse List<T> -> List`1, Dictionary<K,V> -> Dictionary`2
            var match = Regex.Match(name, @"^(.+)<(.+)>$");
            if (!match.Success) return name;

            string baseName = match.Groups[1].Value;
            string typeArgs = match.Groups[2].Value;

            // Count generic args by tracking nesting depth
            int argCount = 1;
            int depth = 0;
            foreach (char c in typeArgs)
            {
                if (c == '<') depth++;
                else if (c == '>') depth--;
                else if (c == ',' && depth == 0) argCount++;
            }

            return $"{baseName}`{argCount}";
        }

        // --- Type Name Formatting ---

        private static string FormatTypeName(Type type)
        {
            if (type == null) return "null";

            if (FriendlyTypeNames.TryGetValue(type, out var friendly))
                return friendly;

            if (type.IsArray)
                return FormatTypeName(type.GetElementType()) + "[]";

            if (type.IsByRef)
                return FormatTypeName(type.GetElementType());

            if (type.IsGenericType)
            {
                string baseName = type.Name;
                int backtickIndex = baseName.IndexOf('`');
                if (backtickIndex > 0)
                    baseName = baseName.Substring(0, backtickIndex);

                var args = type.GetGenericArguments();
                string argsStr = string.Join(", ", args.Select(FormatTypeName));
                return $"{baseName}<{argsStr}>";
            }

            // For nested types, use dot notation
            if (type.IsNested && type.DeclaringType != null)
                return FormatTypeName(type.DeclaringType) + "." + type.Name;

            return type.Name;
        }

        // --- Method Formatting ---

        private static object FormatMethodDetail(MethodInfo m)
        {
            var parameters = m.GetParameters().Select(param =>
            {
                string prefix = "";
                if (param.IsOut) prefix = "out ";
                else if (param.ParameterType.IsByRef) prefix = "ref ";

                return new
                {
                    name = param.Name,
                    type = prefix + FormatTypeName(param.ParameterType),
                    has_default = param.HasDefaultValue,
                    default_value = param.HasDefaultValue ? FormatDefaultValue(param.DefaultValue) : null,
                    is_params = param.IsDefined(typeof(ParamArrayAttribute))
                };
            }).ToArray();

            string signature = FormatMethodSignature(m);
            var obsoleteAttr = m.GetCustomAttribute<ObsoleteAttribute>();

            return new
            {
                signature,
                return_type = FormatTypeName(m.ReturnType),
                parameters,
                is_static = m.IsStatic,
                is_virtual = m.IsVirtual && !m.IsFinal,
                is_abstract = m.IsAbstract,
                is_generic = m.IsGenericMethod,
                generic_arguments = m.IsGenericMethod
                    ? m.GetGenericArguments().Select(a => a.Name).ToArray()
                    : null,
                is_obsolete = obsoleteAttr != null,
                obsolete_message = obsoleteAttr?.Message,
                declaring_type = m.DeclaringType != m.ReflectedType
                    ? FormatTypeName(m.DeclaringType)
                    : null
            };
        }

        private static string FormatMethodSignature(MethodInfo m)
        {
            string staticPrefix = m.IsStatic ? "static " : "";
            string returnType = FormatTypeName(m.ReturnType);
            string name = m.Name;

            if (m.IsGenericMethod)
            {
                var genArgs = m.GetGenericArguments();
                name += "<" + string.Join(", ", genArgs.Select(a => a.Name)) + ">";
            }

            var paramStrings = m.GetParameters().Select(param =>
            {
                string prefix = "";
                if (param.IsOut) prefix = "out ";
                else if (param.ParameterType.IsByRef) prefix = "ref ";

                if (param.IsDefined(typeof(ParamArrayAttribute)))
                    prefix = "params ";

                return prefix + FormatTypeName(param.ParameterType) + " " + param.Name;
            });

            return $"{staticPrefix}{returnType} {name}({string.Join(", ", paramStrings)})";
        }

        private static object FormatDefaultValue(object value)
        {
            if (value == null) return "null";
            if (value is string s) return $"\"{s}\"";
            if (value is bool b) return b ? "true" : "false";
            return value;
        }

        // --- Obsolete Member Detection ---

        private static string[] GetObsoleteMembers(Type type, BindingFlags flags)
        {
            var obsolete = new HashSet<string>();

            foreach (var m in type.GetMethods(flags).Where(m => !m.IsSpecialName))
            {
                if (m.GetCustomAttribute<ObsoleteAttribute>() != null)
                    obsolete.Add(m.Name);
            }
            foreach (var pr in type.GetProperties(flags))
            {
                if (pr.GetCustomAttribute<ObsoleteAttribute>() != null)
                    obsolete.Add(pr.Name);
            }
            foreach (var f in type.GetFields(flags))
            {
                if (f.GetCustomAttribute<ObsoleteAttribute>() != null)
                    obsolete.Add(f.Name);
            }
            foreach (var e in type.GetEvents(flags))
            {
                if (e.GetCustomAttribute<ObsoleteAttribute>() != null)
                    obsolete.Add(e.Name);
            }

            return obsolete.Count > 0 ? obsolete.OrderBy(n => n).ToArray() : Array.Empty<string>();
        }

        // --- Extension Method Discovery ---

        private static string[] FindExtensionMethods(Type targetType)
        {
            if (ExtensionMethodCache.TryGetValue(targetType, out var cached))
                return cached;

            var extensionNames = new HashSet<string>();
            var cache = GetAssemblyTypeCache();

            foreach (var kvp in cache)
            {
                // Extract assembly name from the FullName key (e.g., "UnityEngine, Version=...")
                string asmName = kvp.Key.Split(',')[0];
                if (!asmName.StartsWith("UnityEngine") && !asmName.StartsWith("UnityEditor") && !asmName.StartsWith("Unity."))
                    continue;

                foreach (var t in kvp.Value)
                {
                    if (!t.IsAbstract || !t.IsSealed) continue; // Static classes only
                    if (!t.IsDefined(typeof(ExtensionAttribute), false)) continue;

                    foreach (var method in t.GetMethods(BindingFlags.Public | BindingFlags.Static))
                    {
                        if (!method.IsDefined(typeof(ExtensionAttribute), false)) continue;

                        var firstParam = method.GetParameters().FirstOrDefault();
                        if (firstParam == null) continue;

                        var paramType = firstParam.ParameterType;
                        if (paramType.IsAssignableFrom(targetType) || targetType.IsSubclassOf(paramType)
                            || paramType == targetType
                            || (paramType.IsGenericType && IsGenericMatch(paramType, targetType)))
                        {
                            extensionNames.Add(method.Name);
                        }
                    }
                }
            }

            var result = extensionNames.Count > 0
                ? extensionNames.OrderBy(n => n).ToArray()
                : Array.Empty<string>();

            ExtensionMethodCache.TryAdd(targetType, result);
            return result;
        }

        private static MethodInfo[] FindExtensionMethodInfos(Type targetType, string methodName)
        {
            var results = new List<MethodInfo>();
            var cache = GetAssemblyTypeCache();

            foreach (var kvp in cache)
            {
                string asmName = kvp.Key.Split(',')[0];
                if (!asmName.StartsWith("UnityEngine") && !asmName.StartsWith("UnityEditor") && !asmName.StartsWith("Unity."))
                    continue;

                foreach (var t in kvp.Value)
                {
                    if (!t.IsAbstract || !t.IsSealed) continue;
                    if (!t.IsDefined(typeof(ExtensionAttribute), false)) continue;

                    foreach (var method in t.GetMethods(BindingFlags.Public | BindingFlags.Static))
                    {
                        if (method.Name != methodName) continue;
                        if (!method.IsDefined(typeof(ExtensionAttribute), false)) continue;

                        var firstParam = method.GetParameters().FirstOrDefault();
                        if (firstParam == null) continue;

                        var paramType = firstParam.ParameterType;
                        if (paramType.IsAssignableFrom(targetType) || targetType.IsSubclassOf(paramType)
                            || paramType == targetType
                            || (paramType.IsGenericType && IsGenericMatch(paramType, targetType)))
                        {
                            results.Add(method);
                        }
                    }
                }
            }

            return results.ToArray();
        }

        private static bool IsGenericMatch(Type genericParamType, Type targetType)
        {
            if (!genericParamType.IsGenericType) return false;

            var genDef = genericParamType.GetGenericTypeDefinition();

            // Check if targetType implements or inherits from the generic definition
            if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == genDef)
                return true;

            foreach (var iface in targetType.GetInterfaces())
            {
                if (iface.IsGenericType && iface.GetGenericTypeDefinition() == genDef)
                    return true;
            }

            return false;
        }

        // --- Scope Matching ---

        private static bool MatchesScope(string assemblyName, string scope)
        {
            switch (scope)
            {
                case "unity":
                    return assemblyName.StartsWith("UnityEngine")
                        || assemblyName.StartsWith("UnityEditor")
                        || assemblyName.StartsWith("Unity.");

                case "packages":
                    return !assemblyName.StartsWith("System")
                        && !assemblyName.StartsWith("mscorlib")
                        && !assemblyName.StartsWith("netstandard");

                case "project":
                    return assemblyName == "Assembly-CSharp"
                        || assemblyName == "Assembly-CSharp-Editor"
                        || assemblyName.StartsWith("Assembly-CSharp-firstpass")
                        || assemblyName.StartsWith("Assembly-CSharp-Editor-firstpass");

                case "all":
                    return true;

                default:
                    return false;
            }
        }
    }
}
