using System.Net;
using DotNext;

namespace Peripherals.NetConfigClient;

static class NetConfigAddr
{
    const UInt32 BASE = 0x30A7_0000;

    public static readonly UInt32[] HOST_IP = { BASE + 0, BASE + 1, BASE + 2, BASE + 3 };
    public static readonly UInt32[] BOARD_IP = { BASE + 4, BASE + 5, BASE + 6, BASE + 7 };
    public static readonly UInt32[] HOST_MAC = { BASE + 8, BASE + 9, BASE + 10, BASE + 11, BASE + 12, BASE + 13 };
    public static readonly UInt32[] BOARD_MAC = { BASE + 14, BASE + 15, BASE + 16, BASE + 17, BASE + 18, BASE + 19 };
}

/// <summary>
/// Network configuration client for FPGA board communication
/// </summary>
public class NetConfig
{
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    readonly int timeout = 2000;
    readonly int taskID;
    readonly int port;
    readonly string address;
    private IPEndPoint ep;

    /// <summary>
    /// Initialize NetConfig client
    /// </summary>
    /// <param name="address">Target board address</param>
    /// <param name="port">Target board port</param>
    /// <param name="taskID">Task identifier</param>
    /// <param name="timeout">Timeout in milliseconds</param>
    public NetConfig(string address, int port, int taskID, int timeout = 2000)
    {
        if (timeout < 0)
            throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
        this.address = address;
        this.taskID = taskID;
        this.port = port;
        this.ep = new IPEndPoint(IPAddress.Parse(address), port);
        this.timeout = timeout;
    }

    /// <summary>
    /// Set host IP address
    /// </summary>
    /// <param name="ip">IP address to set</param>
    /// <returns>Result indicating success or failure</returns>
    public async ValueTask<Result<bool>> SetHostIP(IPAddress ip)
    {
        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ipBytes = ip.GetAddressBytes();

        var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_IP, ipBytes, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set host IP: {ret.Error}");
            return new(ret.Error);
        }

        if (!ret.Value)
        {
            logger.Error($"Failed to set host IP: operation returned false");
            return false;
        }

        // 验证设置结果
        var verifyResult = await GetHostIP();
        if (!verifyResult.IsSuccessful)
        {
            logger.Error($"Failed to verify host IP after setting: {verifyResult.Error}");
            return new(verifyResult.Error);
        }

        var expectedIP = ip.ToString();
        if (verifyResult.Value != expectedIP)
        {
            logger.Error($"Host IP verification failed: expected {expectedIP}, got {verifyResult.Value}");
            return false;
        }

        logger.Info($"Successfully set and verified host IP: {expectedIP}");
        return true;
    }

    /// <summary>
    /// Set board IP address
    /// </summary>
    /// <param name="ip">IP address to set</param>
    /// <returns>Result indicating success or failure</returns>
    public async ValueTask<Result<bool>> SetBoardIP(IPAddress ip)
    {
        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ipBytes = ip.GetAddressBytes();

        var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_IP, ipBytes, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set board IP: {ret.Error}");
            return new(ret.Error);
        }

        if (!ret.Value)
        {
            logger.Error($"Failed to set board IP: operation returned false");
            return false;
        }

        // 验证设置结果
        var verifyResult = await GetBoardIP();
        if (!verifyResult.IsSuccessful)
        {
            logger.Error($"Failed to verify board IP after setting: {verifyResult.Error}");
            return new(verifyResult.Error);
        }

        var expectedIP = ip.ToString();
        if (verifyResult.Value != expectedIP)
        {
            logger.Error($"Board IP verification failed: expected {expectedIP}, got {verifyResult.Value}");
            return false;
        }

        logger.Info($"Successfully set and verified board IP: {expectedIP}");
        return true;
    }

    /// <summary>
    /// Set host MAC address
    /// </summary>
    /// <param name="macAddress">MAC address bytes (6 bytes)</param>
    /// <returns>Result indicating success or failure</returns>
    public async ValueTask<Result<bool>> SetHostMAC(byte[] macAddress)
    {
        if (macAddress == null)
            throw new ArgumentNullException(nameof(macAddress));
        if (macAddress.Length != 6)
            throw new ArgumentException("MAC address must be 6 bytes", nameof(macAddress));

        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_MAC, macAddress, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set host MAC address: {ret.Error}");
            return new(ret.Error);
        }

        if (!ret.Value)
        {
            logger.Error($"Failed to set host MAC address: operation returned false");
            return false;
        }

        // 验证设置结果
        var verifyResult = await GetHostMAC();
        if (!verifyResult.IsSuccessful)
        {
            logger.Error($"Failed to verify host MAC after setting: {verifyResult.Error}");
            return new(verifyResult.Error);
        }

        var expectedMAC = string.Join(":", macAddress.Select(b => $"{b:X2}"));
        if (verifyResult.Value != expectedMAC)
        {
            logger.Error($"Host MAC verification failed: expected {expectedMAC}, got {verifyResult.Value}");
            return false;
        }

        logger.Info($"Successfully set and verified host MAC: {expectedMAC}");
        return true;
    }

    /// <summary>
    /// Set board MAC address
    /// </summary>
    /// <param name="macAddress">MAC address bytes (6 bytes)</param>
    /// <returns>Result indicating success or failure</returns>
    public async ValueTask<Result<bool>> SetBoardMAC(byte[] macAddress)
    {
        if (macAddress == null)
            throw new ArgumentNullException(nameof(macAddress));
        if (macAddress.Length != 6)
            throw new ArgumentException("MAC address must be 6 bytes", nameof(macAddress));

        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_MAC, macAddress, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set board MAC address: {ret.Error}");
            return new(ret.Error);
        }

        if (!ret.Value)
        {
            logger.Error($"Failed to set board MAC address: operation returned false");
            return false;
        }

        // 验证设置结果
        var verifyResult = await GetBoardMAC();
        if (!verifyResult.IsSuccessful)
        {
            logger.Error($"Failed to verify board MAC after setting: {verifyResult.Error}");
            return new(verifyResult.Error);
        }

        var expectedMAC = string.Join(":", macAddress.Select(b => $"{b:X2}"));
        if (verifyResult.Value != expectedMAC)
        {
            logger.Error($"Board MAC verification failed: expected {expectedMAC}, got {verifyResult.Value}");
            return false;
        }

        logger.Info($"Successfully set and verified board MAC: {expectedMAC}");
        return true;
    }

    /// <summary>
    /// Get host IP address
    /// </summary>
    /// <returns>Host IP address as string</returns>
    public async ValueTask<Result<string>> GetHostIP()
    {
        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_IP, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to get host IP: {ret.Error}");
            return new(ret.Error);
        }

        var ip = "";
        for (int i = 0; i < NetConfigAddr.HOST_IP.Length; i++)
        {
            ip += $"{ret.Value[i * 4 + 3]}";
            if (i != NetConfigAddr.HOST_IP.Length - 1)
                ip += ".";
        }

        return ip;
    }

    /// <summary>
    /// Get board IP address
    /// </summary>
    /// <returns>Board IP address as string</returns>
    public async ValueTask<Result<string>> GetBoardIP()
    {
        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_IP, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to get board IP: {ret.Error}");
            return new(ret.Error);
        }

        var ip = "";
        for (int i = 0; i < NetConfigAddr.BOARD_IP.Length; i++)
        {
            ip += $"{ret.Value[i * 4 + 3]}";
            if (i != NetConfigAddr.BOARD_IP.Length - 1)
                ip += ".";
        }

        return ip;
    }

    /// <summary>
    /// Get host MAC address
    /// </summary>
    /// <returns>Host MAC address as formatted string (XX:XX:XX:XX:XX:XX)</returns>
    public async ValueTask<Result<string>> GetHostMAC()
    {
        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_MAC, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to get host MAC address: {ret.Error}");
            return new(ret.Error);
        }

        var mac = "";
        for (int i = 0; i < NetConfigAddr.HOST_MAC.Length; i++)
        {
            mac += $"{ret.Value[i * 4 + 3]:X2}";
            if (i != NetConfigAddr.HOST_MAC.Length - 1)
                mac += ":";
        }

        return mac;
    }

    /// <summary>
    /// Get board MAC address
    /// </summary>
    /// <returns>Board MAC address as formatted string (XX:XX:XX:XX:XX:XX)</returns>
    public async ValueTask<Result<string>> GetBoardMAC()
    {
        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        // 刷新ARP
        // var refrshRet = await Arp.UpdateArpEntryByPingAsync(this.address);
        // if (!refrshRet)
        // {
        //     logger.Warn($"Refrash Arp failed, but maybe not a big deal.");
        // }

        var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_MAC, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to get board MAC address: {ret.Error}");
            return new(ret.Error);
        }

        var mac = "";
        for (int i = 0; i < NetConfigAddr.BOARD_MAC.Length; i++)
        {
            mac += $"{ret.Value[i * 4 + 3]:X2}";
            if (i != NetConfigAddr.BOARD_MAC.Length - 1)
                mac += ":";
        }

        return mac;
    }
}
