using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using C4IT.HTTP;
using C4IT.MsGraph;
using Newtonsoft.Json.Linq;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using C4IT.DataHistoryProvider;

namespace C4IT.FASD.Communication.Citrix
{
    public class cCitrixCommunication : cHttpApiBase
    {
        public const int constMaxPageSize = 1000;
        public const string constCitrixBaseUrl = "https://api.cloud.com/{Request}";
        public const string constCitrixUserList = "monitorodata/Users?$top={top}&$skip={skip}&$select=Id,Sid,Upn,Domain&$filter=Domain ne ''";
        public const string constCitrixUserSessions = "monitorodata/Sessions?$filter=UserId eq {UserId} and (EndDate eq null or StartDate ge {MinStartDate})&$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState";

        public cCitrixCommunication() : base("Citrix ID tenant", "https://api.cloud.com/cctrustoauth2/{Tenant}/tokens/clients", "Citrix Comunication")
        {
            KnownAutoRetryErrors.Add(HttpStatusCode.Forbidden);
            autoRetryCount = 2;
            autoRetryDelay = 100;
        }

        private protected override List<KeyValuePair<string, string>> GetRequestHeaders(cOAuthLogonInfo privLogonInfo)
        {
            
            return new List<KeyValuePair<string, string>>()
            {
                new KeyValuePair<string, string>("Authorization", $"CwsAuth Bearer={AccessToken}"),
                new KeyValuePair<string, string>("Citrix-CustomerId", privLogonInfo.Tenant),
                new KeyValuePair<string, string>("Citrix-InstanceId", privLogonInfo.InstanceId)
            };
        }
        public async Task<cCitrixResultBase> RequestAsync(string Request, eHttpMethod httpMethod = eHttpMethod.get, object JsonData = null, bool retryForbidden = false, string nameProperty = null, bool noAutoRelogon = false)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {

                var strUrl = constCitrixBaseUrl;
                strUrl = strUrl.Replace("{Request}", Request);

                var data = await privRequestAsync(strUrl, httpMethod, JsonData, retryForbidden, noAutoRelogon: noAutoRelogon);
                if (data == null)
                    return null;

                if (data is bool isValid)
                {
                    if (isValid)
                        return new cCitrixResultBase(null);
                    else
                        return null;
                }

                var RetVal = new cCitrixResultBase(data, nameProperty);
                return RetVal;
            }
            catch (Exception E)
            {
                cLogManager.LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
            return null;
        }

        public async Task<cCitrixResultList> RequestListAsync(string Request, bool loadPaged = false, bool retryForbidden = false)
        {
            try
            {
                // get the request url

                var strUrl = constCitrixBaseUrl;
                strUrl = strUrl.Replace("{Request}", Request);

                var res = await privRequestAsync(strUrl, autoRetry: retryForbidden);
                if (res != null)
                {
                    var RetVal = new cCitrixResultList(retryForbidden);
                    RetVal.AddResult(res);
                    if (!RetVal.hasRemaining || loadPaged)
                        return RetVal;
                    while (RetVal.hasRemaining)
                    {
                        if (!await RequestNextAsync(RetVal))
                            return RetVal;
                    }
                    return RetVal;
                }
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
            }
            return null;
        }

        public async Task<bool> RequestNextAsync(cCitrixResultList List)
        {
            try
            {
                var res = await privRequestAsync(List.NextResultUrl, autoRetry: List.retryForbidden);
                if (res != null)
                {
                    List.AddResult(res);
                    return true;
                }
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
            }

            return false;
        }

        public class cCitrixResultList : List<cCitrixResultBase>
        {
            public string NextResultUrl { get; private set; } = null;
            public bool retryForbidden { get; private set; } = false;

            public cCitrixResultList(bool retryForbidden)
            {
                this.retryForbidden = retryForbidden;
            }

            public bool hasRemaining
            {
                get
                {
                    return !string.IsNullOrEmpty(NextResultUrl);
                }
            }

            public void AddResult(dynamic Result)
            {
                try
                {
                    NextResultUrl = null;

                    if (Result.TryGetValue("@odata.nextLink", out JToken JT))
                    {
                        var JO = (JValue)JT;

                        NextResultUrl = Result["@odata.nextLink"];
                    }

                    if (Result.TryGetValue("value", out JToken JT2))
                    {
                        foreach (dynamic Entry in Result.value)
                            try
                            {
                                var val = new cCitrixResultBase(Entry);
                                this.Add(val);
                            }
                            catch (Exception E)
                            {
                                cLogManager.DefaultLogger.LogException(E);
                            }
                    }
                }
                catch (Exception E)
                {
                    cLogManager.DefaultLogger.LogException(E);
                }
            }

        }

        public class cCitrixResultBase
        {
            public string ID { get; private set; } = null;
            public string ODataId { get; private set; } = null;
            public string DisplayName { get; private set; } = null;

            public string Context { get; private set; } = null;

            public dynamic Result { get; private set; } = null;

            public cCitrixResultBase(dynamic Result, string namePropery = null)
            {
                this.Result = Result;

                try { ID = Result.Id; } catch { }
                ;
                try
                {
                    if (namePropery == null)
                        namePropery = "displayName";
                    if (Result.TryGetValue(namePropery, out JToken JT1))
                    {
                        var _ty = JT1.GetType().ToString();
                        if (JT1 is JValue _jVal)
                            DisplayName = _jVal.Value?.ToString();
                    }
                    else if (Result.TryGetValue("name", out JToken JT2))
                        DisplayName = Result.name;
                }
                catch { }
                try { Context = Result["@odata.context"]; } catch { }
                ODataId = GetStringFromDynamic(Result, "@odata.id");
                if (string.IsNullOrEmpty(ODataId))
                    ODataId = @"https://api.cloud.com/" + ID;
            }

            public cCitrixResultBase(cCitrixResultBase Result)
            {
                if (Result == null)
                    return;

                this.Result = Result.Result;
                ID = Result.ID;
                ODataId = Result.ODataId;
                DisplayName = Result.DisplayName;
                Context = Result.Context;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static string GetStringFromDynamic(dynamic O, string ProperyName)
            {
                try
                {
                    return (string)O[ProperyName];
                }
                catch { }
                return null;
            }
        }
    }
}
