using System.Diagnostics;
using System.Net;
using System.Text.RegularExpressions;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods;
using InnovEnergy.App.Backend.Relations;
using InnovEnergy.App.Backend.Websockets;
using InnovEnergy.Lib.Utils;
using Microsoft.AspNetCore.Mvc;

namespace InnovEnergy.App.Backend;

using Token = String;

// create JobStatus class to track download battery log job
public class JobStatus
{
    public string JobId { get; set; }
    public string Status { get; set; }
    public string FileName { get; set; }
    public DateTime StartTime { get; set; } 
}

[Controller]
[Route("api/")]
public class Controller : ControllerBase  
{
    
    [HttpPost(nameof(Login))]
    public ActionResult<Session> Login(String username, String? password)
    {
        var user = Db.GetUserByEmail(username);

        if (user is null)
            throw new Exceptions(400, "Null User Exception", "Must provide a user to log in as.", Request.Path.Value!);

        if (!(user.Password.IsNullOrEmpty() && user.MustResetPassword) && !user.VerifyPassword(password))
        {
            //return Unauthorized("No Password set");
            throw new Exceptions(401, "Wrong Password Exception", "Please try again.", Request.Path.Value!);
        }

        var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
        
        //TODO The Frontend should check for the MustResetPassword Flag

        return Db.Create(session) 
             ? session 
             : throw new Exceptions(401,"Session Creation Exception", "Not allowed to log in.", Request.Path.Value!);
    }


    [HttpPost(nameof(Logout))]
    public ActionResult Logout(Token authToken)
    {
        var session = Db.GetSession(authToken);

        return session.Logout()
             ? Ok()
             : Unauthorized();
    }
    
    
    
    [HttpGet(nameof(CreateWebSocket))]
    public async Task CreateWebSocket(Token authToken)
    {
        var session = Db.GetSession(authToken)?.User;

        if (session is null)
        {
            Console.WriteLine("------------------------------------Unauthorized user----------------------------------------------");
            HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            HttpContext.Abort(); 
            return;
        }

        if (!HttpContext.WebSockets.IsWebSocketRequest)
        {
            Console.WriteLine("------------------------------------Not a websocket request ----------------------------------------------");
            HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            HttpContext.Abort();
            return;
        }
       
        var webSocketContext = await HttpContext.WebSockets.AcceptWebSocketAsync();
        var webSocket = webSocketContext;
        
        //Handle the WebSocket connection
        await WebsocketManager.HandleWebSocketConnection(webSocket);
    }
    
    [HttpGet(nameof(GetAllErrorsForInstallation))]
    public ActionResult<IEnumerable<Error>> GetAllErrorsForInstallation(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
    
        var installation = Db.GetInstallationById(id);
    
        if (installation is null || !user.HasAccessTo(installation))
            return Unauthorized();
        
        return Db.Errors
            .Where(error => error.InstallationId == id)
            .OrderByDescending(error => error.Date)
            .ThenByDescending(error => error.Time)
            .ToList();
    }
    
    [HttpGet(nameof(GetHistoryForInstallation))]
    public ActionResult<IEnumerable<UserAction>> GetHistoryForInstallation(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
    
        var installation = Db.GetInstallationById(id);
    
        if (installation is null || !user.HasAccessTo(installation))
            return Unauthorized();
        
        return Db.UserActions
            .Where(action =>action.InstallationId == id)
            .OrderByDescending(action => action.Timestamp)
            .ToList();
    }
    
    [HttpGet(nameof(GetAllWarningsForInstallation))]
    public ActionResult<IEnumerable<Warning>> GetAllWarningsForInstallation(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
    
        var installation = Db.GetInstallationById(id);
    
        if (installation is null || !user.HasAccessTo(installation))
            return Unauthorized();
        
        return Db.Warnings
            .Where(error => error.InstallationId == id)
            .OrderByDescending(error => error.Date)
            .ThenByDescending(error => error.Time)
            .ToList();
    }
    
    

    [HttpGet(nameof(GetCsvTimestampsForInstallation))]
    public ActionResult<IEnumerable<Int64>> GetCsvTimestampsForInstallation(Int64 id, Int32 start, Int32 end, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();

        var installation = Db.GetInstallationById(id);

        if (installation is null || !user.HasAccessTo(installation))
            return Unauthorized();

        var sampleSize = 100;
        var allTimestamps = new List<Int64>();
            
        static string FindCommonPrefix(string str1, string str2)
        {
            int minLength = Math.Min(str1.Length, str2.Length);
            int i = 0;
            while (i < minLength && str1[i] == str2[i])
            {
                i++;
            }
            return str1.Substring(0, i);
        }
        
        string commonPrefix = FindCommonPrefix(start.ToString(), end.ToString());
        
        Int64 startTimestamp = Int64.Parse(start.ToString().Substring(0,5));
        Int64 endTimestamp = Int64.Parse(end.ToString().Substring(0,5));
        
        string configPath = "/home/ubuntu/.s3cfg";

        while (startTimestamp <= endTimestamp)
        {
            string bucketPath = "s3://"+installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/"+startTimestamp;
            Console.WriteLine("Fetching data for "+startTimestamp);

            try
            {
                // Set up process start info
                ProcessStartInfo startInfo = new ProcessStartInfo
                {
                    FileName = "s3cmd",
                    Arguments = $"--config {configPath} ls {bucketPath}",
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                };

                // Start the process
                Process process = new Process
                {
                    StartInfo = startInfo
                };

                process.Start();

                // Read the output
                string output = process.StandardOutput.ReadToEnd();
                string error = process.StandardError.ReadToEnd();

                process.WaitForExit();

                // Check for errors
                if (process.ExitCode != 0)
                {
                    Console.WriteLine("Error executing command:");
                    Console.WriteLine(error);
                }
                else
                {
                    // Define a regex pattern to match the filenames without .csv extension
                    var pattern = @"/([^/]+)\.csv$";
                    var regex = new Regex(pattern);

                    // Process each line of the output
                    foreach (var line in output.Split('\n'))
                    {
                        var match = regex.Match(line);
                        if (match.Success && long.Parse(match.Groups[1].Value) >= start && long.Parse(match.Groups[1].Value) <= end)
                        {
                            allTimestamps.Add(long.Parse(match.Groups[1].Value));
                            //Console.WriteLine(match.Groups[1].Value);
                        }
                    }

                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"Exception: {e.Message}");
            }

            startTimestamp++;
        }
        
        int totalRecords = allTimestamps.Count;
        if (totalRecords <= sampleSize)
        {
            // If the total records are less than or equal to the sample size, return all records
            Console.WriteLine("Start timestamp = "+start +" end timestamp = "+end);
            Console.WriteLine("SampledTimestamps = " + allTimestamps.Count);
            return allTimestamps;
        }

        int interval = totalRecords / sampleSize;
        var sampledTimestamps = new List<Int64>();
        
        for (int i = 0; i < totalRecords; i += interval)
        {
            sampledTimestamps.Add(allTimestamps[i]);
        }

        // If we haven't picked enough records (due to rounding), add the latest record to ensure completeness
        if (sampledTimestamps.Count < sampleSize)
        {
            sampledTimestamps.Add(allTimestamps.Last());
        }
        
        Console.WriteLine("Start timestamp = "+start +" end timestamp = "+end);
        Console.WriteLine("TotalRecords = "+totalRecords + " interval = "+ interval);
        Console.WriteLine("SampledTimestamps = " + sampledTimestamps.Count);

        return sampledTimestamps;
    }

    [HttpGet(nameof(GetUserById))]
    public ActionResult<User> GetUserById(Int64 id, Token authToken)
    {
        var session = Db.GetSession(authToken)?.User;
        if (session == null)
            return Unauthorized();
    
        var user = Db.GetUserById(id);
    
        if (user is null || !session.HasAccessTo(user))
            return Unauthorized();

        return user
              .HidePassword()
              .HideParentIfUserHasNoAccessToParent(session);
    }

    
    [HttpGet(nameof(GetInstallationById))]
    public ActionResult<Installation> GetInstallationById(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
    
        var installation = Db.GetInstallationById(id);
    
        if (installation is null || !user.HasAccessTo(installation))
            return Unauthorized();
        
        return installation
              .FillOrderNumbers()
              .HideParentIfUserHasNoAccessToParent(user)
              .HideWriteKeyIfUserIsNotAdmin(user.UserType);
    }
    
    [HttpGet(nameof(GetUsersWithDirectAccessToInstallation))]
    public ActionResult<IEnumerable<Object>> GetUsersWithDirectAccessToInstallation(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
    
        var installation = Db.GetInstallationById(id);
        
        if (installation is null || !user.HasAccessTo(installation))
            return Unauthorized();

        return installation
              .UsersWithDirectAccess()
              .Where(u => u.IsDescendantOf(user))
              .Select(u => u.HidePassword())
              .ToList();
    }

    [HttpGet(nameof(GetUsersWithInheritedAccessToInstallation))]
    public ActionResult<IEnumerable<Object>> GetUsersWithInheritedAccessToInstallation(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();

        var installation = Db.GetInstallationById(id);

        if (installation is null || !user.HasAccessTo(installation))
            return Unauthorized();

        return installation
              .Ancestors()
              .SelectMany(f => f.UsersWithDirectAccess()
                                .Where(u => u.IsDescendantOf(user))
                                .Select(u => new { folderId = f.Id, folderName = f.Name, user = u.HidePassword() }))
              .ToList();
    }

    [HttpGet(nameof(GetUsersWithDirectAccessToFolder))]
    public ActionResult<IEnumerable<Object>> GetUsersWithDirectAccessToFolder(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
    
        var folder = Db.GetFolderById(id);
        
        if (folder is null || !user.HasAccessTo(folder))
            return Unauthorized();

        return folder
            .UsersWithDirectAccess()
            .Where(u => u.IsDescendantOf(user))
            .Select(u => u.HidePassword())
            .ToList();
    }
    
    [HttpGet(nameof(GetUsersWithInheritedAccessToFolder))]
    public ActionResult<IEnumerable<Object>> GetUsersWithInheritedAccessToFolder(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
    
        var folder = Db.GetFolderById(id);
        
        if (folder is null || !user.HasAccessTo(folder))
            return Unauthorized();

        return folder
            .Ancestors()
            .SelectMany(f => f.UsersWithDirectAccess()
                .Where(u => u.IsDescendantOf(user))
                .Select(u => new { folderId = f.Id, folderName = f.Name, user = u.HidePassword() }))
            .ToList();
    }
    
    [HttpGet(nameof(GetFolderById))]
    public ActionResult<Folder> GetFolderById(Int64 id, Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();
        
        var folder = Db.GetFolderById(id);
    
        if (folder is null || !user.HasAccessTo(folder))
            return Unauthorized();

        return folder.HideParentIfUserHasNoAccessToParent(user);
    }
    
    [HttpGet(nameof(GetAllDirectChildUsers))]
    public ActionResult<IEnumerable<User>> GetAllDirectChildUsers(Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();

        return user.ChildUsers().Select(u => u.HidePassword()).ToList();
    }

    [HttpGet(nameof(GetAllChildUsers))]
    public ActionResult<IEnumerable<User>> GetAllChildUsers(Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        if (user == null)
            return Unauthorized();

        return user
              .DescendantUsers()
              .Select(u => u.HidePassword())
              .ToList();
    }


    [HttpGet(nameof(GetAllInstallations))]
    public ActionResult<IEnumerable<Installation>> GetAllInstallations(Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;

        if (user is null)
            return Unauthorized();

        return user
               .AccessibleInstallations(product:0)
               .Select(i => i.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(user).HideWriteKeyIfUserIsNotAdmin(user.UserType))
               .ToList();
    }
    
    [HttpGet(nameof(GetAllSalidomoInstallations))]
    public ActionResult<IEnumerable<Installation>> GetAllSalidomoInstallations(Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;

        if (user is null)
            return Unauthorized();

        return user
            .AccessibleInstallations(product:1)
            .ToList();
    }
    
    

    [HttpGet(nameof(GetAllFolders))]
    public ActionResult<IEnumerable<Folder>> GetAllFolders(Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;

        if (user is null)
            return Unauthorized();
        
        return new(user.AccessibleFolders().HideParentIfUserHasNoAccessToParent(user));
    }
    
    
    [HttpGet(nameof(GetAllFoldersAndInstallations))]
    public ActionResult<IEnumerable<Object>> GetAllFoldersAndInstallations(Token authToken)
    {
        var user = Db.GetSession(authToken)?.User;
        
        if (user is null)
            return Unauthorized();

        var foldersAndInstallations = user
            .AccessibleFoldersAndInstallations(product:0)
            .Do(o => o.FillOrderNumbers())
            .Select(o => o.HideParentIfUserHasNoAccessToParent(user))
            .OfType<Object>();  // Important! JSON serializer must see Objects otherwise
                                // it will just serialize the members of TreeNode %&@#!!!
        
                                // TODO Filter out write keys
        return new (foldersAndInstallations);
    }
    

    [HttpPost(nameof(CreateUser))]
    public async Task<ActionResult<User>> CreateUser([FromBody] User newUser, Token authToken)
    {
        
        var create = Db.GetSession(authToken).Create(newUser);
        if (create)
        {
            var mail_success= await Db.SendNewUserEmail(newUser);
            if (!mail_success)
            {
                Db.GetSession(authToken).Delete(newUser);
            }
            
            return mail_success ? newUser.HidePassword():Unauthorized();
        }

        return Unauthorized() ;
    }
    
    [HttpPost(nameof(CreateInstallation))]
    public async Task<ActionResult<Installation>> CreateInstallation([FromBody] Installation installation, Token authToken)
    {
        
        var session = Db.GetSession(authToken);
        
        if (! await session.Create(installation))
            return Unauthorized();
        
        return installation;
    }
    
    [HttpPost(nameof(CreateFolder))]
    public ActionResult<Folder> CreateFolder([FromBody] Folder folder, Token authToken)
    {
        var session = Db.GetSession(authToken);

        if (!session.Create(folder))
            return Unauthorized();
        
        return folder.HideParentIfUserHasNoAccessToParent(session!.User);
    }
    
    [HttpPost(nameof(GrantUserAccessToFolder))]
    public ActionResult GrantUserAccessToFolder(FolderAccess folderAccess, Token authToken)
    {
        var session = Db.GetSession(authToken);

        // TODO: automatic BadRequest when properties are null during deserialization
        var folder = Db.GetFolderById(folderAccess.FolderId);
        var user   = Db.GetUserById(folderAccess.UserId);

        return session.GrantUserAccessTo(user, folder) 
            ? Ok()
            : Unauthorized();
    }
    
    
    [HttpPost(nameof(RevokeUserAccessToFolder))]
    public ActionResult RevokeUserAccessToFolder(FolderAccess folderAccess, Token authToken)
    {
        var session = Db.GetSession(authToken);

        // TODO: automatic BadRequest when properties are null during deserialization
        var folder = Db.GetFolderById(folderAccess.FolderId);
        var user   = Db.GetUserById(folderAccess.UserId);

        return session.RevokeUserAccessTo(user, folder) 
             ? Ok()
             : Unauthorized();
    }
    
    
    [HttpPost(nameof(GrantUserAccessToInstallation))]
    public ActionResult GrantUserAccessToInstallation(InstallationAccess installationAccess, Token authToken)
    {
        var session = Db.GetSession(authToken);
       
        // TODO: automatic BadRequest when properties are null during deserialization
        var installation = Db.GetInstallationById(installationAccess.InstallationId);
        var user         = Db.GetUserById(installationAccess.UserId);
        
        return session.GrantUserAccessTo(user, installation) 
             ? Ok()
             : Unauthorized();
    }
    
    [HttpPost(nameof(RevokeUserAccessToInstallation))]
    public ActionResult RevokeUserAccessToInstallation(InstallationAccess installationAccess, Token authToken)
    {
        var session = Db.GetSession(authToken);
       
        // TODO: automatic BadRequest when properties are null during deserialization
        var installation = Db.GetInstallationById(installationAccess.InstallationId);
        var user         = Db.GetUserById(installationAccess.UserId);
        
        return session.RevokeUserAccessTo(user, installation) 
             ? Ok()
             : Unauthorized();
    }
    
    
    
    [HttpPut(nameof(UpdateUser))]
    public ActionResult<User> UpdateUser([FromBody]  User updatedUser, Token authToken)
    {
        var session = Db.GetSession(authToken);

        if (!session.Update(updatedUser)) 
            return Unauthorized();

        return updatedUser.HidePassword();
    }
    
    
    [HttpPut(nameof(UpdatePassword))]
    public ActionResult<User> UpdatePassword(String newPassword, Token authToken)
    {
        var session = Db.GetSession(authToken);

        return session.UpdatePassword(newPassword)
            ? Ok()
            : Unauthorized();
    }
    
    
    
    [HttpPut(nameof(UpdateInstallation))]
    public ActionResult<Installation> UpdateInstallation([FromBody] Installation installation, Token authToken)
    {
        var session = Db.GetSession(authToken);

        if (!session.Update(installation))
            return Unauthorized();

        if (installation.Product == 0)
        { 
            return installation.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(session!.User).HideWriteKeyIfUserIsNotAdmin(session.User.UserType);
        }
        
        return installation.HideParentIfUserHasNoAccessToParent(session!.User);
    }
    
    [HttpPost(nameof(AcknowledgeError))]
    public ActionResult AcknowledgeError(Int64 id, Token authToken)
    {
        var session = Db.GetSession(authToken);
        
        if (session == null)
            return Unauthorized();
        
        var error=Db.Errors
            .FirstOrDefault(error => error.Id == id);

        error.Seen = true;
        
        return Db.Update(error)
            ? Ok()
            : Unauthorized();
    }
    
    [HttpPost(nameof(AcknowledgeWarning))]
    public ActionResult AcknowledgeWarning(Int64 id, Token authToken)
    {
        var session = Db.GetSession(authToken);
        
        if (session == null)
            return Unauthorized();
        
        var warning=Db.Warnings
            .FirstOrDefault(warning => warning.Id == id);

        warning.Seen = true;
        
        return Db.Update(warning)
            ? Ok()
            : Unauthorized();
    }
    
    [HttpPut(nameof(UpdateFolder))]
    public ActionResult<Folder> UpdateFolder([FromBody] Folder folder, Token authToken)
    {
        var session = Db.GetSession(authToken);

        if (!session.Update(folder))
            return Unauthorized();
        
        return folder.HideParentIfUserHasNoAccessToParent(session!.User);
    }
    
    [HttpPut(nameof(MoveInstallation))]
    public ActionResult MoveInstallation(Int64 installationId,Int64 parentId, Token authToken)
    {
        var session = Db.GetSession(authToken);

        return session.MoveInstallation(installationId, parentId)
            ? Ok()
            : Unauthorized();
    }
    
    [HttpPost(nameof(UpdateFirmware))]
    public async Task<ActionResult> UpdateFirmware(Int64 batteryNode, Int64 installationId,String version,Token authToken)
    {
        var session = Db.GetSession(authToken);
        var installationToUpdate = Db.GetInstallationById(installationId);
        

        if (installationToUpdate != null)
        {
            _ = session.RunScriptInBackground(installationToUpdate.VpnIp, batteryNode,version,installationToUpdate.Product);
        }

        return Ok();
    }
    
    private static Dictionary<string, JobStatus> JobStatuses = new Dictionary<string, JobStatus>();

    [HttpPost("StartDownloadBatteryLog")]
    public async Task<ActionResult<string>> StartDownloadBatteryLog(long batteryNode, long installationId, Token authToken)
    {
        var session = Db.GetSession(authToken);
        var installationToDownload = Db.GetInstallationById(installationId);

        if (installationToDownload != null)
        {
            string jobId = Guid.NewGuid().ToString();
            _ = Task.Run(async () =>
            {
                await session.RunDownloadLogScript(installationToDownload.VpnIp, batteryNode, installationToDownload.Product);
                string fileName = $"{installationToDownload.VpnIp}-node{batteryNode}-{DateTime.Now:dd-MM-yyyy}.bin";
                string filePath = $"/home/ubuntu/backend/downloadBatteryLog/{fileName}";

                if (System.IO.File.Exists(filePath))
                {
                    SaveJobStatus(jobId, "Completed", fileName:fileName);
                }
                else
                {
                    SaveJobStatus(jobId, "Failed");
                }
            });

            // Store initial job status in in-memory storage
            SaveJobStatus(jobId, "Processing");

            return Ok(jobId);
        }

        return NotFound();
    }

    [HttpGet("DownloadBatteryLog")]
    public async Task<ActionResult> DownloadBatteryLog(string jobId)
    
    {
        Console.WriteLine("-----------------------------------Start uploading battery log-----------------------------------");
        var jobStatus = JobStatuses.TryGetValue(jobId, out var status) ? status : null;
        if (jobStatus == null || jobStatus.Status != "Completed" || string.IsNullOrEmpty(jobStatus.FileName))
        {
            return NotFound();
        }

        string fileName = jobStatus.FileName;
        string filePath = $"/home/ubuntu/backend/downloadBatteryLog/{fileName}";

        if (!System.IO.File.Exists(filePath))
        {
            return NotFound();
        }

        string contentType = "application/octet-stream";
        var memory = new MemoryStream();
        await using (var stream = new FileStream(filePath, FileMode.Open))
        {
            await stream.CopyToAsync(memory);
        }
        memory.Position = 0;

        var fileContentResult = new FileContentResult(memory.ToArray(), contentType)
        {
            //FileDownloadName = Path.GetFileName(filePath)
            FileDownloadName = fileName
        };
        
        Console.WriteLine("-----------------------------------Stop uploading battery log-----------------------------------");

        return fileContentResult;
    }
    
    [HttpDelete("DeleteBatteryLog")]
    public IActionResult DeleteBatteryLog(string fileName)
    {
        Console.WriteLine("-----------------------------------Start deleting downloaded battery log-----------------------------------");
        string filePath = $"/home/ubuntu/backend/downloadBatteryLog/{fileName}";
        try
        {
            if (System.IO.File.Exists(filePath))
            {
                System.IO.File.Delete(filePath);
                Console.WriteLine("-----------------------------------Stop deleting downloaded battery log-----------------------------------");
                return Ok();
            }
            else
            {
                return NotFound("File not found.");
            }
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Internal server error: {ex.Message}");
        }
    }
    
    private void SaveJobStatus(string jobId, string status, string fileName = null)
    {
        JobStatuses[jobId] = new JobStatus
        {
            JobId = jobId,
            Status = status,
            FileName = fileName,
            StartTime = DateTime.UtcNow // Initialize StartTime when saving
        };
    }
    
    [HttpGet("GetJobResult")]
    public ActionResult GetJobResult(string jobId)
    {
        if (string.IsNullOrEmpty(jobId))
        {
            return BadRequest(new { status = "Error", message = "Job ID is required." });
        }
        
        if (!JobStatuses.TryGetValue(jobId, out var jobStatus))
        {
            return NotFound();
        }

        if (jobStatus.Status == "Completed")
        {
            return Ok(new { status = "Completed",  fileName = jobStatus.FileName });
        }
        else if (jobStatus.Status == "Failed")
        {
            return StatusCode(500, new { status = "Failed", message = "Job processing failed." });
        }
        else if (jobStatus.Status == "Processing")
        {
            // Check for timeout
            var startTime = jobStatus.StartTime;
            var currentTime = DateTime.UtcNow;

            if ((currentTime - startTime).TotalMinutes > 60)//60 minutes as timeout => Running multiple tasks in parallel on a crowded backend server will increase the time each task takes to complete
            {
                return StatusCode(500, new { status = "Failed", message = "Job in back end timeout exceeded." });
            }

            return Ok(new { status = "Processing" });
        }
        else
        {
            return BadRequest(new { status = "Unknown", message = "Unknown job status." });
        }
    }
    
    
    [HttpPost(nameof(InsertNewAction))]
    public async Task<ActionResult<IEnumerable<Object>>> InsertNewAction([FromBody] UserAction action, Token authToken)
    {
        var session = Db.GetSession(authToken);
        var actionSuccess = await session.InsertUserAction(action);
        return actionSuccess ? Ok() : Unauthorized();

    }
    
    [HttpPost(nameof(UpdateAction))]
    public async Task<ActionResult<IEnumerable<Object>>> UpdateAction([FromBody] UserAction action, Token authToken)
    {
        var session = Db.GetSession(authToken);
        var actionSuccess = await session.UpdateUserAction(action);
        return actionSuccess ? Ok() : Unauthorized();
    }

    
    [HttpPost(nameof(DeleteAction))]
    public async Task<ActionResult<IEnumerable<Object>>> DeleteAction(Int64 actionId, Token authToken)
    {
        var session = Db.GetSession(authToken);
        var actionSuccess = await session.DeleteUserAction(actionId);
        return actionSuccess ? Ok() : Unauthorized();
    }
    
        
    [HttpPost(nameof(EditInstallationConfig))]
    public async Task<ActionResult<IEnumerable<Object>>> EditInstallationConfig([FromBody] Configuration config, Int64 installationId,Token authToken)
    {
        var session = Db.GetSession(authToken);
        
        // Send configuration changes
        var success = await session.SendInstallationConfig(installationId, config);

        // Record configuration change
        if (success)
        {
            // Create a new UserAction object
            var action = new UserAction
            {
                InstallationId = installationId,
                Timestamp = DateTime.Now,
                Description = config.GetConfigurationString()
            };
            
            var actionSuccess = await session.InsertUserAction(action);
            return actionSuccess?Ok():Unauthorized();
        }
        
        return Unauthorized();
        
    }
    
    [HttpPut(nameof(MoveFolder))]
    public ActionResult MoveFolder(Int64 folderId,Int64 parentId, Token authToken)
    {
        var session = Db.GetSession(authToken);

        return session.MoveFolder(folderId, parentId)
            ? Ok()
            : Unauthorized();
    }
    
    [HttpDelete(nameof(DeleteUser))]
    public ActionResult DeleteUser(Int64 userId, Token authToken)
    {
        var session = Db.GetSession(authToken);
        var user    = Db.GetUserById(userId);

        return session.Delete(user) 
             ? Ok()
             : Unauthorized();
    }
    
    [HttpDelete(nameof(DeleteInstallation))]
    public async Task<ActionResult> DeleteInstallation(Int64 installationId, Token authToken)
    {
        var session      = Db.GetSession(authToken);
        var installation = Db.GetInstallationById(installationId);

        return await session.Delete(installation)
             ? Ok()  
             : Unauthorized();
    }

    [HttpDelete(nameof(DeleteFolder))]
    public ActionResult DeleteFolder(Int64 folderId, Token authToken)
    {
        var session = Db.GetSession(authToken);
        var folder  = Db.GetFolderById(folderId);

        return session.Delete(folder) 
             ? Ok() 
             : Unauthorized();
        
    }
    
    [HttpPost(nameof(ResetPasswordRequest))]
    public async Task<ActionResult<IEnumerable<Object>>> ResetPasswordRequest(String username)
    {
        var user = Db.GetUserByEmail(username);
        
        if (user is null)
            return Unauthorized();
        
        var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
        var success = Db.Create(session);
        
        return success && await Db.SendPasswordResetEmail(user, session.Token)
             ? Ok() 
             : Unauthorized();
    }


    [HttpGet(nameof(ResetPassword))]
    public ActionResult<Object> ResetPassword(Token token)
    {
        var user = Db.GetSession(token)?.User;

        if (user is null)
            return Unauthorized();

        Db.DeleteUserPassword(user);

        return Redirect($"https://monitor.innov.energy/?username={user.Email}&reset=true"); // TODO: move to settings file
    }

}