using System.Collections.Concurrent;
using System.Reflection;
using EggLink.DanhengServer.Proto;
using Google.Protobuf;
using Google.Protobuf.Reflection;

namespace EggLink.DanhengServer.Kcp;

public static class PacketLogHelper
{
    private static ConcurrentDictionary<ushort, ParseIMessage> CachedParsers { get; } = [];

    public static string ConvertPacketToJson(ushort opcode, byte[] payload)
    {
        var descriptor = GetParser(opcode);
        if (descriptor == null) throw new Exception();

        var message = descriptor(payload);
        var formatter = JsonFormatter.Default;
        var asJson = formatter.Format(message);
        return asJson ?? throw new Exception();
    }

    private static ParseIMessage? GetParser(ushort opcode)
    {
        if (CachedParsers.TryGetValue(opcode, out var parser)) return parser;

        lock (CachedParsers)
        {
            // try to find the descriptor by opcode
            var asbly = Assembly.GetAssembly(typeof(PlayerGetTokenCsReq));
            if (asbly == null) return null;

            var typ = asbly.GetType($"EggLink.DanhengServer.Proto.{DanhengConnection.LogMap[opcode]}");
            if (typ == null) return null;
            var desc = typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static);
            if (desc?.GetMethod == null) return null;

            // get parser
            if (desc.GetValue(null) is not MessageDescriptor parserProperty) return null;

            var parserMethod = parserProperty.Parser.GetType().GetMethod("ParseFrom", [typeof(byte[])]);
            if (parserMethod == null) return null;

            parser = (ParseIMessage)Delegate.CreateDelegate(
                typeof(ParseIMessage),
                parserProperty.Parser,
                parserMethod
            );

            CachedParsers[opcode] = parser;

            return parser;
        }
    }

    private delegate IMessage ParseIMessage(byte[] data);
}