using System.Collections.Concurrent;
using System.Reflection;
using AyCode.Services.SignalRs;

namespace AyCode.Models.Server.DynamicMethods;

/// <summary>
/// Registry for dynamic method lookups with lazy initialization.
/// Caches method metadata statically (by messageTag), resolves instances per-request.
/// </summary>
/// <typeparam name="TAttribute">The attribute type used to mark methods (e.g., SignalRAttribute)</typeparam>
public class AcDynamicMethodRegistry<TAttribute> where TAttribute : TagAttribute
{
    /// <summary>
    /// Statikus cache: messageTag → (DeclaringType, MethodInfo)
    /// A reflection eredménye, nem változik runtime-ban.
    /// null érték = már kerestük, de nem találtuk.
    /// </summary>
    private static readonly ConcurrentDictionary<int, (Type DeclaringType, AcMethodInfoModel<TAttribute> Method)?> _methodLookupCache = new();

    /// <summary>
    /// Instance array - NEM statikus, request/Hub-specifikus.
    /// Array is faster than List for small fixed-size collections (2-5 elements).
    /// </summary>
    private object[] _instances = [];
    private int _count;

    /// <summary>
    /// Gets or sets the capacity of the instance array.
    /// Set this before calling Register() to avoid allocations.
    /// </summary>
    public int CahcheSizeCapacity
    {
        get => _instances.Length;
        set => _instances = new object[value];
    }

    /// <summary>
    /// Registers an instance for method lookup.
    /// No reflection happens here - just stores the instance reference.
    /// </summary>
    public void Register(object instance)
    {
        if (_count >= _instances.Length)
        {
            // CahcheSizeCapacity not set or exceeded - resize
            var newSize = _instances.Length == 0 ? 4 : _instances.Length * 2;
            Array.Resize(ref _instances, newSize);
        }
        _instances[_count++] = instance;
    }

    /// <summary>
    /// Finds the method and instance for a given messageTag.
    /// Uses cached lookup when possible, falls back to lazy search.
    /// </summary>
    public (object Instance, AcMethodInfoModel<TAttribute> Method)? GetMethodByMessageTag(int messageTag)
    {
        // 1. Check cache first
        if (_methodLookupCache.TryGetValue(messageTag, out var cached))
        {
            if (cached == null)
                return null; // Already searched, not found

            // Find the instance of the cached type
            var instance = FindInstanceByType(cached.Value.DeclaringType);
            if (instance != null)
                return (instance, cached.Value.Method);
        }

        // 2. Lazy search through registered instances
        for (var i = 0; i < _count; i++)
        {
            var instance = _instances[i];
            var type = instance.GetType();
            
            // Search methods with TAttribute
            foreach (var methodInfo in type.GetMethods())
            {
                if (methodInfo.GetCustomAttribute(typeof(TAttribute)) is not TAttribute attribute)
                    continue;

                if (attribute.MessageTag == messageTag)
                {
                    var method = new AcMethodInfoModel<TAttribute>(attribute, methodInfo);
                    _methodLookupCache[messageTag] = (type, method);
                    return (instance, method);
                }
            }
        }

        // 3. Not found - cache this result too
        _methodLookupCache[messageTag] = null;
        return null;
    }

    /// <summary>
    /// Finds an instance by its type from the registered instances.
    /// </summary>
    private object? FindInstanceByType(Type type)
    {
        for (var i = 0; i < _count; i++)
        {
            var instance = _instances[i];
            if (instance.GetType() == type)
                return instance;
        }
        return null;
    }

    /// <summary>
    /// Gets the number of registered instances.
    /// </summary>
    public int Count => _count;
}
