using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DeviceCommand.Base
{
    public class Tcp: ITcp
    {
        public string IPAddress { get;  set; }
        public int Port { get; set; } 
        public int SendTimeout { get;  set; } 
        public int ReceiveTimeout { get;  set; } 
        public TcpClient TcpClient { get;  set; } = new TcpClient();
        protected readonly SemaphoreSlim _commLock = new(1, 1);

        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)
                {
                    
                }

                await TcpClient.ConnectAsync(IPAddress, Port, ct);
                return true;
            }
            finally
            {
                _commLock.Release();
            }
        }

        public virtual void Close()
        {
            if (!TcpClient.Connected)
            {
                throw new InvalidOperationException("TCP 没有连接成功");
            }
            if (TcpClient.Connected) TcpClient.Close();
        }

        public async Task SendAsync(byte[] buffer, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                if (!TcpClient.Connected)
                {
                    throw new InvalidOperationException("TCP 没有连接成功");
                }
                NetworkStream stream = TcpClient.GetStream();

                var timeoutTask = Task.Delay(SendTimeout > 0 ? SendTimeout : Timeout.Infinite, ct);
                var sendTask = stream.WriteAsync(buffer, 0, buffer.Length, ct);

                var completedTask = await Task.WhenAny(sendTask, timeoutTask);
                if (completedTask == timeoutTask) throw new TimeoutException($"写入操作在 {SendTimeout} ms 内未完成");

                await sendTask;
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task SendAsync(string str, CancellationToken ct = default)
        {
            if (!TcpClient.Connected)
            {
                throw new InvalidOperationException("TCP 没有连接成功");
            }

            await SendAsync(Encoding.UTF8.GetBytes(str), ct);
        }

        public async Task<byte[]> ReadAsync(int length, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                if (!TcpClient.Connected)
                {
                    throw new InvalidOperationException("TCP 没有连接成功");
                }

                NetworkStream stream = TcpClient.GetStream();
                byte[] buffer = new byte[length];
                int offset = 0;

                using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
                if (ReceiveTimeout > 0) cts.CancelAfter(ReceiveTimeout);

                while (offset < length)
                {
                    int read = await stream.ReadAsync(buffer, offset, length - offset, cts.Token);
                    if (read == 0) break;
                    offset += read;
                }

                if (offset == 0) return null!;
                return buffer[..offset];
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task<string> ReadAsync(string delimiter = "\n", CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                if (!TcpClient.Connected)
                {
                    throw new InvalidOperationException("TCP 没有连接成功");
                }

                delimiter ??= "\n";
                var sb = new StringBuilder();
                byte[] buffer = new byte[1024];
                NetworkStream stream = TcpClient.GetStream();

                using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
                if (ReceiveTimeout > 0) cts.CancelAfter(ReceiveTimeout);

                while (!cts.Token.IsCancellationRequested)
                {
                    if (stream.DataAvailable)
                    {
                        int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);
                        if (bytesRead == 0) break;
                        sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));

                        int index = sb.ToString().IndexOf(delimiter, StringComparison.Ordinal);
                        if (index >= 0)
                            return sb.ToString(0, index).Trim();
                    }
                    else
                    {
                        await Task.Delay(10, ct);
                    }
                }

                throw new TimeoutException("读取超时或对端关闭");
            }
            finally
            {
                _commLock.Release();
            }
        }

        public async Task<string> WriteReadAsync(string command, string delimiter = "\n", CancellationToken ct = default)
        {
            if (!TcpClient.Connected)
            {
                throw new InvalidOperationException("TCP 没有连接成功");
            }

            await SendAsync(command, ct);
            return await ReadAsync(delimiter, ct);
        }
    }
}
