using CliWrap;
using CliWrap.Buffered;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.App.BmsTunnel.CliPrograms;

namespace InnovEnergy.App.BmsTunnel;

using Nodes = IReadOnlyList<Byte>;

public static class BatteryTty
{
    const String DevDir = "/dev";
    
    public static async Task<String?> GetTty()
    {
        Console.WriteLine("searching battery connection...");

        return await GetTtyFromDBus() 
            ?? await GetTtyFromDev();
    }

    private static async Task<String?> GetTtyFromDev()
    {
        var ttys = await FileSystem
                        .Local
                        .GetFiles(DevDir, FileType.CharacterDevice);

        var candidateTtys = ttys
                           .Where(f => f.StartsWith(DevDir + "/ttyUSB"))
                           .NotNull()
                           .ToList();

        if (!candidateTtys.Any())
        {
            Console.WriteLine("no USB converter cable attached!");
            return null;
        }

        if (candidateTtys.Count == 1)
            return candidateTtys[0];

        return "Select TTY:".ChooseFrom(candidateTtys);
    }

    private static async Task<String?> GetTtyFromDBus()
    {
        var tty = await LsDBus
                       .ExecuteBufferedAsync()
                       .Select(ParseBatteryTty);

        if (tty == null)
            return null;
        
        Console.WriteLine("found battery on DBus");

        return $"{DevDir}/{tty}";
    }

    private static CommandTask<Nodes> GetNodes(String tty)
    {
        const String defaultArgs = "--system --print-reply --type=method_call / com.victronenergy.BusItem.GetValue";
        
        return DBusSend
              .AppendArgument($"--dest=com.victronenergy.battery.{tty}")  
              .AppendArgument(defaultArgs)  
              .ExecuteBufferedAsync()
              .Select(ParseBatteryNodes);
    }

    private static Nodes ParseBatteryNodes(BufferedCommandResult result)
    {
        return result
              .StandardOutput
              .Split(Environment.NewLine)
              .Where(l => l.Contains("_Battery/"))
              .Select(l => l.Split('/')[1])
              .Where(n => Byte.TryParse(n, out _))
              .Select(Byte.Parse)
              .Distinct()
              .OrderBy(n => n)
              .ToList();
    }

    private static String? ParseBatteryTty(BufferedCommandResult result)
    {
        return result
              .StandardOutput
              .Split(Environment.NewLine)
              .Where(l => l.Contains("com.victronenergy.battery."))
              .SelectMany(l => l.Split('.'))
              .LastOrDefault();
    }

}