using System.Net.Sockets;
using System.Text;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.MongoModels;
using adas_core.module.Relays.Models;
using Serilog;

namespace adas_core.module.Relays.Devices;

public class KmTronicRelay(Relay relay, RelaySettings relaySettings)
    : RelayDevice(relay, relaySettings)
{
    private UdpClient? _udpClient;

    public bool DeviceStatus { get; set; }

    public override bool CheckDevice()
    {
        CheckStatus();
        var result = GetStatusRelay(1);
        return result != RelayEnum.Status.Unknown;
    }

    public override bool GetStatusRelay()
    {
        var status = Send("FF0000");
        //while (!status.IsCompleted)
        //{

        //}
        //return !string.IsNullOrEmpty(status.Result);

        return !string.IsNullOrEmpty(status);
    }

    public override RelayEnum.Status GetStatusRelay(int outletId)
    {
        var status = Send("FF0000");

        //while (!status.IsCompleted)
        //{

        //}


        //if (!string.IsNullOrEmpty(status.Result))
        //{
        //    if (status.Result.Substring(outlet.outlet - 1, 1) == "0")
        //    {
        //        return true;
        //    }
        //}

        if (!string.IsNullOrEmpty(status))
            try
            {
                if (status.Substring(outletId - 1, 1) == "0")
                    //return RelayStatus.Off;
                    return Relay.Mode == RelayEnum.Mode.OpenedOffClosedOn
                        ? RelayEnum.Status.On
                        : RelayEnum.Status.Off;
            }
            catch (ArgumentOutOfRangeException e)
            {
                Log.Error(
                    "There isn't an outlet {Outlet} in the rele {Ip}. Please check the configuration.\nException: {Ex}",
                    outletId, Relay.Ip, e.Message);
            }

        //return RelayStatus.On;
        return Relay.Mode == RelayEnum.Mode.OpenedOffClosedOn ? RelayEnum.Status.Off : RelayEnum.Status.On;
    }

    public override void PowerOffRelay(int outletId)
    {
        if (Relay.Mode == RelayEnum.Mode.OpenedOffClosedOn) OpenRelay(outletId);
        else CloseRelay(outletId);
    }

    public void OpenRelay(int outletId)
    {
        //Activa el relé por lo que corta la corriente del dispositivo conectado
        Send("FF0" + outletId + "01");
    }

    public override void PowerOnRelay(int outletId)
    {
        if (Relay.Mode == RelayEnum.Mode.OpenedOffClosedOn) CloseRelay(outletId);
        else OpenRelay(outletId);
    }

    public void CloseRelay(int outletId)
    {
        //Desactiva el relé por lo que deja pasar la corriente del dispositivo conectado
        Send("FF0" + outletId + "00");
    }

    public override void PowerOffAll()
    {
        //Activa el relé por lo que corta la corriente de todos los dispositivo conectados
        Send("FFE0FF");
    }

    public override void PowerOnAll()
    {
        //Desactiva el relé por lo que deja pasar la corriente a todos dispositivo conectados
        Send("FFE000");
    }

    public override void CheckStatus()
    {
        DeviceStatus = GetStatusRelay();
    }


    public override void RebootOutlet(int outletId)
    {
        //Reinicia el relé del dispositivo conectado            
        PowerOnOffRelay(outletId, 2000);
    }

    public override void Reboot()
    {
        PowerOffAll();
        if (Relay.RebootDelay is > 0) Thread.Sleep(Relay.RebootDelay!.Value);
        PowerOnAll();
    }

    public override Task<bool> GetStatusRelayAsync()
    {
        throw new NotImplementedException();
    }

    protected virtual void Connect()
    {
        if (_udpClient == null)
            try
            {
                if (!string.IsNullOrEmpty(Relay.Ip)) _udpClient = new UdpClient(Relay.Ip, Relay.Port);
                //var ipaddress = IPAddress.Parse(RelayConfig.Ip);
                //sourceipendpoint = new IPEndPoint(ipaddress, port);    //source port IPEndpoint
            }
            catch (Exception e)
            {
                Log.Debug("UdpClient not connect {Host}: {Port} Ex: {Ex}", Relay.Ip, Relay.Port, e.Message);

                try
                {
                    if (_udpClient != null)
                    {
                        _udpClient.Close();
                        _udpClient = null;
                    }
                }
                catch
                {
                    // ignored
                }
            }
        else
            try
            {
                _udpClient.Close();
                _udpClient = null;
            }
            catch
            {
                // ignored
            }
    }

    protected virtual string Send(string datagrama)
    {
        if (_udpClient == null) Connect();

        if (_udpClient == null) return string.Empty;

        var command = Encoding.ASCII.GetBytes(datagrama.ToUpper());
        _udpClient.Send(command, command.Length);

        var returnData = string.Empty;
        try
        {
            var t = Task.Run(() =>
            {
                var taskReceiveBytes = _udpClient.ReceiveAsync();
                returnData = Encoding.ASCII.GetString(taskReceiveBytes.Result.Buffer);
            });
            var ts = TimeSpan.FromSeconds(5);
            if (!t.Wait(ts))
            {
                if (!t.IsCompletedSuccessfully) returnData = string.Empty;
                if (t.Exception != null) throw t.Exception;
            }

            if (t.Status == TaskStatus.RanToCompletion) t.Dispose();
        }
        catch (Exception ex)
        {
            Log.Debug("UdpClient not connect {Host}: {Port} Ex: {Ex}", Relay.Ip, Relay.Port, ex.Message);
            returnData = string.Empty;
        }

        if (returnData == string.Empty)
            Log.Debug("UdpClient not connect {Host}: {Port}", Relay.Ip, Relay.Port);

        return returnData;
    }
}