using CliWrap;
using HandlebarsDotNet;
using InnovEnergy.App.VrmGrabber.Database;
using InnovEnergy.Lib.Utils;
using Microsoft.AspNetCore.Mvc;
using VrmInstallation = InnovEnergy.Lib.Victron.VictronVRM.Installation;

namespace InnovEnergy.App.VrmGrabber;

public record InstallationToHtmlInterface(
    String Name,
    String Ip,
    Int64 Vrm,
    String Identifier,
    String Serial,
    String EscapedName,
    String Online,
    String LastSeen,
    String NumBatteries,
    String BatteryVersion,
    String BatteryUpdateStatus,
    String ServerIp = "10.2.0.1",      //TODO MAKE ME DYNAMIC
    String FirmwareVersion = "AF09",    //Todo automatically grab newest version?
    String NodeRedFiles = "NodeRedFiles"
    );

[Controller]
public class Controller : ControllerBase  
{
    
    //Todo automatically grab newest version?
    private const String FirmwareVersion = "AF09";


    [HttpGet]
    [Route("/")]
    [Produces("text/html")]
    public ActionResult Index()
    {
        const String source = @"<head>
                                                          <style>
                                                          tbody {
                                                              background-color: #e4f0f5;
                                                          }
                                                         

tbody tr:nth-child(odd) {
  background-color: #ECE9E9;
}

th, td { /* cell */
  padding: 0.75rem;
  font-size: 0.9375rem;
}

th { /* header cell */
  font-weight: 700;
  text-align: left;
  color: #272838;
  border-bottom: 2px solid #EB9486;

  position: sticky;
  top: 0;
  background-color: #F9F8F8;
}
                                                          table {
                                                              border-collapse: collapse;
                                                              width: 100%;
                                                              border: 2px solid rgb(200, 200, 200);
                                                              letter-spacing: 1px;
                                                              font-family: sans-serif;
                                                              font-size: 0.8rem;
                                                              position: absolute; top: 0; bottom: 0; left: 0; right: 0;
                                                          }
                                                          
                                                          thead th {
                                                              border: 1px solid rgb(190, 190, 190);
                                                              padding: 5px 10px;
                                                              position: sticky;
                                                              position: -webkit-sticky;
                                                              top: 0px;
                                                              background: white;
                                                              z-index: 999;
                                                          }
                                                          
                                                          td {
                                                              text-align: left;
                                                          }
                                                          #managerTable {
                                                              overflow: hidden;
                                                          }</style></head>

                                                          <div id='managerTable'>
                                                          <table>
                                                               <tbody>
                                                               <tr>
                                                               <th>Name    This site is updated once per day!</th>
                                                               <th>Gui</th>
                                                               <th>VRM</th>
                                                               <th>Grafana</th>
                                                               <th>Identifier</th>
                                                               <th>Last Seen</th>
                                                               <th>Serial</th>
                                                               <th>#Batteries</th>
                                                               <th>Firmware-Version</th>
                                                               <th>Update</th>
                                                               <th>Last Update Status</th>
                                                               <th>Upload Node Red Files</th>
                                                               </tr>
                                                               {{#inst}}
                                                                   {{> installations}}
                                                               {{/inst}}
                                                               </tbody>
                                                          </table>
                                                          <div id='managerTable'>";



        const String partialSource = @"<tr><td>{{Name}}</td>
            <td><a target='_blank' href=http://{{Ip}}>{{online}} {{Ip}}</a></td>
            <td><a target='_blank' href=https://vrm.victronenergy.com/installation/{{Vrm}}/dashboard>VRM</a></td>
            <td><a target='_blank' href='https://salidomo.innovenergy.ch/d/ENkNRQXmk/installation?refresh=5s&orgId=1&var-Installation={{EscapedName}}&kiosk=tv'>Grafana</a></td>
            <td>{{Identifier}}</td>
            <td>{{LastSeen}}</td>
            <td>{{Serial}}</td>
            <td>{{NumBatteries}}</td>
            <td>{{BatteryVersion}}</td>
            <td><a target='_blank' href=http://{{ServerIp}}/UpdateBatteryFirmware/{{Ip}}>⬆️{{FirmwareVersion}}</a></td>
            <td>{{BatteryUpdateStatus}}</td>
            <td><a target='_blank' href=http://{{ServerIp}}/UploadNodeRedFiles/{{Ip}}>⬆️{{NodeRedFiles}}</a></td>
            </tr>";
        
        var installationsInDb = Db.Installations.OrderBy(i => i.Name, StringComparer.OrdinalIgnoreCase).ToList();
        if (installationsInDb.Count == 0) return new ContentResult 
        {
            ContentType = "text/html",
            Content = "<p>Please wait page is still loading</p>"
        };
        
        Handlebars.RegisterTemplate("installations", partialSource);
        var template = Handlebars.Compile(source);
        var installsForHtml = installationsInDb.Select(i => new InstallationToHtmlInterface(
            i.Name,
            i.Ip,
            i.Vrm,
            i.Identifier,
            i.Serial, 
            i.EscapedName, 
            i.Online,
            DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(i.LastSeen)).ToString(),
            i.NumberOfBatteries,
            i.BatteryFirmwareVersion,
            i.BatteryUpdateStatus));

        var data = new
        {
            inst = installsForHtml,
        };

        var result = template(data);
        
        return new ContentResult 
        {
            ContentType = "text/html",
            Content = result
        };
    }


    [HttpGet("UpdateBatteryFirmware/{installationIp}")]
    public async Task<String> UpdateBatteryFirmware(String installationIp)
    {
        //We need the DeviceName of the battery (ttyUSB?)
        var pathToBattery = await Db.ExecuteBufferedAsyncCommandOnIp(installationIp, "dbus-send --system --dest=com.victronenergy.system --type=method_call --print-reply /ServiceMapping/com_victronenergy_battery_1 com.victronenergy.BusItem.GetText");

        var split = pathToBattery.Split('"');
        var split2 = pathToBattery.Split(' ');
        
        if (split.Length < 2 || split2.Length < 1)
        {
            Console.WriteLine(pathToBattery + " Split failed ");
            return "Update failed";
        }
        if (split[1] == "Failed" || split2[0] == "Error") return "Update failed";

        

        await SendNewBatteryFirmware(installationIp);
        var batteryTtyName = split[1].Split(".").Last();
        var localCommand = "echo start";
        var installation = Db.Installations.First(installation => installation.Ip == installationIp);
        installation.BatteryUpdateStatus = "Running";
        Db.Update(installation: installation);
        var batteryIdsResult = await Db.ExecuteBufferedAsyncCommandOnIp(installationIp, $"dbus-send --system --dest=com.victronenergy.battery.{batteryTtyName} --type=method_call --print-reply / com.victronenergy.BusItem.GetText  | grep -E -o '_Battery/[0-9]+/' | grep -E -o '[0-9]+'| sort -u");
        var batteryIds = batteryIdsResult.Split("\n").ToList();
        batteryIds.Pop();
        
        foreach (var batteryId in batteryIds)
        {
            localCommand = localCommand.Append(
                $" && /opt/innovenergy/scripts/upload-bms-firmware {batteryTtyName} {batteryId} /opt/innovenergy/bms-firmware/{FirmwareVersion}.bin");
        }
        #pragma warning disable CS4014
        // Console.WriteLine(localCommand);
        Db.ExecuteBufferedAsyncCommandOnIp(installationIp, localCommand)
            .ContinueWith(async t =>
            {
                    Console.WriteLine(t.Result);
                    installation.BatteryUpdateStatus = "Complete";
                    // installation.BatteryFirmwareVersion = FirmwareVersion;
                    Db.Update(installation: installation);
                    var vrmInst = await FindVrmInstallationByIp(installation.Ip!);
                    await UpdateVrmTagsToNewFirmware(installationIp);
                    await Db.UpdateAlarms(vrmInst);
            });
        #pragma warning restore CS4014
        return "Battery update is successfully initiated, it will take around 15 minutes to complete! You can close this page now.";
    }

    private static async Task UpdateVrmTagsToNewFirmware(String installationIp)
    {
        var vrmInstallation = await FindVrmInstallationByIp(installationIp);
        var tags = await vrmInstallation.GetTags();

        async void RemoveTag(String t) => await vrmInstallation.RemoveTags(t);

        tags.Where(tag => tag.StartsWith("FM-"))
            .Do(RemoveTag);

        await vrmInstallation.AddTags("FM-" + FirmwareVersion);
    }

    private static async Task<VrmInstallation> FindVrmInstallationByIp(String installationIp)
    {
        var installationId = Db.Installations.Where(i => i.Ip == installationIp).Select(i => i.Vrm).First();
        var vrmAccount = await Db.GetVrmAccount();
        return await vrmAccount.GetInstallation(installationId!);
    }

    private static async Task SendNewBatteryFirmware(String installationIp)
    {
        await Cli.Wrap("rsync")
            .WithArguments($@"-r --relative bms-firmware/{FirmwareVersion}.bin")
            .AppendArgument($@"root@{installationIp}:/opt/innovenergy")
            .ExecuteAsync();
    }
    // [HttpGet(nameof(GetInstallation))]
    // [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
    // public Object GetInstallation(UInt64 serialNumber)
    // {
    //     var instList = Db.InstallationsAndDetails.Values.ToList();
    //     foreach (var detailList in instList.Select((value, index) => new { Value = value, Index = index}))
    //     {
    //         if (detailList.Value.All(detail => detail.Json["idSite"]?.GetValue<UInt64>() != serialNumber)) continue;
    //         var retour = Db.InstallationsAndDetails.Keys.ToList()[detailList.Index].Json;
    //         retour["details"] = JsonSerializer.Deserialize<JsonArray>(JsonSerializer.Serialize(detailList.Value.Select(d => d.Json).ToArray()));
    //         return retour;
    //     }
    //
    //     return new NotFoundResult();
    // }

    // remove the original ones????????
    [HttpPost("UploadNodeRedFiles/{installationIp}")]
    public async Task<IActionResult> UploadNodeRedFiles(String installationIp)
    {
        // Define the mapping of files to remote locations
        var fileLocationMappings = new Dictionary<string, string>
        {
            { "flows.json", "/opt/data/nodered/.node-red/" },
            { "settings-user.js", "/opt/data/nodered/.node-red/" },
            { "rc.local", "/data/" },
            { "dbus-fzsonick-48tl", "/data/"}
        };

        var nodeRedFilesFolder = Path.Combine(Directory.GetCurrentDirectory(), "NodeRedFiles");
        if (!Directory.Exists(nodeRedFilesFolder))
        {
            return BadRequest("NodeRedFiles folder does not exist.");
        }

        var tasks = fileLocationMappings.Select(async mapping =>
        {
            var fileName = mapping.Key;
            var remoteLocation = mapping.Value;

            var filePath = Path.Combine(nodeRedFilesFolder, fileName);
            if (!System.IO.File.Exists(filePath))
            {
                throw new FileNotFoundException($"File {fileName} not found in {nodeRedFilesFolder}.");
            }

            // Execute the SCP command to upload the file
            await Cli.Wrap("rsync")
            .WithArguments($@"-r {filePath}")
            .AppendArgument($@"root@{installationIp}:{remoteLocation}")
            .ExecuteAsync();
        });

        try
        {
            await Task.WhenAll(tasks);
            return Ok("All files uploaded successfully.");
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"An error occurred while uploading files: {ex.Message}");
        }
    }
}