using System.Reactive.Linq;
using InnovEnergy.Lib.Devices.EmuMeter;
using InnovEnergy.Lib.Protocols.DBus;
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VeDBus;

namespace InnovEnergy.App.EmuMeterDriver;

public static class EmuMeterDriver
{
    public static Task<Exception> Run(String hostName, Bus dbusAddress)
    {
        return Run(hostName, ModbusTcpClient.DefaultPort, dbusAddress);
    }

    public static async Task<Exception> Run(String hostName, UInt16 port, Bus dbusAddress)
    {
        // var ep = new UnixDomainSocketEndPoint("/home/eef/graber_dbus.sock");
        // var auth = AuthenticationMethod.ExternalAsRoot();
        // dbusAddress = new Bus(ep, auth);

        var emuMeter = new EmuMeterDevice(hostName, port, Config.ModbusNodeId);
        
        
        var meterStatus = Observable
                         .Interval(Config.UpdatePeriod)
                         .Select(_ => emuMeter.ReadStatus())
                         .Publish();

        var poller = meterStatus.Connect();

        var properties = Config.DefaultProperties;

        var signals = Config
                     .Signals
                     .Select(signal => meterStatus.Select(signal.ToVeProperty))
                     .Merge()
                     .Do(p => properties.Set(p));

        // TODO: remove when possible
        // Apparently some VE services need to be periodically reminded that
        // this service is /Connected
        meterStatus.Subscribe(_ => properties.Set("/Connected", 1));
        
        // Wait until status is read once to make sure all
        // properties are set when we go onto the bus.
        var dbus = meterStatus
                  .Skip(1)
                  .Take(1)
                  .SelectMany(_ => PublishPropertiesOnDBus(properties, dbusAddress));

        return await signals
                    .MergeErrors(dbus)
                    .Finally(poller.Dispose)
                    .SelectErrors();

    }

    
    private static Task<Exception> PublishPropertiesOnDBus(VeProperties properties, Bus bus)
    {
        Console.WriteLine($"Connecting to DBus {bus}");
        return properties.PublishOnDBus(bus, Config.BusName);
    }
}