using System;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;

[MyAttribute<int>("hi", NamedProp = 3)]
public class C
{
    static void Main()
    {
        foreach (Attribute a in typeof(C).GetCustomAttributesAllowGeneric())
        {
            Console.WriteLine(a);
        }
    }
}

[AttributeUsage(AttributeTargets.Class)]
public class MyAttribute<T>(string arg) : Attribute
{
    public T NamedProp { get; set; }

    public override string ToString() => $"{arg} {NamedProp}";
}

internal static class Utility
{
    internal static IEnumerable<Attribute> GetCustomAttributesAllowGeneric(this MemberInfo member)
    {
        using PEReader peReader = new PEReader(File.OpenRead(member.Module.Assembly.Location));
        MetadataReader mdReader = peReader.GetMetadataReader();

        EntityHandle attributedMemberHandle = MetadataTokens.EntityHandle(member.MetadataToken);
        foreach (CustomAttributeHandle cah in mdReader.GetCustomAttributes(attributedMemberHandle))
        {
            CustomAttribute att = mdReader.GetCustomAttribute(cah);
            int attCtorToken = MetadataTokens.GetToken(att.Constructor);
            Type constructedAttributeType;
            switch (att.Constructor.Kind)
            {
                case HandleKind.MemberReference:
                    MemberReference mr = mdReader.GetMemberReference((MemberReferenceHandle)att.Constructor);
                    MethodSignature<Type> sig = mr.DecodeMethodSignature(SignatureTypeProvider.Instance, new GenericContext());
                    switch (mr.Parent.Kind)
                    {
                        case HandleKind.TypeSpecification:
                            TypeSpecification attTypeSpec = mdReader.GetTypeSpecification((TypeSpecificationHandle)mr.Parent);
                            constructedAttributeType = attTypeSpec.DecodeSignature(SignatureTypeProvider.Instance, default);
                            break;
                        default:
                            throw new NotSupportedException();
                    }

                    break;
                case HandleKind.MethodDefinition:
                    MethodDefinition attCtor = mdReader.GetMethodDefinition((MethodDefinitionHandle)att.Constructor);
                    TypeDefinition attClass = mdReader.GetTypeDefinition(attCtor.GetDeclaringType());
                    Console.WriteLine(mdReader.GetString(attClass.Name));
                    throw new NotImplementedException();
                    break;
                default:
                    throw new NotSupportedException();
            }

            CustomAttributeValue<Type> attValue = att.DecodeValue(CustomAttributeTypeProvider.Instance);
            ConstructorInfo attCtorInfo = (ConstructorInfo)constructedAttributeType.Module.ResolveMethod(attCtorToken);
            Attribute attObject = (Attribute)attCtorInfo.Invoke(GetAttributeConstructorArguments(attValue));

            // Set properties
            foreach (CustomAttributeNamedArgument<Type> namedArg in attValue.NamedArguments)
            {
                PropertyInfo prop = constructedAttributeType.GetProperty(namedArg.Name);
                prop.SetValue(attObject, namedArg.Value);
            }

            yield return attObject;
        }
    }

    private static object?[] GetAttributeConstructorArguments(CustomAttributeValue<Type> attValue)
    {
        object?[] args = new object[attValue.FixedArguments.Length];
        for (int i = 0; i < attValue.FixedArguments.Length; i++)
        {
            CustomAttributeTypedArgument<Type> arg = attValue.FixedArguments[i];
            args[i] = arg.Value;
        }

        return args;
    }
}