using Model.Models;
using NModbus;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace DeviceCommand.Base
{
    public class ModbusTcp : IModbusDevice
    {
        public string IPAddress { get; private set; } = "127.0.0.1";
        public int Port { get; private set; } = 502;
        public int SendTimeout { get; private set; } = 3000;
        public int ReceiveTimeout { get; private set; } = 3000;

        private TcpClient _tcpClient;
        public IModbusMaster Modbus { get; private set; }
        public bool IsConnected => _tcpClient?.Connected ?? false;

        protected readonly SemaphoreSlim _commLock = new(1, 1);
        public ModbusTcp(TcpConfig config) : this()
        {
            if (config == null) return;
            ConfigureDevice(config.IPAddress, config.Port, config.SendTimeout, config.ReceiveTimeout);
        }
        public ModbusTcp()
        {
            _tcpClient = new TcpClient();
        }

        public void ConfigureDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
        {
            IPAddress = ipAddress;
            Port = port;
            SendTimeout = sendTimeout;
            ReceiveTimeout = receiveTimeout;
        }

        public virtual async Task<bool> ConnectAsync(CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                if (_tcpClient.Connected)
                {
                    var remoteEndPoint = (IPEndPoint)_tcpClient.Client.RemoteEndPoint!;
                    if (remoteEndPoint.Address.MapToIPv4().ToString() == IPAddress && remoteEndPoint.Port == Port)
                        return true;
                }

                _tcpClient.Close();
                _tcpClient.Dispose();
                _tcpClient = new TcpClient();

                await _tcpClient.ConnectAsync(IPAddress, Port, ct);
                Modbus = new ModbusFactory().CreateMaster(_tcpClient);
                return true;
            }
            finally
            {
                _commLock.Release();
            }
        }

        public virtual void Close()
        {
            if (_tcpClient.Connected) _tcpClient.Close();
        }

        public async Task WriteSingleRegisterAsync(byte slaveAddress, ushort registerAddress, ushort value, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                // 修复：FromMinutes 改为 FromMilliseconds
                await Modbus.WriteSingleRegisterAsync(slaveAddress, registerAddress, value)
                            .WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task WriteMultipleRegistersAsync(byte slaveAddress, ushort startAddress, ushort[] values, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                await Modbus.WriteMultipleRegistersAsync(slaveAddress, startAddress, values)
                            .WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task<ushort[]> ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                return await Modbus.ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints)
                                   .WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct);
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task WriteSingleCoilAsync(byte slaveAddress, ushort coilAddress, bool value, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                await Modbus.WriteSingleCoilAsync(slaveAddress, coilAddress, value)
                            .WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task<bool[]> ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                return await Modbus.ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints)
                                   .WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct);
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task<ushort[]> ReadInputRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                return await Modbus.ReadInputRegistersAsync(slaveAddress, startAddress, numberOfPoints)
                                   .WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct);
            }
            finally
            {
                _commLock.Release();
            }
        }

        public void Dispose()
        {
            _tcpClient?.Dispose();
            _commLock?.Dispose();
        }
    }
}