using System.Net;
using System.Text;
using Common;
using Xunit.Abstractions;

namespace server.test;


public class UDPServerTest
{
    const string address = "127.0.0.1";
    const int port = 33000;
    private static readonly UDPServer udpServer = new UDPServer(port);

    private readonly ITestOutputHelper output;

    public UDPServerTest(ITestOutputHelper output)
    {
        this.output = output;
        udpServer.Start();
    }

    [Fact]
    public void UDPDataDeepClone()
    {
        var udpData = new UDPData()
        {
            DateTime = DateTime.Now,
            Address = "127.0.0.1",
            Port = 1234,
            Data = new byte[] { 0xf0, 00, 00, 00 },
            HasRead = false
        };
        var cloneUdpData = udpData.DeepClone();

        Assert.Equal(udpData.DateTime, cloneUdpData.DateTime);
        Assert.Equal(udpData.Address, cloneUdpData.Address);
        Assert.Equal(udpData.Port, cloneUdpData.Port);
        Assert.Equal(udpData.Data, cloneUdpData.Data);
        Assert.Equal(udpData.HasRead, cloneUdpData.HasRead);

        udpData.DateTime = DateTime.Now;
        udpData.Address = "192.168.1.1";
        udpData.Port = 33000;
        udpData.Data = new byte[] { 0xFF, 00, 00, 00 };
        udpData.HasRead = true;

        Assert.NotNull(cloneUdpData.DateTime);
        Assert.NotNull(cloneUdpData.Address);
        Assert.NotNull(cloneUdpData.Port);
        Assert.NotNull(cloneUdpData.Data);
        Assert.NotNull(cloneUdpData.HasRead);

        Assert.NotEqual(udpData.DateTime, cloneUdpData.DateTime);
        Assert.NotEqual(udpData.Address, cloneUdpData.Address);
        Assert.NotEqual(udpData.Port, cloneUdpData.Port);
        Assert.NotEqual(udpData.Data, cloneUdpData.Data);
        Assert.NotEqual(udpData.HasRead, cloneUdpData.HasRead);
    }

    [Theory]
    [InlineData(new object[] { new string[] { "Hello World!", "Hello Server!", "What is your problem?" } })]
    public async Task UDPServerFindString(string[] textArray)
    {
        Assert.True(udpServer.IsRunning);

        var serverEP = new IPEndPoint(IPAddress.Parse(address), port);

        foreach (var text in textArray)
        {
            Assert.True(await UDPClientPool.SendStringAsync(serverEP, [text]));
            var ret = await udpServer.FindDataAsync(address);
            Assert.True(ret.HasValue);
            var data = ret.Value;
            Assert.Equal(address, data.Address);
            Assert.Equal(text, Encoding.ASCII.GetString(data.Data));
        }
    }

    [Theory]
    [InlineData(new object[] { new UInt32[] { 0xF0_00_00_00, 0xFF_00_00_00, 0xFF_FF_FF_FF } })]
    public async Task UDPServerFindBytes(UInt32[] bytesArray)
    {
        Assert.True(udpServer.IsRunning);

        var serverEP = new IPEndPoint(IPAddress.Parse(address), port);

        foreach (var number in bytesArray)
        {
            Assert.True(await UDPClientPool.SendBytesAsync(serverEP, Number.NumberToBytes(number, 4).Value));
            var ret = await udpServer.FindDataAsync(address);
            Assert.True(ret.HasValue);
            var data = ret.Value;
            Assert.Equal(address, data.Address);
            Assert.Equal(number, Number.BytesToUInt32(data.Data).Value);
        }
    }

    [Theory]
    [InlineData(new object[] { new UInt32[] { 0xF0_00_00_00, 0xF0_01_00_00 } })]
    public async Task UDPServerWaitResp(UInt32[] bytesArray)
    {
        Assert.True(udpServer.IsRunning);

        var serverEP = new IPEndPoint(IPAddress.Parse(address), port);

        foreach (var number in bytesArray)
        {
            Assert.True(await UDPClientPool.SendBytesAsync(serverEP, Number.NumberToBytes(number, 4).Value));

            var ret = await udpServer.WaitForAckAsync(address);
            Assert.True(ret.IsSuccessful);
            var data = ret.Value;
            Assert.True(data.IsSuccessful);
            Assert.Equal(number, Number.BytesToUInt32(data.ToBytes()).Value);
        }
    }

    [Theory]
    [InlineData(new object[] { new UInt64[] { 0x0F_00_00_00_01_02_02_02, 0x0F_01_00_00_FF_FF_FF_FF } })]
    public async Task UDPServerWaitData(UInt64[] bytesArray)
    {
        Assert.True(udpServer.IsRunning);

        var serverEP = new IPEndPoint(IPAddress.Parse(address), port);

        foreach (var number in bytesArray)
        {
            Assert.True(await UDPClientPool.SendBytesAsync(serverEP, Number.NumberToBytes(number, 8).Value));

            var ret = await udpServer.WaitForDataAsync(address);
            Assert.True(ret.IsSuccessful);
            var data = ret.Value;
            Assert.True(data.IsSuccessful);
            Assert.Equal((UInt64)number, Number.BytesToUInt64(data.ToBytes()).Value);
        }
    }
}
