using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Diagnostics;

using C4IT.FASD.Base;
using C4IT.HTTP;
using C4IT.Logging;
using C4IT.MultiLanguage;
using C4IT.Security;
using C4IT.FASD.Communication.Agent;

using FasdCockpitBase.Models;
using FasdCockpitCommunication;
using FasdCockpitCommunication.TicketOverview;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using static C4IT.Logging.cLogManager;
using C4IT.Configuration;
using System.Runtime.InteropServices.WindowsRuntime;

namespace C4IT.FASD.Cockpit.Communication
{
    public class cFasdCockpitCommunicationWeb : cFasdCockpitCommunicationBase
    {
        private cAgentApiHelper agentApiHelper;

        private string ApiTimingLog = null;

        public cFasdCockpitCommunicationWeb()
        {
            M42 = new cF4sdCockpitCommunicationM42Web();
        }

        public override bool IsDemo() => false;

        public override bool CheckConnectionInfo()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var serverUrl = cFasdCockpitMachineConfiguration.Instance.ServerUrl;
                var url = new Uri(serverUrl);
                return url.IsAbsoluteUri;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return false;
        }

        public async override Task<cCheckConnectionResult> CheckConnection(Version minServerVersion)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var output = new cCheckConnectionResult() { ConnectionStatus = enumConnectionStatus.unknown };
            try
            {
                var http = GetHttpHelper(false);
                var result = await http.GetHttpJson($"api/CheckConnection", 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    output.ConnectionStatus = enumConnectionStatus.serverResponseError;
                    return output;
                }

                SaveApiResultValueJson("CheckConnection", result.Result);
                var connectionResult = JsonConvert.DeserializeObject<cFasdApiConnectionInfo>(result.Result);
                output.ApiConnectionInfo = connectionResult;
                var assembly = Assembly.GetExecutingAssembly();
                var ClientVersion = assembly.GetName().Version;

                if (!Version.TryParse(connectionResult.MinCockpitVersion, out var minClientVersion))
                {
                    LogEntry($"Minimum cockpit version is illegal: {connectionResult.MinCockpitVersion}");
                    return output;
                }

                if (!Version.TryParse(connectionResult.ServerVersion, out var serverVersion))
                {
                    LogEntry($"Server version is illegal: {connectionResult.ServerVersion}");
                    return output;
                }

                if (ClientVersion < minClientVersion)
                {
                    LogEntry($"Cockpit version ({ClientVersion}) is less than required ({minClientVersion}).");
                    output.ConnectionStatus = enumConnectionStatus.incompatibleServerVersion;
                    return output;
                }

                if (serverVersion < minServerVersion)
                {
                    LogEntry($"Server version ({serverVersion}) is less than required ({minServerVersion}).");
                    output.ConnectionStatus = enumConnectionStatus.incompatibleServerVersion;
                    return output;
                }

                switch (connectionResult.WebServerStatus)
                {
                    case enumWebServerStatus.starting:
                        output.ConnectionStatus = enumConnectionStatus.serverStarting;
                        return output;
                    case enumWebServerStatus.notConfigured:
                        output.ConnectionStatus = enumConnectionStatus.serverNotConfigured;
                        return output;
                }

                output.ConnectionStatus = enumConnectionStatus.connected;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return output;
        }

        public static cOneTimePW GetOneTimePw() => new cOneTimePW("OneTimePw", cSecurePassword.Instance);

        public cHttpHelper GetHttpHelper(bool useToken)
        {
            try
            {
                string Token;
                lock (CockpitUserInfoLock)
                {
                    Token = CockpitUserInfo?.Token;
                }
                if (useToken && Token != null)
                    return new cHttpHelper(cFasdCockpitMachineConfiguration.Instance.ServerUrl, null, false) { BearerToken = Token };

                return new cHttpHelper(cFasdCockpitMachineConfiguration.Instance.ServerUrl, GetOneTimePw().GetCredential(), false);
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return null;
        }

        public override async Task<Guid> GetUserIdByAccount(string userName, string userDomain)
        {
            var output = Guid.Empty;
            try
            {
                var http = GetHttpHelper(false);
                var result = await http.GetHttpJson($"api/Logon/GetUserIdByAccount?Account={userName}&Domain={userDomain}", 15000, CancellationToken.None);

                if (result.IsOk)
                {
                    SaveApiResultValue("Logon-GetUserIdByAccount", result.Result, "json");
                    output = JsonConvert.DeserializeObject<Guid>(result.Result);
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            return output;
        }

        public async override Task<cF4sdUserInfo> WinLogon()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = new cHttpHelper(cFasdCockpitMachineConfiguration.Instance.ServerUrl, null, false, UseCookies: true, UseDefaultCredentials: true);
                var _url = "api/Logon/Logon";
                if (!string.IsNullOrEmpty(cMultiLanguageSupport.CurrentLanguage))
                    _url = $"api/Logon/Logon?lang={cMultiLanguageSupport.CurrentLanguage}";
                var result = await http.GetHttpJson(_url, 20000, CancellationToken.None);

                if (result.IsOk)
                {
                    if (Debug_apiValues) SaveApiResultValueJson("Logon-Logon", result.Result);
                    var _retVal = JsonConvert.DeserializeObject<cF4sdUserInfo>(result.Result);
                    return _retVal;
                }
                else
                    apiError = (int)result.Status;

            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("Logon-Logon", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<cF4SdUserInfoChange> RegisterExternalTokenAsync(cF4SDTokenRegistration Token)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var _payLoad = JsonConvert.SerializeObject(Token);
                var http = GetHttpHelper(true);
                var apiAccessInfo = await http.PostJsonAsync($"api/Logon/RegisterExternalToken", _payLoad, 14000, System.Threading.CancellationToken.None);
                if (apiAccessInfo.IsOk)
                {
                    var _str = apiAccessInfo.Result;
                    if (Debug_apiValues) SaveApiResultValueJson("Logon-RegisterExternalToken", _str);
                    var RetVal = JsonConvert.DeserializeObject<cF4SdUserInfoChange>(_str);
                    return RetVal;
                }
                else
                    apiError = (int)apiAccessInfo.Status;

            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("Logon-RegisterExternalToken", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<cF4SDAdditionalUserInfo> GetAdditionalUserInfo(enumAdditionalAuthentication Type)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(true);
                var result = await http.GetHttpJson($"api/Logon/GetAdditionalUserInfo?AccountType={Type}", 5000, CancellationToken.None);
                if (result.IsOk)
                {
                    var _retVal = JsonConvert.DeserializeObject<cF4SDAdditionalUserInfo>(result.Result);
                    return _retVal;
                }
                else
                    apiError = (int)result.Status;

            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("Logon-GetAdditionalUserInfo", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<bool> IsAnalyticsModuleActive()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            bool output = false;
            try
            {
                var http = GetHttpHelper(false);
                var result = await http.GetHttpJson("api/F4SDAnalytics/GetF4SDAnalyticsState", 2000, CancellationToken.None);

                if (result.IsOk)
                {
                    if (Debug_apiValues) SaveApiResultValueJson("F4SDAnalytics-GetF4SDAnalyticsState", result.Result);
                    output = JsonConvert.DeserializeObject<bool>(result.Result);
                }
                else
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                }

            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("F4SDAnalytics-GetF4SDAnalyticsState", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return output;
        }

        public override async Task<bool> CreateUserSession(cF4SDUserSessionParameters sessionParams)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var payload = JsonConvert.SerializeObject(sessionParams);

                var result = await http.PostJsonAsync("api/F4SDAnalytics/CreateUserSession", payload, 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                }

                return result.IsOk;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("F4SDAnalytics-CreateUserSession", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return false;
        }

        public override async Task CloseUserSession(Guid sessionId)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var parameter = new cF4SDUserSessionParameters() { SessionId = sessionId, SessionDate = DateTime.UtcNow };
                var payload = JsonConvert.SerializeObject(parameter);

                var result = await http.PostJsonAsync("api/F4SDAnalytics/CloseUserSession", payload, 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                }
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("F4SDAnalytics-CloseUserSession", timeStart, apiError);
                LogMethodEnd(CM);
            }
        }

        public override async Task<bool> StartCase(cF4SDCaseParameters caseParameters)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var payload = JsonConvert.SerializeObject(caseParameters);
                var result = await http.PostJsonAsync("api/F4SDAnalytics/CreateCase", payload, 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                }

                return result.IsOk;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("F4SDAnalytics-CreateCase", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return false;
        }

        public override async Task<bool> UpdateCase(cF4SDCaseStatusParameters caseStatusParameters, List<cF4SDCaseTime> caseTimes)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                cF4SDCaseTimeParameters param = new cF4SDCaseTimeParameters
                {
                    CaseId = caseStatusParameters.CaseId,
                    ActiveTime = caseStatusParameters.ActiveTime,
                    StatusId = caseStatusParameters?.StatusId,
                    CaseTimes = caseTimes
                };

                var http = GetHttpHelper(false);
                var payload = JsonConvert.SerializeObject(param);

                var result = await http.PostJsonAsync("api/F4SDAnalytics/UpdateCaseState", payload, 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                }

                return result.IsOk;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("F4SDAnalytics-UpdateCaseState", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return false;
        }

        public override async Task<bool> ReportQuickAction(cF4SDQuickActionParameters quickActionParameters)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var payload = JsonConvert.SerializeObject(quickActionParameters);
                var result = await http.PostJsonAsync("api/F4SDAnalytics/ReportQuickActionExecution", payload, 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                }

                return result.IsOk;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("F4SDAnalytics-ReportQuickActionExecution", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return false;
        }

        public override async Task<bool> KeepAliveCase(Guid caseId)
        {
            try
            {
                Debug.WriteLine($"#############!!!!!!!!!!!!! KeepAliveCase: {caseId}");

                var http = GetHttpHelper(false);
                var payload = JsonConvert.SerializeObject(caseId);
                var result = await http.PostJsonAsync("api/F4SDAnalytics/KeepAliveCase", payload, 15000, CancellationToken.None);

                if (!result.IsOk)
                    await CheckConnectionStatus?.Invoke();

                return result.IsOk;
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return false;
        }

        public override async Task<bool> KeepAliveSession(Guid sessionId)
        {
            try
            {
                var http = GetHttpHelper(false);
                var payload = JsonConvert.SerializeObject(sessionId);
                var result = await http.PostJsonAsync("api/F4SDAnalytics/KeepAliveSession", payload, 15000, CancellationToken.None);

                if (!result.IsOk)
                    await CheckConnectionStatus?.Invoke();

                return result.IsOk;
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return false;
        }

        public override async Task<cCollectorStatusInfo> CheckCollectorStatusAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            var apiError = 0;
            var timeStart = DateTime.UtcNow;
            try
            {
                var http = GetHttpHelper(false);
                
                var result = await http.GetHttpJson("api/CheckCollectorStatus", 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    return null;
                }
                var strXml = JsonConvert.DeserializeObject<cCollectorStatusInfo>(result.Result);
                return strXml;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("Check Collector Status", timeStart, apiError);
                LogMethodEnd(CM);
            }
            return null;
        }

        public override async Task<cFasdBaseConfig> GetConfiguration(enumFasdConfigurationType configType)
        {
            var CM = MethodBase.GetCurrentMethod();

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            LogMethodBegin(CM);
            try
            {
                var http = GetHttpHelper(true);
                var url = $"api/GetConfiguration?configType={configType}";
                var result = await http.GetHttpJson(url, 20000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    return null;
                }

                var strXml = JsonConvert.DeserializeObject<string>(result.Result);
                if (Debug_apiValues) SaveApiResultValue("GetConfiguration-" + configType.ToString(), strXml, "xml");
                var output = cFasdBaseConfig.GetConfigByType(configType, strXml);
                return output;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetConfiguration-" + configType.ToString(), timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<Guid?> GetSearchResultsStart(string searchValue, CancellationToken cancellationToken)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var url = $"api/SearchDetailedStart?search={searchValue}";

                var result = await http.GetHttpJson(url, 20000, cancellationToken, true);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    if (!cancellationToken.IsCancellationRequested)
                        await CheckConnectionStatus?.Invoke();
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("SearchDetailedStart", result.Result, url);
                var deserializedObject = JsonConvert.DeserializeObject<Guid?>(result.Result);
                return deserializedObject;
            }
            catch (TaskCanceledException E) when (cancellationToken.IsCancellationRequested)
            {
                apiError = E.HResult;
                LogEntry("GetSearchResultsStart was cancelled by token.", LogLevels.Debug);
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("SearchDetailedStart", timeStart, apiError);
                LogMethodEnd(CM);
            }
            return null;
        }

        public override async Task<cFasdApiSearchResultCollection> GetSearchResultsResult(Guid taskID, CancellationToken cancellationToken)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var url = $"api/SearchDetailedResult?taskID={taskID}";

                var result = await http.GetHttpJson(url, 20000, cancellationToken, true);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    if (!cancellationToken.IsCancellationRequested)
                        await CheckConnectionStatus?.Invoke();
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("SearchDetailedResult", result.Result, url);
                var deserializedObject = JsonConvert.DeserializeObject<cFasdApiSearchResultCollection>(result.Result);
                return deserializedObject;
            }
            catch (TaskCanceledException E) when (cancellationToken.IsCancellationRequested)
            {
                apiError = E.HResult;
                LogEntry("GetSearchResultsStart was cancelled by token.", LogLevels.Debug);
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("SearchDetailedResult", timeStart, apiError);
                LogMethodEnd(CM);
            }
            return null;
        }

        public override async Task GetSearchResultsStop(Guid taskID, CancellationToken cancellationToken)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var url = $"api/SearchDetailedStop?taskID={taskID}";

                var result = await http.GetHttpJson(url, 20000, cancellationToken);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    if (!cancellationToken.IsCancellationRequested)
                        await CheckConnectionStatus?.Invoke();
                    return;
                }
            }
            catch (TaskCanceledException E) when (cancellationToken.IsCancellationRequested)
            {
                apiError = E.HResult;
                LogEntry("GetSearchResultsStart was cancelled by token.", LogLevels.Debug);
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("SearchDetailedResult", timeStart, apiError);
                LogMethodEnd(CM);
            }
        }


        public override async Task<cFasdApiSearchResultCollection> GetPhoneSearchResults(cPhoneSearchParameters searchInfo)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var output = new cFasdApiSearchResultCollection();

                if (searchInfo == null || searchInfo.phone == null)
                    return output;

                var name = searchInfo.name ?? "";
                name = HttpUtility.UrlEncode(name);
                var phone = HttpUtility.UrlEncode(searchInfo.phone);

                var http = GetHttpHelper(false);

                var strUrl = $"api/SearchPhone?searchPhone={phone}&searchName={name}";
                var result = await http.GetHttpJson(strUrl, 10000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("SearchPhone", result.Result, strUrl);
                var deserializedObject = JsonConvert.DeserializeObject<cFasdApiSearchResultCollection>(result.Result);
                output = deserializedObject;
                return output;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("SearchPhone", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }


        public override async Task<cFasdApiSearchResultCollection> GetComputerSearchResults(string Name, string Domain)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var _Name = HttpUtility.UrlEncode(Name);
                var _Domain = "";
                if (string.IsNullOrWhiteSpace(Domain))
                    _Domain = HttpUtility.UrlEncode(Domain);

                var strUrl = $"api/SearchComputerByName?Name={_Name}&Domain={_Domain}";

                var http = GetHttpHelper(true);
                var result = await http.GetHttpJson(strUrl, 10000, System.Threading.CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("SearchComputerByName", result.Result, strUrl);
                var deserializedObject = JsonConvert.DeserializeObject<cFasdApiSearchResultCollection>(result.Result);

                return deserializedObject;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("SearchComputerByName", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<cFasdApiSearchResultCollection> GetUserSearchResults(string Name, List<string> SIDs)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var _Name = HttpUtility.UrlEncode(Name);

                var strSids = "";
                foreach (var _sid in SIDs)
                {
                    if (strSids != "")
                        strSids += ",";
                    strSids += _sid;
                }

                strSids = HttpUtility.UrlEncode(strSids);

                var strUrl = $"api/SearchUserByNameAndSids?Name={_Name}&SIDs={strSids}";

                var http = GetHttpHelper(true);
                var result = await http.GetHttpJson(strUrl, 10000, System.Threading.CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("SearchUserByNameAndSids", result.Result, strUrl);
                var deserializedObject = JsonConvert.DeserializeObject<cFasdApiSearchResultCollection>(result.Result);

                return deserializedObject;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("SearchUserByNameAndSids", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;

        }

        private void EnrichInfosOfRelationBySearchResult(List<cF4sdApiSearchResultRelation> relations, List<cFasdApiSearchResultEntry> searchResults)
        {
            try
            {
                if (relations is null || searchResults is null)
                    return;

                foreach (var relation in relations)
                {
                    var matchingSearchResult = searchResults
                        .FirstOrDefault(searchResult => relation.Identities.Any(identity => identity.Id.Equals(searchResult.id)));

                    if (matchingSearchResult?.Infos is null)
                        continue;

                    if (relation.Infos is null)
                        relation.Infos = new Dictionary<string, string>();

                    foreach (var info in matchingSearchResult.Infos)
                    {
                        relation.Infos["relation_" + info.Key] = info.Value;
                    }
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public override async Task<cF4sdStagedSearchResultRelationTaskId> StartGatheringRelations(IEnumerable<cFasdApiSearchResultEntry> relatedTo, CancellationToken token)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                string url = $"api/StagedSearchRelations?age={cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge}";
                string json = JsonConvert.SerializeObject(relatedTo);
                var http = GetHttpHelper(true);
                var result = await http.PostJsonAsync(url, json, 5_000, token);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    return null;
                }

                return JsonConvert.DeserializeObject<cF4sdStagedSearchResultRelationTaskId>(result.Result);
            }
            catch (Exception ex)
            {
                apiError = ex.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(ex);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("StagedSearchRelationsStart", timeStart, apiError);
                LogMethodEnd(CM);
            }
            return null;
        }

        public override async Task<cF4sdStagedSearchResultRelations> GetStagedRelations(Guid id, CancellationToken token)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                string url = $"api/StagedSearchRelations/{id}";
                var http = GetHttpHelper(true);
                var result = await http.GetHttpJson(url, 25_000, token, true);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    return null;
                }

                return JsonConvert.DeserializeObject<cF4sdStagedSearchResultRelations>(result.Result);
            }
            catch (Exception ex)
            {
                apiError = ex.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(ex);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("StagedSearchRelationsStart", timeStart, apiError);
                LogMethodEnd(CM);
            }
            return null;
        }

        private async Task<cF4sdStagedSearchResultRelations> GetRelationResults(Guid relationTaskId, CancellationToken token)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                string url = $"api/StagedSearchRelations/{relationTaskId}";
                var http = GetHttpHelper(true);
                var result = await http.GetHttpJson(url, 5_000, token, true);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    return null;
                }

                return JsonConvert.DeserializeObject<cF4sdStagedSearchResultRelations>(result.Result);
            }
            catch (Exception ex)
            {
                apiError = ex.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(ex);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("StagedSearchRelations", timeStart, apiError);
                LogMethodEnd(CM);
            }
            return null;
        }

        public override async Task<List<cF4sdApiSearchResultRelation>> GetSearchResultRelations(enumF4sdSearchResultClass resultType, List<cFasdApiSearchResultEntry> searchResults)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            var output = new List<cF4sdApiSearchResultRelation>();

            try
            {
                var resultIds = searchResults.Select(resultData => resultData.id).ToList();

                if (resultIds?.Count <= 0)
                {
                    LogEntry("No valid resultIds where passed.", LogLevels.Error);
                    return output;
                }

                cF4sdStagedSearchResultRelationTaskId relationTaskId = await StartGatheringRelations(searchResults, CancellationToken.None).ConfigureAwait(false);

                if (relationTaskId is null || relationTaskId?.Id == Guid.Empty)
                {
                    LogEntry("Could not start gathering relations.", LogLevels.Warning);
                    return output;
                }

                const int maxRetryCount = 30;
                TimeSpan getRelationsDelay = TimeSpan.FromMilliseconds(500);
                for (int i = 0; i < maxRetryCount; i++)
                {
                    var relations = await GetRelationResults(relationTaskId.Id, CancellationToken.None).ConfigureAwait(false);
                    output.AddRange(relations?.Relations);

                    if (relations.IsComplete)
                        break;

                    await Task.Delay(getRelationsDelay);
                }

                EnrichInfosOfRelationBySearchResult(output, searchResults);

                output = output.OrderByDescending(relation => relation.LastUsed)
                    .ToList();
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetUsageDetailed", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return output;
        }

        #region Ticketübersicht

        public override async Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var normalizedKeys = (keys ?? Enumerable.Empty<string>())
                    .Where(k => !string.IsNullOrWhiteSpace(k))
                    .Distinct(StringComparer.OrdinalIgnoreCase)
                    .ToList();

                var http = GetHttpHelper(true);
                var scope = useRoleScope ? "role" : "personal";
                var urlBuilder = new StringBuilder($"api/TicketOverview/GetCounts?scope={scope}");

                if (normalizedKeys.Count > 0)
                {
                    var joinedKeys = HttpUtility.UrlEncode(string.Join(",", normalizedKeys));
                    urlBuilder.Append($"&keys={joinedKeys}");
                }

                var url = urlBuilder.ToString();
                var result = await http.GetHttpJson(url, 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    if (CheckConnectionStatus != null)
                        await CheckConnectionStatus.Invoke();

                    LogEntry($"Error on requesting ticket overview counts ({scope}). Status: {result.Status}", LogLevels.Warning);
                    return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
                }

                if (Debug_apiValues) SaveApiResultValueJson("TicketOverview.GetCounts", result.Result, url);

                var response = JsonConvert.DeserializeObject<TicketOverviewCountsResponse>(result.Result);
                return response?.ToDictionary(normalizedKeys) ?? new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                if (CheckConnectionStatus != null)
                    await CheckConnectionStatus.Invoke();

                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("TicketOverview.GetCounts", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
        }

        public override async Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;
            var output = new List<cF4sdApiSearchResultRelation>();

            try
            {
                if (string.IsNullOrWhiteSpace(key))
                    return output;

                var http = GetHttpHelper(true);
                var scope = useRoleScope ? "role" : "personal";
                var safeKey = HttpUtility.UrlEncode(key);
                var url = $"api/TicketOverview/GetRelations?key={safeKey}&scope={scope}&count={Math.Max(0, count)}";

                var result = await http.GetHttpJson(url, 20000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    if (CheckConnectionStatus != null)
                        await CheckConnectionStatus.Invoke();

                    LogEntry($"Error on requesting ticket overview relations for '{key}' ({scope}). Status: {result.Status}", LogLevels.Warning);
                    return output;
                }

                if (Debug_apiValues) SaveApiResultValueJson("TicketOverview.GetRelations", result.Result, url);

                var relations = JsonConvert.DeserializeObject<List<cF4sdApiSearchResultRelation>>(result.Result);
                if (relations != null)
                    output = relations;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                if (CheckConnectionStatus != null)
                    await CheckConnectionStatus.Invoke();

                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("TicketOverview.GetRelations", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return output;
        }

        #endregion

        public override async Task<cF4SDHealthCardRawData> GetHealthCardData(cF4sdHealthCardRawDataRequest requestData)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var jsonData = JsonConvert.SerializeObject(requestData);
                var urlData = HttpUtility.UrlEncode(jsonData);

                var http = GetHttpHelper(true);
                var url = $"api/GetRawDataStart?jsonRequest={urlData}";
                var result = await http.GetHttpJson(url, 20000, System.Threading.CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    LogEntry($"Error on requesting healthcard data: {result.ReasonPhrase}", LogLevels.Error);
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("GetRawDataStart", result.Result, url);
                var output = JsonConvert.DeserializeObject<cF4SDHealthCardRawData>(result.Result);
                return output;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetRawDataStart", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<cF4SDHealthCardRawData> GetHealthCardData(Guid healthCardId)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var urlData = HttpUtility.UrlEncode(healthCardId.ToString());

                var http = GetHttpHelper(true);
                var url = $"api/GetRawDataNext?RequestId={urlData}";
                var result = await http.GetHttpJson(url, 20000, System.Threading.CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    LogEntry($"Error on requesting healthcard data: {result.ReasonPhrase}", LogLevels.Error);
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("GetRawDataNext", result.Result, url);
                var output = JsonConvert.DeserializeObject<cF4SDHealthCardRawData>(result.Result);

                return output;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetRawDataNext", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<List<cF4SDHealthCardRawData.cHealthCardDetailsTable>> GetDetailsData(cF4sdHealthCardRawDataRequest requestData)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var jsonData = JsonConvert.SerializeObject(requestData);
                var urlData = HttpUtility.UrlEncode(jsonData);

                var http = GetHttpHelper(true);
                var url = $"api/GetDetailsData?jsonRequest={urlData}";
                var result = await http.GetHttpJson(url, 20000, System.Threading.CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    LogEntry($"Error on requesting details data: {result.ReasonPhrase}", LogLevels.Error);
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("GetDetailsData", result.Result, url);
                var output = JsonConvert.DeserializeObject<List<cF4SDHealthCardRawData.cHealthCardDetailsTable>>(result.Result);

                return output;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetDetailsData", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<int> GetPagedDataCount(cF4sdHealthSelectionDataRequest requestData)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var jsonData = JsonConvert.SerializeObject(requestData);
                var urlData = HttpUtility.UrlEncode(jsonData);

                var http = GetHttpHelper(true);
                var url = $"api/GetSelectionCount?jsonRequest={urlData}";
                var result = await http.GetHttpJson(url, 20000, System.Threading.CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    LogEntry($"Error on requesting paged data count: {result.ReasonPhrase}", LogLevels.Error);
                    return -1;
                }

                if (Debug_apiValues) SaveApiResultValueJson("GetSelectionCount", result.Result, url);
                var output = JsonConvert.DeserializeObject<int>(result.Result);

                return output;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetSelectionCount", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return -1;
        }

        public override async Task<cF4SDHealthCardRawData.cHealthCardTable> GetPagedData(cF4sdHealthSelectionDataRequest requestData)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var jsonData = JsonConvert.SerializeObject(requestData);
                var urlData = HttpUtility.UrlEncode(jsonData);

                var http = GetHttpHelper(true);
                var url = $"api/GetSelectionData?jsonRequest={urlData}";
                var result = await http.GetHttpJson(url, 20000, System.Threading.CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    await CheckConnectionStatus?.Invoke();
                    LogEntry($"Error on requesting paged data: {result.ReasonPhrase}", LogLevels.Error);
                    return null;
                }

                if (Debug_apiValues) SaveApiResultValueJson("GetSelectionData", result.Result, url);
                var output = JsonConvert.DeserializeObject<cF4SDHealthCardRawData.cHealthCardTable>(result.Result);

                return output;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetSelectionData", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<bool> UpdateHealthcardTableData(cF4SDWriteParameters dataParameter)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            bool output = false;
            try
            {
                var http = GetHttpHelper(true);
                var url = $"api/WriteProperty";
                var payload = JsonConvert.SerializeObject(dataParameter);

                var result = await http.PostJsonAsync(url, payload, 20000, CancellationToken.None);

                if (result.IsOk)
                    return true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return output;
        }
        public override async Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            bool output = false;
            try
            {
                var http = GetHttpHelper(true);
                var url = $"api/Matrix42/TicketFinalization";
                var payload = JsonConvert.SerializeObject(ticketData);

                var result = await http.PostJsonAsync(url, payload, 20000, CancellationToken.None);

                if (result.IsOk)
                    return true;
                else
                    apiError = (int)result.Status;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("Matrix42-TicketFinalization", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return output;
        }

        public override async Task<bool> GetAgentApiAccessInfo()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var apiAccessInfo = await http.GetHttpJson($"api/GetAgentApiConfiguration", 10000, System.Threading.CancellationToken.None);

                if (!apiAccessInfo.IsOk)
                {
                    apiError = (int)apiAccessInfo.Status;
                    return false;
                }

                if (Debug_apiValues) SaveApiResultValueJson("GetAgentApiConfiguration", apiAccessInfo.Result);
                var apiConfiguration = JsonConvert.DeserializeObject<cAgentApiConfiguration>(apiAccessInfo.Result);
                if (apiConfiguration != null)
                    apiConfiguration.ClientSecret = cSecurePassword.Instance.Decode(apiConfiguration.ClientSecret);

                var cockpitConfiguration = new cCockpitConfiguration() { agentApiConfiguration = apiConfiguration };
                cCockpitConfiguration.Instance = cockpitConfiguration;

                agentApiHelper = new cAgentApiHelper();
                await agentApiHelper.LogonAsync(apiConfiguration);

                return true;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetAgentApiConfiguration", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return false;
        }

        public override async Task<bool> GetCockpitConfiguration()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(true);
                var cockpitconfigResult = await http.GetHttpJson($"api/GetCockpitConfiguration", 10000, System.Threading.CancellationToken.None);

                if (!cockpitconfigResult.IsOk)
                {
                    apiError = (int)cockpitconfigResult.Status;
                    return false;
                }

                if (Debug_apiValues) SaveApiResultValueJson("GetCockpitConfiguration", cockpitconfigResult.Result);
                var Configuration = JsonConvert.DeserializeObject<cCockpitConfiguration>(cockpitconfigResult.Result);
                if (Configuration?.agentApiConfiguration?.ClientSecret != null)
                    Configuration.agentApiConfiguration.ClientSecret = cSecurePassword.Instance.Decode(Configuration.agentApiConfiguration.ClientSecret);

                cCockpitConfiguration.Instance = Configuration;

                agentApiHelper = new cAgentApiHelper();
                await agentApiHelper.LogonAsync(Configuration.agentApiConfiguration);

                return true;
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetCockpitConfiguration", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return false;
        }

        public override async Task<List<string>> GetQuickActionsOfServer()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            var apiError = 0;
            var timeStart = DateTime.UtcNow;

            try
            {
                var http = GetHttpHelper(false);
                var result = await http.GetHttpJson("api/QuickAction/GetQuickActionList", 15000, CancellationToken.None);

                if (!result.IsOk)
                {
                    apiError = (int)result.Status;
                    if (CheckConnectionStatus != null)
                    {
                        await CheckConnectionStatus.Invoke();
                    }
                    return new List<string>();
                }
                if (result.Result is string jsonString && !string.IsNullOrWhiteSpace(jsonString))
                {
                    return JsonConvert.DeserializeObject<List<string>>(result.Result);
                }
            }
            catch (Exception E)
            {
                apiError = E.HResult;
                LogException(E);
            }
            finally
            {
                if (Debug_apiTiming) SaveApiTimingEntry("GetQuickActionList", timeStart, apiError);
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task<bool> GetAgentOnlineStatus(int AgentDeviceId, int? AgentUserId = null)
        {
            try
            {
                if (cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode == null)
                    return false;

                var onlineStatus = await agentApiHelper.GetOnlineStatusAsync(cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode.Value, AgentDeviceId, AgentUserId);

                if (onlineStatus != null)
                    return onlineStatus.IsOnline;
            }
            catch (Exception E)
            {
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }

            return false;

        }

        public override async Task<cF4sdAgentScript> GetQuickActionOfAgent(int ScriptId)
        {
            var output = new cF4sdAgentScript();

            try
            {
                var apiResult = await agentApiHelper.GetScript(ScriptId);
                if (apiResult == null)
                {
                    await CheckConnectionStatus?.Invoke();
                    return output;
                }

                output.Type = (enumAgentScriptType)(int)apiResult.Type;
                output.UserPermissionRequired = apiResult.PersmissionLevel == 2 || apiResult.PersmissionLevel == 4;
                output.Id = apiResult.Id;
                output.Name = apiResult.Name;
            }
            catch (Exception E)
            {
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }

            return output;
        }

        public override async Task<List<cF4sdAgentScript>> GetQuickActionsOfAgent()
        {
            var output = new List<cF4sdAgentScript>();

            try
            {
                var apiResult = await agentApiHelper.GetScripts();
                if (apiResult == null)
                {
                    await CheckConnectionStatus?.Invoke();
                    return output;
                }

                foreach (var script in apiResult)
                {
                    var newAgentScript = new cF4sdAgentScript
                    {
                        Type = (enumAgentScriptType)(int)script.Type,
                        UserPermissionRequired = script.PersmissionLevel >= 2,
                        Id = script.Id,
                        Name = script.Name
                    };

                    output.Add(newAgentScript);
                }
            }
            catch (Exception E)
            {
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }

            return output;
        }

        public override async Task<Guid> ActualizeAgentData(int AgentDeviceId, int? AgentUserId = null)
        {
            Guid output = Guid.Empty;

            try
            {
                if (cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode == null)
                    return output;

                LogEntry($"Actualize Data - Actializing Data for DeviceId: {AgentDeviceId} & UserId {AgentUserId ?? -1}");

                output = await agentApiHelper.ActualizeData(cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode.Value, AgentDeviceId, AgentUserId);

                LogEntry($"Actualize Data - Actializing Data ActualizingId: {output}");
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return output;
        }

        public override async Task<enumActualizeStatus> GetActualizeAgentDataStatus(Guid actualizeId, enumFasdInformationClass informationClass)
        {
            var output = enumActualizeStatus.unknown;
            try
            {
                enumAgentActualizeStatus actualizeStatus = enumAgentActualizeStatus.Unknown;

                LogEntry($"Actualize Data - Get Actializing Status for Id: {actualizeId}");
                actualizeStatus = await agentApiHelper.GetActualizeStatus(actualizeId, informationClass);
                LogEntry($"Actualize Data - Actializing Status for Id: {actualizeId} = {actualizeStatus}");

                switch (actualizeStatus)
                {
                    case enumAgentActualizeStatus.FinishedByTimeOut:
                        output = enumActualizeStatus.failed;
                        break;
                    case enumAgentActualizeStatus.Finished:
                        output = enumActualizeStatus.successfull;
                        break;
                    default:
                        output = enumActualizeStatus.unknown;
                        break;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            return output;
        }

        public override async Task<Guid?> TryActivateDirectConnection(int DeviceId)
        {

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode == null)
                    return null;

                var connectionId = await agentApiHelper.ActivateDirectConnection(cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode.Value, DeviceId);

                if (connectionId.Equals(Guid.Empty))
                {
                    LogEntry($"Couldn't create connection to device with id: {DeviceId}", LogLevels.Error);
                    return null;
                }

                const int retries = 90;
                enumDirectConnectionStatus enumDirectConnectionStatus = enumDirectConnectionStatus.Unknown;

                for (int i = 0; i < retries; i++)
                {
                    await Task.Delay(500);
                    enumDirectConnectionStatus = await agentApiHelper.GetDirectConnectionStatus(connectionId);

                    if (enumDirectConnectionStatus == enumDirectConnectionStatus.Connected)
                        return connectionId;
                    if (enumDirectConnectionStatus == enumDirectConnectionStatus.ClosingConnection)
                    {
                        LogEntry("Connection is allready closing. Please try again.");
                        return null;
                    }
                }
                LogEntry("Timeout while waiting for direct connection.", LogLevels.Debug);

            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return null;
        }

        public override async Task StopDirectConnection(Guid connectionId) => await agentApiHelper.StopDirectConnection(connectionId);

        public override async Task<bool> IsDirectConnectionUp(Guid connectionId)
        {
            var connectionStatus = await agentApiHelper.GetDirectConnectionStatus(connectionId);
            return connectionStatus == enumDirectConnectionStatus.Connected;
        }

        public override async Task<bool> DirectConnectionExtendDuration(Guid connectonId, int timeOutInSeconds) => await agentApiHelper.DirectConnectionExtendDuration(connectonId, timeOutInSeconds);

        public override async Task<List<List<object>>> GetQuickActionHistory(string QuickActionName, int OrgId, int DeviceId, int? UserId)
        {
            var output = new List<List<object>>();

            try
            {
                if (!cF4SDCockpitXmlConfig.Instance.QuickActionConfig.F4SDQuickActions.QuickActions.TryGetValue(QuickActionName, out var quickAction))
                    return output;

                if (quickAction is cFasdQuickActionLocal)
                    return output;

                if (quickAction is cF4sdQuickActionRemote remoteQuickAction)
                {
                    var revisions = await agentApiHelper.GetRevisions(new DateTime(1900, 01, 01), DateTime.MaxValue, OrgId, DeviceId, UserId);
                    revisions = revisions.Where(revision => revision.ScriptName == remoteQuickAction.ScriptName).ToList();

                    foreach (var revision in revisions)
                    {
                        enumQuickActionRevisionStatus convertedRevisionStatus = enumQuickActionRevisionStatus.unknown;

                        switch (revision.Status)
                        {
                            case enumRevisionStatus.Success:
                                convertedRevisionStatus = enumQuickActionRevisionStatus.finishedSuccessfull;
                                break;
                            case enumRevisionStatus.Warning:
                            case enumRevisionStatus.Error:
                            case enumRevisionStatus.Fatal:
                                convertedRevisionStatus = enumQuickActionRevisionStatus.finishedWithError;
                                break;
                            case enumRevisionStatus.Created:
                            default:
                                convertedRevisionStatus = enumQuickActionRevisionStatus.inProgress;
                                break;
                        }

                        List<object> revisionLine = new List<object>
                        {
                            convertedRevisionStatus,
                            revision.CreateDate,
                            revision.Output
                        };

                        output.Add(revisionLine);
                    }
                }

            }
            catch (Exception E)
            {
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }

            return output;
        }

        public override async Task<cF4sdAgentScriptInformation> RunAgentScript(cF4sdQuickActionRemote Script, int OrgCode, int DeviceId, int? DeviceAccountId, string Parameters)
        {
            var output = new cF4sdAgentScriptInformation();

            try
            {
                var agentScriptResult = await agentApiHelper.RunSingleScript(Script.AgentScriptId, OrgCode, DeviceId, DeviceAccountId, Parameters);
                output.ScriptId = agentScriptResult.Id;
                output.RevisionId = agentScriptResult.RevisionId;
                output.TaskId = agentScriptResult.TaskId;
            }
            catch (Exception E)
            {
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }

            return output;
        }

        public override async Task<enumQuickActionStatus> GetAgentQuickActionStatus(int TaskId)
        {
            try
            {
                var taskStatus = await agentApiHelper.GetTaskStatus(TaskId);

                switch (taskStatus)
                {
                    case enumAgentTaskStatus.ConnectingToClient:
                        return enumQuickActionStatus.ConnectingToClient;
                    case enumAgentTaskStatus.WaitingForUserAcceptance:
                        return enumQuickActionStatus.WaitingForUserAcceptance;
                    case enumAgentTaskStatus.Running:
                    case enumAgentTaskStatus.DownloadingScript:
                    case enumAgentTaskStatus.Canceling:
                        return enumQuickActionStatus.Running;
                    case enumAgentTaskStatus.Finished:
                        return enumQuickActionStatus.Finished;
                    case enumAgentTaskStatus.Canceled:
                        return enumQuickActionStatus.Cancelled;
                    default:
                        return enumQuickActionStatus.Unknown;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return enumQuickActionStatus.Unknown;
        }

        public override async Task<cF4sdQuickActionRevision> GetAgentRevision(int RevisionId, int OrgId)
        {
            var output = new cF4sdQuickActionRevision();

            try
            {
                var agentRevision = await agentApiHelper.GetRevision(RevisionId, OrgId);

                switch (agentRevision?.Status)
                {
                    case enumRevisionStatus.Success:
                        output.Status = enumQuickActionRevisionStatus.finishedSuccessfull;
                        break;
                    case enumRevisionStatus.Warning:
                    case enumRevisionStatus.Error:
                    case enumRevisionStatus.Fatal:
                        output.Status = enumQuickActionRevisionStatus.finishedWithError;
                        break;
                    case enumRevisionStatus.Created:
                        output.Status = enumQuickActionRevisionStatus.inProgress;
                        break;
                }

                output.OutputText = agentRevision.Output;

                if (agentRevision.Measures?.Count > 0)
                {
                    output.Measures = new List<cF4sdQuickActionRevision.cMeasure>();
                    foreach (var measure in agentRevision.Measures)
                    {
                        output.Measures.Add(new cF4sdQuickActionRevision.cMeasure()
                        {
                            Id = measure.Measure.MeasureId,
                            Value = measure.Value.Data,
                            PostValue = measure.PostValue.Data
                        });
                    }
                }
            }
            catch (Exception E)
            {
                await CheckConnectionStatus?.Invoke();
                LogException(E);
            }

            return output;
        }

        public override async Task CancelAgentTask(int TaskId)
        {
            try
            {
                var cancelSuccessfull = await agentApiHelper.CancelTask(TaskId);

                if (!cancelSuccessfull)
                    LogEntry($"Failed to cancel task with id: {TaskId}", LogLevels.Warning);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void SaveApiResultValueJson(string ApiName, string json, string comment = null)
        {
            List<string> comments = null;
            if (comment != null)
                comments = new List<string>() { comment };
            SaveApiResultValueJson(ApiName, json, comments);
        }

        private void SaveApiResultValueJson(string ApiName, string json, List<string> comment)
        {
            try
            {
                string jsonFormatted = JValue.Parse(json).ToString(Formatting.Indented);
                if (comment != null)
                {
                    var _cmm = "";
                    foreach (var c in comment)
                    {
                        _cmm += "//\t" + c + "\r\n";
                    }
                    jsonFormatted = _cmm + jsonFormatted;
                }

                SaveApiResultValue(ApiName, jsonFormatted, "json");
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void SaveApiTimingEntry(string ApiName, DateTime timeStart, int error)
        {
            try
            {
                if (!Debug_apiTiming)
                    return;

                var duration = (DateTime.UtcNow - timeStart).TotalMilliseconds;
                var _now = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-FFFFF");

                if (ApiTimingLog == null)
                {
                    if (!(cLogManager.Instance is cLogManagerFile lf))
                        return;

                    if (string.IsNullOrEmpty(lf.LogFolder))
                        return;

                    var _fn = Path.GetFileNameWithoutExtension(lf.GetLogFileName());
                    ApiTimingLog = Path.Combine(lf.LogFolder, _fn + "-ApiTiming.log");
                }

                var _line = string.Format("{0}\t{1}\t{2}\t{3}\r\n", _now, ApiName, duration, error);
                System.IO.File.AppendAllText(ApiTimingLog, _line, Encoding.UTF8);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void SaveApiResultValue(string ApiName, string Value, string extension)
        {
            try
            {
                if (!Debug_apiValues)
                    return;

                if (!(cLogManager.Instance is cLogManagerFile lf))
                    return;

                if (string.IsNullOrEmpty(lf.LogFolder))
                    return;

                var _now = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-FFFFF");
                var _lf = Path.Combine(lf.LogFolder, "ApiValues");

                if (!Directory.Exists(_lf))
                    Directory.CreateDirectory(_lf);

                var _fn = Path.Combine(_lf, ApiName + "-" + _now + "." + extension);
                System.IO.File.WriteAllText(_fn, Value, Encoding.UTF8);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public override async Task InitializeAfterOnlineAsync() { await Task.CompletedTask; }

        public override async Task TerminateAsync() { await Task.CompletedTask; }

        public override Uri GetMediaContentUri(string ContentRelPath)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            try
            {
                if (!string.IsNullOrWhiteSpace(cFasdCockpitMachineConfiguration.Instance.ServerUrl))
                {
                    Uri httpsUri = new Uri(cFasdCockpitMachineConfiguration.Instance.ServerUrl + ContentRelPath, UriKind.Absolute);
                    UriBuilder uriBuilder = new UriBuilder(httpsUri) { Scheme = "http", Port = -1 };
                    return uriBuilder.Uri;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

            return null;
        }
        
    }
}
