using System.Net;
using System.Xml;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.MongoModels;
using adas_core.module.Relays.Models;
using adas_core.module.Relays.Utils;
using Serilog;

namespace adas_core.module.Relays.Devices;

public class KmTronicV2Relay : RelayDevice
{
    public KmTronicV2Relay(Relay relay, RelaySettings relaySettings) : base(relay, relaySettings)
    {
        CheckDevice();
    }

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

    public override void CheckStatus()
    {
        try
        {
            var httpResponse = SendRequest("status.xml", HttpMethod.Get);

            if (httpResponse.StatusCode.Equals(HttpStatusCode.OK))
            {
                var responseContent = httpResponse.Content;
                var responseString = responseContent.ReadAsStringAsync().Result;
                if (!string.IsNullOrEmpty(responseString))
                {
                    var response = new XmlDocument();
                    response.LoadXml(responseString);
                    foreach (XmlNode node in response.SelectNodes("//response")!)
                    foreach (XmlNode childNode in node.ChildNodes)
                    {
                        if (!childNode.Name.StartsWith("relay") || string.IsNullOrEmpty(childNode.InnerText)) continue;
                        var id = int.Parse(childNode.Name[5..]);
                        if (id <= 0 || id > Relay.Total) continue;
                        var value = int.Parse(childNode.InnerText);
                        var relayStatus = value.Equals(0) ? RelayEnum.Status.Off :
                            value.Equals(1) ? RelayEnum.Status.On : RelayEnum.Status.Unknown;
                        Tuple.Create(Relay.Ip, Relay.Port, id);
                        SetStatusRelay(id, relayStatus);
                    }
                }

                Log.Debug(responseContent.ReadAsStringAsync().Result);
            }
            else
            {
                Log.Debug("Status code = " + httpResponse.StatusCode);
            }
        }
        catch (Exception ex)
        {
            Log.Error(ex.ToString());
            Enumerable.Range(1, Relay.Total).ToList().ForEach(outletId =>
            {
                SetStatusRelay(outletId, RelayEnum.Status.Unknown);
            });
        }
    }


    public override bool GetStatusRelay()
    {
        return GetStatusRelay(1) == RelayEnum.Status.On;
    }

    public override Task<bool> GetStatusRelayAsync()
    {
        var result = GetStatusRelay();
        return Task.FromResult(result);
    }

    public override void PowerOnRelay(int outletId)
    {
        PowerRelay(outletId, RelayEnum.Status.On);
    }

    public override void PowerOffRelay(int outletId)
    {
        PowerRelay(outletId, RelayEnum.Status.Off);
    }

    private Uri GetUri(string path, string? query = null)
    {
        var uriBuilder = new UriBuilder
            { Scheme = "http", Host = Relay.Ip, Port = Relay.Port, Path = path };
        if (!string.IsNullOrEmpty(query)) uriBuilder.Query = query;
        return uriBuilder.Uri;
    }

    private HttpRequestMessage GetRequest(string path, HttpMethod method, string? query = null)
    {
        var uri = GetUri(path, query);
        var request = new HttpRequestMessage
        {
            RequestUri = uri,
            Method = method
        };

        if (!string.IsNullOrEmpty(Relay.Username) && !string.IsNullOrEmpty(Relay.Password))
            HttpUtils.AddBasicAuth(Relay.Username, Relay.Password, request.Headers);
        return request;
    }

    private HttpResponseMessage SendRequest(string path, HttpMethod method, string? query = null)
    {
        using var client = new HttpClient();
        client.Timeout = TimeSpan.FromSeconds(15);
        try
        {
            return client.Send(GetRequest(path, method, query));
        }
        catch (Exception e)
        {
            Log.Warning("Error sending request to kmtronic ip {IP} port {Port}: {Message}", Relay.Ip,
                Relay.Port, e.Message);
            return new HttpResponseMessage(HttpStatusCode.Gone);
        }
    }

    private void PowerRelay(int outletId, RelayEnum.Status toState, bool checkStatus = true)
    {
        if (Relay.Mode == RelayEnum.Mode.OpenedOffClosedOn)
            toState = toState == RelayEnum.Status.Off ? RelayEnum.Status.On : RelayEnum.Status.Off;

        var status = GetStatusRelay(outletId);

        if (status == toState) return;


        //No entiendo esto así, siempre enciende/apaga el mismo uriBuilder.Query = $"relay={outlet.outlet}";
        //me quedo con el nombre y solo con el número del rele que es la llamada que el hace realmente desde la web
        //No uso una regex porque es mucho más lento            

        try
        {
            SendRequest("relays.cgi", HttpMethod.Post, $"relay={outletId}");
        }
        catch (Exception ex)
        {
            Log.Debug(ex.Message);
        }
        finally
        {
            if (checkStatus) CheckStatus();
        }
    }

    public override void PowerOnAll()
    {
        PowerAll(RelayEnum.Status.On);
    }

    public override void PowerOffAll()
    {
        PowerAll(RelayEnum.Status.Off);
    }

    private void PowerAll(RelayEnum.Status toState)
    {
        Enumerable.Range(1, Relay.Total).ToList().ForEach(outletId => { PowerRelay(outletId, toState, false); });
        CheckStatus();
    }

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

    public override void RebootOutlet(int outletId)
    {
        PowerOffRelay(outletId);
        Thread.Sleep(30000);
        PowerOnRelay(outletId);
    }
}