using Wonderking.Packets;

namespace Server;

using System.Reflection;
using MassTransit.Mediator;
using Microsoft.Extensions.Logging;
using NetCoreServer;
using Packets;

public class AuthSession : TcpSession
{
    private readonly ILogger<AuthSession> _logger;
    private readonly IMediator _mediator;

    public AuthSession(TcpServer
        server, IMediator mediator, ILogger<AuthSession> logger) : base(server)
    {
        this._mediator = mediator;
        this._logger = logger;
    }

    public Guid AccountId { get; set; }

    public override long Send(byte[] buffer)
    {
        this._logger.LogInformation("Data being sent is: {Data}", BitConverter.ToString(buffer));
        return base.Send(buffer);
    }

    public void Send(IPacket packet)
    {
        var type = packet.GetType();
        this._logger.LogTrace("Packet of type {Type} is being serialized", type.Name);
        var packetIdAttribute = type.GetCustomAttribute<PacketIdAttribute>();
        if (packetIdAttribute == null)
        {
            return;
        }

        var opcode = packetIdAttribute.Code;

        Span<byte> packetData = packet.Serialize();
        var length = (ushort)(packetData.Length + 8);

        Span<byte> buffer = stackalloc byte[length];
        buffer.Clear();
        packetData.CopyTo(buffer[8..length]);

        var bytesOfLength = BitConverter.GetBytes(length);
        var bytesOfOpcode = BitConverter.GetBytes((ushort)opcode);
        for (var i = 0; i < bytesOfLength.Length || i < 2; i++)
        {
            buffer[i] = bytesOfLength[i];
        }

        for (var i = 0; i < bytesOfOpcode.Length || i < 2; i++)
        {
            buffer[2 + i] = bytesOfOpcode[i];
        }

        this._logger.LogTrace("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
        this._logger.LogTrace("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));

        this.Send(buffer);
    }

    protected override void OnReceived(byte[] buffer, long offset, long size)
    {
        this._logger.LogDebug("Length: {Size} & offset: {Offset}", size, offset);
        Span<byte> decryptedBuffer = new byte[size];

        // xor every value after the first 8 bytes
        var dataBuffer = Decrypt(new ArraySegment<byte>(buffer, 8, (int)size - 8).ToArray());

        this._logger.LogDebug("Length {Length}", BitConverter.ToUInt16(buffer, 0));

        var opCode = BitConverter.ToUInt16(buffer.ToArray(), 2);

        this._logger.LogDebug("Packet Op Code: {OpCode}", opCode);
        this._logger.LogDebug("Some Value: {RandomValue}", buffer[4]);

        var clientAliveTime = BitConverter.ToUInt16(buffer.ToArray(), 5);

        this._logger.LogDebug("Client Alive time: {ClientAliveTime}", clientAliveTime);
        this._logger.LogDebug("Might be a flag: {Flag}", buffer[7]);

        this._logger.LogDebug("Full buffer: {Buffer}", BitConverter.ToString(dataBuffer.ToArray()));

        var rawPacket = new RawPacket((OperationCode)opCode, dataBuffer, clientAliveTime, buffer[7],
            buffer[4], this.Id, this);

        _ = this._mediator.Send(rawPacket);

        this._logger.LogInformation("Connection from: {@RemoteEndpoint}", this.Socket.RemoteEndPoint?.ToString());
        base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length);
    }

    private static byte[] Decrypt(byte[] buffer)
    {
        for (var i = 0; i < buffer.Length; ++i)
        {
            buffer[i] = (byte)(buffer[i] ^ i ^ (3 * (0xFE - i)));
        }

        return buffer;
    }
}