using C4IT.Logging;
using System;
using System.Activities;
using System.ComponentModel;
using System.Reflection;
using static C4IT.Logging.cLogManager;
using Matrix42.Contracts.Platform.Data;
using Matrix42.ServiceRepository.Contracts.Components;
using Matrix42.Workflows.Contracts;
using System.Linq;
using Matrix42.Contracts.Platform.General;
using System.Json;
using Newtonsoft.Json;
using System.Collections.Generic;
using Matrix42.Workflows.Activities.Common.Data;
using System.Activities.Validation;
using System.Threading.Tasks;
using C4IT.Matrix42.ServerInfo;
using C4IT.LIAM;
using System.Data;
using System.Xml;
using System.IO;
using System.Runtime.Remoting.Contexts;
using Matrix42.Common;
using C4IT.MsGraph;

namespace LiamWorkflowActivities
{
    public abstract class cLIAMM42BaseActivity : NativeActivity
    {
        public const string LiamProductGuid = "28757DE2-690F-4D3B-9AB7-4AB7FB630901";

        public const string constFragmentNameDataAreaTargetPickupType = "C4IT_DataAreaTargetPickupType";

        public const string constFragmentNameDataAreaMain = "C4IT_DataAreaClassBase";
        public const string constFragmentNameConfigProviderMain = "C4IT_GCC_DataArea_Collector";
        public const string constFragmentNameAddonConfigClassBase = "C4IT_AddonConfigClassBase";
        public const string constFragmentNameConfigProviderBase = "SPSGenericConnectorConfigurationClassBase";
        public const string constFragmentNameConfigProviderAdditionalAttributes = "C4IT_GCC_DataArea_Collector_AdditionalAttributes";
        public const string constFragmentNameCustomTagBase = "C4IT_DataAreaCustomTagClassBase";
        public const string constFragmentNameNamingConvention = "C4IT_LIAMNamingConvention";
        public const string constFragmentNameConfigNamingConvention = "C4IT_LIAMConfigNamingConvention";
        public const string constTypeNameConfigProvider = "C4IT_GCC_DataAreaCollectorType";
        public const string constFragmentNameAccountAd = "SPSAccountClassAD";
        public const string constFragmentNameAccountBase = "SPSAccountClassBase";
        public const string constFragmentNameUserBase = "SPSUserClassBase";
        public const string constFragmentNameCommon = "SPSCommonClassBase";

        private static readonly Dictionary<Guid, ProviderCacheEntry> ProviderCacheByID = new Dictionary<Guid, ProviderCacheEntry>();
        private static readonly Dictionary<Guid, ProviderCacheEntry> ProviderCacheByObjectID = new Dictionary<Guid, ProviderCacheEntry>();

        internal IExtensionExecutor executor;
        internal ISchemaReaderProvider schemaReader;
        internal IDataReaderProvider dataProvider;

        public static bool IsInitialized { get; private set; } = false;

        private static Object initLock = new object();
        protected string LastOperationErrorCode { get; private set; } = string.Empty;
        protected string LastOperationErrorMessage { get; private set; } = string.Empty;

        protected void ClearOperationError()
        {
            LastOperationErrorCode = string.Empty;
            LastOperationErrorMessage = string.Empty;
        }

        protected void SetOperationError(string code, string message)
        {
            LastOperationErrorCode = string.IsNullOrWhiteSpace(code) ? "WF_OPERATION_FAILED" : code;
            LastOperationErrorMessage = message ?? string.Empty;
            LogEntry($"[{LastOperationErrorCode}] {LastOperationErrorMessage}", LogLevels.Error);
        }

        protected void SetOperationErrorFromProvider(cLiamProviderBase provider, string fallbackCode, string fallbackMessage)
        {
            if (provider is cLiamProviderExchange exProvider)
            {
                var code = exProvider.GetLastErrorCode();
                var message = exProvider.GetLastErrorMessage();
                if (!string.IsNullOrWhiteSpace(code) || !string.IsNullOrWhiteSpace(message))
                {
                    SetOperationError(
                        string.IsNullOrWhiteSpace(code) ? fallbackCode : code,
                        string.IsNullOrWhiteSpace(message) ? fallbackMessage : message);
                    return;
                }
            }

            var providerMessage = provider?.GetLastErrorMessage();
            SetOperationError(
                fallbackCode,
                string.IsNullOrWhiteSpace(providerMessage) ? fallbackMessage : providerMessage);
        }

        protected bool IsAdditionalConfigurationEnabled(cLiamProviderBase provider, string key)
        {
            if (provider?.AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
                return false;

            if (!provider.AdditionalConfiguration.TryGetValue(key, out var rawValue) || string.IsNullOrWhiteSpace(rawValue))
                return false;

            return rawValue.Equals("true", StringComparison.OrdinalIgnoreCase)
                || rawValue.Equals("1", StringComparison.OrdinalIgnoreCase)
                || rawValue.Equals("yes", StringComparison.OrdinalIgnoreCase);
        }

        protected void Initialize(NativeActivityContext context)
        {
            try
            {
                lock (initLock)
                {
                    if (IsInitialized)
                        return;

                    var Ass = Assembly.GetExecutingAssembly();
                    var LM = cLogManagerFile.CreateInstance(LocalMachine: true, A: Ass);
                    var CM = MethodBase.GetCurrentMethod();
                    LogMethodBegin(CM);
                    cLogManager.DefaultLogger.LogAssemblyInfo(Ass);
                    IsInitialized = true;
                    LogMethodEnd(CM);
                }
            }
            catch { };
        }

        public bool LoadLicensingInformation(bool force = false)
        {
            if (cC4ITLicenseM42ESM.Instance == null || force)
            {
                cC4ITLicenseM42ESM.Instance = null;
                new cC4ITLicenseM42ESM(new Guid(LiamProductGuid));
                var strLicense = getLicenseString();
                if (!string.IsNullOrEmpty(strLicense))
                    cC4ITLicenseM42ESM.Instance.LoadFromString(strLicense);
            }
            return cC4ITLicenseM42ESM.Instance.IsValid;
        }
        private string getLicenseString()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var dataRows = dataProvider.GetDataList(constFragmentNameAddonConfigClassBase, "license",
                    "UsedInTypeC4IT_LIAMConfigurationType is not null"
                );
                if (dataRows.Length <= 0)
                {
                    LogEntry($"Addon Config fragment not found", LogLevels.Debug);
                    return null;
                }
                var license = cLIAMHelper.getStringFromObject(dataRows[0]["license"]);
                LogEntry($"Addon Config license: {license}", LogLevels.Debug);
                return license;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }


        }

        private cLiamProviderBase CreateProviderInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
        {
            LoadLicensingInformation();
            ProviderData.ReplaceCustomTags();
            switch (ProviderData.ProviderType)
            {
                case eLiamProviderTypes.MsTeams:
                    return new cLiamProviderMsTeams(LiamConfiguration, ProviderData);
                case eLiamProviderTypes.Ntfs:
                    return new cLiamProviderNtfs(LiamConfiguration, ProviderData);
                case eLiamProviderTypes.ActiveDirectory:
                    return new cLiamProviderAD(LiamConfiguration, ProviderData);
                case eLiamProviderTypes.Exchange:
                    return new cLiamProviderExchange(LiamConfiguration, ProviderData);
            }
            return null;
        }

        internal static ProviderCacheEntry AddCache(Guid ID, Guid ObjectID, cLiamProviderBase Provider)
        {
            var Entry = new ProviderCacheEntry()
            {
                ID = ID,
                ObjectID = ObjectID,
                ValidUntil = DateTime.UtcNow + TimeSpan.FromMinutes(15),
                Provider = Provider
            };

            if (ProviderCacheByID.ContainsKey(ID))
                ProviderCacheByID[ID] = Entry;
            else
                ProviderCacheByID.Add(ID, Entry);
            if (ProviderCacheByObjectID.ContainsKey(ObjectID))
                ProviderCacheByObjectID[ObjectID] = Entry;
            else
                ProviderCacheByObjectID.Add(ObjectID, Entry);

            return Entry;
        }

        private static ProviderCacheEntry GetCacheByID(Guid ID)
        {
            if (!ProviderCacheByID.TryGetValue(ID, out var Entry))
                return null;

            if (Entry.ValidUntil < DateTime.UtcNow)
            {
                ProviderCacheByID.Remove(ID);
                ProviderCacheByObjectID.Remove(Entry.ObjectID);
                return null;
            }

            return Entry;
        }

        private static ProviderCacheEntry GetCacheByObjectID(Guid ObjectID)
        {
            if (!ProviderCacheByObjectID.TryGetValue(ObjectID, out var Entry))
                return null;

            if (Entry.ValidUntil < DateTime.UtcNow)
            {
                ProviderCacheByID.Remove(Entry.ID);
                ProviderCacheByObjectID.Remove(ObjectID);
                return null;
            }

            return Entry;
        }


        public cLiamProviderBase createProvider(DataRow dataMain, DataRow dataBase, IEnumerable<DataRow> dataAdditional, IEnumerable<DataRow> dataNamingConvention = null, IEnumerable<DataRow> dataCustomTag = null, SecureData secureData = null)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var PW = secureData?.Value;
                var DataProviderData = new cLiamProviderData()
                {
                    Domain = cLIAMHelper.getStringFromObject(dataMain["GCCDomain"]),
                    Credential = new cLiamCredential()
                    {
                        Domain = cLIAMHelper.getStringFromObject(dataMain["GCCDomain"]),
                        Identification = cLIAMHelper.getStringFromObject(dataBase["Account"]),
                        Secret = "***"
                    },
                    RootPath = cLIAMHelper.getStringFromObject(dataMain["GCCTarget"]),
                    MaxDepth = cLIAMHelper.getIntFromObject(dataMain["GCCMaxDepth"]),
                    GroupFilter = cLIAMHelper.getStringFromObject(dataMain["GCCgroupLDAPFilter"]),
                    GroupPath = cLIAMHelper.getStringFromObject(dataMain["GCCgroupOUPath"]),
                    GroupStrategy = (eLiamGroupStrategies)cLIAMHelper.getIntFromObject(dataMain["GCCPermissionGroupStrategy"]),
                    ProviderType = (eLiamProviderTypes)cLIAMHelper.getIntFromObject(dataMain["GCCtargetType"])
                };
                if (dataAdditional != null)
                {
                    foreach (DataRow row in dataAdditional)
                    {
                        var Name = cLIAMHelper.getStringFromObject(row["Name"]);
                        var Value = cLIAMHelper.getStringFromObject(row["Value"]);
                        DataProviderData.AdditionalConfiguration[Name] = Value;
                    }
                }
                if (dataCustomTag != null)
                {
                    foreach (DataRow row in dataCustomTag)
                    {
                        var Name = cLIAMHelper.getStringFromObject(row["Key"]);
                        var Value = cLIAMHelper.getStringFromObject(row["Name"]);
                        DataProviderData.CustomTags[Name] = Value;
                    }
                }

                var namingConventions = new List<cLiamNamingConvention>();
                if (dataNamingConvention != null)
                {
                    foreach (DataRow row in dataNamingConvention)
                    {
                        var Usage = cLIAMHelper.getIntFromObject(row["Usage"]);
                        var AccessRole = eLiamAccessRoles.Read;
                        var Scope = eLiamAccessRoleScopes.Unknown;
                        switch (Usage)
                        {
                            case -10:
                                AccessRole = eLiamAccessRoles.Traverse;
                                Scope = eLiamAccessRoleScopes.Global;
                                break;
                            case 10:
                                AccessRole = eLiamAccessRoles.Read;
                                Scope = eLiamAccessRoleScopes.Global;
                                break;
                            case 20:
                                AccessRole = eLiamAccessRoles.Write;
                                Scope = eLiamAccessRoleScopes.Global;
                                break;
                            case 30:
                                AccessRole = eLiamAccessRoles.Owner;
                                Scope = eLiamAccessRoleScopes.Global;
                                break;
                            case 40:
                                AccessRole = eLiamAccessRoles.Read;
                                Scope = eLiamAccessRoleScopes.DomainLocal;
                                break;
                            case 50:
                                AccessRole = eLiamAccessRoles.Write;
                                Scope = eLiamAccessRoleScopes.DomainLocal;
                                break;
                            case 60:
                                AccessRole = eLiamAccessRoles.Owner;
                                Scope = eLiamAccessRoleScopes.DomainLocal;
                                break;
                            case 100:
                                AccessRole = eLiamAccessRoles.ADOwner;
                                Scope = eLiamAccessRoleScopes.Global;
                                break;
                            case 110:
                                AccessRole = eLiamAccessRoles.ADMember;
                                Scope = eLiamAccessRoleScopes.Global;
                                break;
                            case 200:
                                AccessRole = eLiamAccessRoles.ExchangeMLMember;
                                Scope = eLiamAccessRoleScopes.Universal;
                                break;
                            case 210:
                                AccessRole = eLiamAccessRoles.ExchangeMLOwner;
                                Scope = eLiamAccessRoleScopes.Universal;
                                break;
                            case 250:
                                AccessRole = eLiamAccessRoles.ExchangeSMBFullAccess;
                                Scope = eLiamAccessRoleScopes.Universal;
                                break;
                            case 260:
                                AccessRole = eLiamAccessRoles.ExchangeSMBSendAs;
                                Scope = eLiamAccessRoleScopes.Universal;
                                break;
                            case 270:
                                AccessRole = eLiamAccessRoles.ExchangeSMBOwner;
                                Scope = eLiamAccessRoleScopes.Universal;
                                break;
                        }
                        DataProviderData.NamingConventions.Add(new cLiamNamingConvention()
                        {
                            AccessRole = AccessRole,
                            Scope = Scope,
                            Description = cLIAMHelper.getStringFromObject(row["Description"]),
                            DescriptionTemplate = cLIAMHelper.getStringFromObject(row["DescriptionTemplate"]),
                            Name = cLIAMHelper.getStringFromObject(row["Name"]),
                            NamingTemplate = cLIAMHelper.getStringFromObject(row["NamingTemplate"]),
                            Wildcard = cLIAMHelper.getStringFromObject(row["Wildcard"])
                        });
                    }
                }
                var sanitizedJson = JsonConvert.SerializeObject(DataProviderData, Newtonsoft.Json.Formatting.Indented);
                LogEntry("Provider configuration (sanitized JSON, copy for diagnostics tool):", LogLevels.Info);
                LogEntry(sanitizedJson, LogLevels.Info);
                DataProviderData.Credential.Secret = PW;
                var DataProvider = CreateProviderInstance(new cLiamConfiguration(), DataProviderData);


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

        }

        public ProviderCacheEntry getDataProvider(Guid ProviderConfigClassID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var Provider = GetCacheByID(ProviderConfigClassID);
                if (Provider == null)
                    Provider = GetCacheByObjectID(ProviderConfigClassID);
                if (Provider != null)
                {
                    LogEntry($"provider already cached", LogLevels.Debug);
                    return Provider;
                }
                else
                    return null;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public async Task<IEnumerable<SecurityGroupEntry>> getSecurityGroupsFromProvider(Guid ProviderConfigClassID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                ClearOperationError();
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    SetOperationError("WF_GET_SECURITYGROUPS_LICENSE_INVALID", "License not valid");
                    return new List<SecurityGroupEntry>();
                }
                var ProviderEntry = getDataProvider(ProviderConfigClassID);
                if (ProviderEntry == null)
                {
                    LogEntry($"Could not initialize Provider config class with ID {ProviderConfigClassID}", LogLevels.Warning);
                    SetOperationError("WF_GET_SECURITYGROUPS_PROVIDER_NOT_FOUND", $"Could not initialize Provider config class with ID {ProviderConfigClassID}");
                    return null;
                }

                var result = await LiamWorkflowRuntime.GetSecurityGroupsFromProviderAsync(ProviderEntry.Provider);
                if (!result.Success)
                {
                    SetOperationError(result.ErrorCode, result.ErrorMessage);
                    return null;
                }

                if (result.SecurityGroups.Count == 0)
                {
                    LogEntry($"No security groups found for Provider config class with ID {ProviderConfigClassID}", LogLevels.Warning);
                    return new List<SecurityGroupEntry>();
                }

                return result.SecurityGroups;
            }
            catch (Exception E)
            {
                LogException(E);
                SetOperationError("WF_GET_SECURITYGROUPS_EXCEPTION", E.Message);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public async Task<IEnumerable<DataAreaEntry>> getDataAreasFromProvider(Guid ProviderConfigClassID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                ClearOperationError();
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    SetOperationError("WF_GET_DATAAREAS_LICENSE_INVALID", "License not valid");
                    return new List<DataAreaEntry>();
                }
                var ProviderEntry = getDataProvider(ProviderConfigClassID);
                if (ProviderEntry == null)
                {
                    LogEntry($"Could not initialize Provider config class with ID {ProviderConfigClassID}", LogLevels.Warning);
                    SetOperationError("WF_GET_DATAAREAS_PROVIDER_NOT_FOUND", $"Could not initialize Provider config class with ID {ProviderConfigClassID}");
                    return null;
                }

                var result = await LiamWorkflowRuntime.GetDataAreasFromProviderAsync(
                    ProviderEntry.Provider,
                    ProviderEntry.ObjectID.ToString());
                if (!result.Success)
                {
                    SetOperationError(result.ErrorCode, result.ErrorMessage);
                    return null;
                }

                if (result.DataAreas.Count <= 0)
                {
                    LogEntry($"No data areas found for Provider config class with ID {ProviderConfigClassID}", LogLevels.Warning);
                    return new List<DataAreaEntry>();
                }

                return result.DataAreas;
            }
            catch (Exception E)
            {
                LogException(E);
                SetOperationError("WF_GET_DATAAREAS_EXCEPTION", E.Message);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        private async Task<cLiamDataAreaBase> getDataAreaFromUID(string UID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var ProviderID = cLIAMHelper.getUidItem(ref UID);
                if (!Guid.TryParse(ProviderID, out var ProviderGuid))
                {
                    LogEntry($"No valid Provider config class ID in UID: {UID}", LogLevels.Warning);
                    return null;
                }

                var ProviderEntry = getDataProvider(ProviderGuid);
                if (ProviderEntry == null)
                {
                    LogEntry($"Could not initialize Provider config class with ID {ProviderGuid}", LogLevels.Warning);
                    return null;
                }

                var DA = await ProviderEntry.Provider.LoadDataArea(UID);
                if (DA == null)
                {
                    LogEntry($"Could not load data area with UID '{UID}' from provider with ID '{ProviderGuid}'", LogLevels.Warning);
                    return null;
                }

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

        }

        internal class cGetDataAreaResult
        {
            internal cLiamDataAreaBase dataArea;
            internal Guid configId;
        }
        private async Task<cGetDataAreaResult> getDataAreaFromId(Guid DataAreaClassID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var ConfigID = dataProvider.GetFragmentValue<Guid>(constFragmentNameDataAreaMain, DataAreaClassID, "DataAreaConfiguration");
                if (ConfigID == Guid.Empty)
                {
                    LogEntry($"DataAreaConfiguration value not found.", LogLevels.Warning);
                    return null;
                }
                LogEntry($"Config provider class ID: {ConfigID}", LogLevels.Debug);

                var ConfigProviderEntry = getDataProvider(ConfigID);
                if (ConfigProviderEntry == null)
                {
                    LogEntry($"Could not load Config Provider from cache with ID: {ConfigID}", LogLevels.Debug);
                    return null;
                }
                var UID = dataProvider.GetFragmentValue<string>(constFragmentNameDataAreaMain, DataAreaClassID, "technicalName");
                LogEntry($"Data area UID: {UID}", LogLevels.Debug);

                var DA = await ConfigProviderEntry.Provider.LoadDataArea(UID);
                if (DA == null)
                {
                    LogEntry($"Could not found LIAM data area with UID: {UID}", LogLevels.Debug);
                    return null;
                }

                return new cGetDataAreaResult()
                {
                    dataArea = DA,
                    configId = ConfigID
                };
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }


        }


        public async Task<List<cLiamUserInfo>> getOwnerInfosFromDataArea(Guid DataAreaClassID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return new List<cLiamUserInfo>();
                }

                var ConfigID = dataProvider.GetFragmentValue<Guid>(constFragmentNameDataAreaMain, DataAreaClassID, "DataAreaConfiguration");

                if (ConfigID == Guid.Empty)
                {
                    LogEntry($"DataAreaConfiguration value not found.", LogLevels.Warning);
                    return null;
                }
                LogEntry($"Config provider class ID: {ConfigID}", LogLevels.Debug);

                var ConfigProviderEntry = getDataProvider(ConfigID);
                if (ConfigProviderEntry == null)
                {
                    LogEntry($"Could not load Config Provider from cache with ID: {ConfigID}", LogLevels.Debug);
                    return null;
                }

                var UID = dataProvider.GetFragmentValue<string>(constFragmentNameDataAreaMain, DataAreaClassID, "technicalName");
                LogEntry($"Data area UID: {UID}", LogLevels.Debug);

                var DA = await ConfigProviderEntry.Provider.LoadDataArea(UID);
                if (DA == null)
                {
                    LogEntry($"Could not found LIAM data area with UID: {UID}", LogLevels.Debug);
                    return null;
                }

                var lstOwners = await DA.GetOwnersAsync();
                if (lstOwners == null)
                {
                    LogEntry($"Error getting owners of data area with UID: {UID}", LogLevels.Warning);
                    return null;
                }
                LogEntry($"Number of owners: {lstOwners.Count}", LogLevels.Debug);


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


        }

        internal List<Guid> getPersonsFromUsers(List<cLiamUserInfo> Users)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {

                var ListOwners = new List<Guid>();
                foreach (var OwnerEntry in Users)
                {
                    if (string.IsNullOrEmpty(OwnerEntry.UserPrincipalName))
                        continue;

                    var FilterValues = new List<string>()
                    {
                        OwnerEntry.UserPrincipalName
                    };
                    var Filter = AsqlHelper.BuildInCondition("UserPrincipalName", FilterValues);
                    LogEntry($"ASql Filter: {Filter}");

                    var Rows = dataProvider.GetDataList(constFragmentNameAccountAd, "[Expression-ObjectID] as Id, UserPrincipalName",
                        Filter
                    );
                    if (Rows == null)
                    {
                        LogEntry($"no rows in account entry list found with UserPrincipalName='{OwnerEntry.UserPrincipalName}'", LogLevels.Warning);
                        return null;
                    }

                    LogEntry($"ASql Filter: {Filter}");

                    foreach (DataRow AdEntry in Rows)
                    {
                        // get the expression object id for the account
                        var AdId = cLIAMHelper.getGuidFromObject(AdEntry["ID"]);
                        if (AdId == Guid.Empty)
                        {
                            LogEntry($"no expression object id found for AccountAd entry", LogLevels.Warning);
                            continue;
                        }

                        // get the account class base & ID of the user class base
                        Guid[] ids2 = { AdId };
                        var rows2 = dataProvider.GetDataList(constFragmentNameAccountBase, "Owner",
                        AsqlHelper.BuildInCondition("[Expression-ObjectID]",
                            ids2)
                    );
                        if (rows2 == null || Rows.Length <= 0)
                        {
                            LogEntry($"account class base fragment not found, ObjectId={AdId}", LogLevels.Debug);
                            continue;
                        }

                        var OwnerID = cLIAMHelper.getGuidFromObject(rows2[0]["Owner"]);
                        if (OwnerID == Guid.Empty)
                        {
                            LogEntry($"Could not get Owner ID for account class base: ObjectId={AdId}", LogLevels.Debug);
                            continue;
                        }

                        // get the SPSUserClassBase entry
                        var UserID = dataProvider.GetFragmentValue<Guid>(constFragmentNameUserBase, OwnerID, "ID");
                        var UserObjectID = dataProvider.GetFragmentValue<Guid>(constFragmentNameUserBase, OwnerID, "Expression-ObjectID");

                        // get the user common class base & check the state for 2001
                        Guid[] ids3 = { UserObjectID };
                        var rows3 = dataProvider.GetDataList(constFragmentNameCommon, "State",
                        AsqlHelper.BuildInCondition("[Expression-ObjectID]",
                            ids3));

                        if (rows3 == null || rows3.Length <= 0)
                        {
                            LogEntry($"account class base fragment not found: ObjectId={AdId}", LogLevels.Debug);
                            continue;
                        }
                        var State = cLIAMHelper.getIntFromObject(rows3[0]["State"]);
                        LogEntry($"User found: ID={UserID}, ObjectID={UserObjectID}, State={State}", LogLevels.Debug);
                        if (State == 2023)
                            ListOwners.Add(UserID);
                        else
                            LogEntry($"User is not active", LogLevels.Debug);
                    }

                }

                return ListOwners;

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

        public async Task<cLiamPermissionResult> grantPermission(Guid DataAreaId, Guid PersonId, int AccessType, Guid AccoundId)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return new cLiamPermissionResult()
                    {
                        Valid = false
                    };
                }

                var DA = await getDataAreaFromId(DataAreaId);
                if (DA == null)
                    return null;
                Guid myAccountId;
                if (AccoundId != null && AccoundId != Guid.Empty)
                    myAccountId = (Guid)AccoundId;
                else
                {
                    myAccountId = dataProvider.GetFragmentValue<Guid>(constFragmentNameUserBase, PersonId, "PrimaryAccount");
                    if (myAccountId == Guid.Empty)
                    {
                        LogEntry($"Account value not found.", LogLevels.Warning);
                        return null;
                    }
                    LogEntry($"Account class ID: {myAccountId}", LogLevels.Debug);
                }

                var accountEoid = dataProvider.GetFragmentValue<Guid>(constFragmentNameAccountBase, myAccountId, "Expression-ObjectId");
                if (accountEoid == Guid.Empty)
                {
                    LogEntry($"Account EOID value not found.", LogLevels.Warning);
                    return null;
                }
                LogEntry($"Account EOID: {accountEoid}", LogLevels.Debug);

                // get the expression object id for the account
                var UPN = dataProvider.GetFragmentValue<string>(constFragmentNameAccountAd, accountEoid, "UserPrincipalName");
                if (string.IsNullOrEmpty(UPN))
                {
                    LogEntry($"no UserPrincipalName found for AccountAd entry", LogLevels.Warning);
                    return null;
                }


                var User = new cLiamUserInfo()
                {
                    EMail = UPN,
                    UserPrincipalName = UPN
                };

                var RetVal = await DA.dataArea.GrantPermissionAsync(User, (eLiamAccessRoles)AccessType);

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

        /// <summary>
        /// Erstellt eine Distribution Group samt zugehöriger AD-Gruppen (Member, Owner) über den konfigurierten Provider.
        /// </summary>
        public bool createDistributionGroup(
            Guid ProviderConfigClassID,
            string name,
            string alias,
            string displayName = null,
            string primarySmtpAddress = null)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                // Lizenz prüfen (falls nötig analog zu cloneTeam)
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry("Error: License not valid", LogLevels.Error);
                    return false;
                }

                // DataProvider holen
                var providerEntry = getDataProvider(ProviderConfigClassID);
                if (providerEntry == null)
                {
                    LogEntry($"Could not initialize Provider config class with ID {ProviderConfigClassID}", LogLevels.Warning);
                    return false;
                }

                // Auf den konkreten Exchange-Provider casten
                if (providerEntry.Provider is cLiamProviderExchange exProv)
                {
                    // Aufruf der Methode im Provider
                    var result = exProv.exchangeManager.CreateDistributionGroupWithOwnershipGroups(
                        name,
                        alias,
                        displayName,
                        primarySmtpAddress,
                        out string errorCode,
                        out string errorMessage
                    );

                    if (result == null)
                    {
                        LogEntry(
                            $"createDistributionGroup failed [{errorCode}] {errorMessage}",
                            LogLevels.Error);
                        return false;
                    }

                    LogEntry(
                        $"createDistributionGroup succeeded. ObjectGuid='{result.Item1}', GroupCount='{result.Item2?.Count ?? 0}'",
                        LogLevels.Info);
                    return true;
                }

                LogEntry($"Provider is not a cLiamProviderExchange, but {providerEntry.Provider.GetType().Name}", LogLevels.Warning);
                return false;
            }
            catch (Exception e)
            {
                LogException(e);
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        /// <summary>
        /// Erstellt eine Shared Mailbox samt zugehöriger AD-Gruppen (FullAccess, SendAs, Owner) über den konfigurierten Provider.
        /// </summary>
        public bool createSharedMailbox(
            Guid ProviderConfigClassID,
            string name,
            string alias,
            string displayName = null,
            string primarySmtpAddress = null)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                // Lizenz prüfen (falls nötig analog zu cloneTeam)
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry("Error: License not valid", LogLevels.Error);
                    return false;
                }

                // DataProvider holen
                var providerEntry = getDataProvider(ProviderConfigClassID);
                if (providerEntry == null)
                {
                    LogEntry($"Could not initialize Provider config class with ID {ProviderConfigClassID}", LogLevels.Warning);
                    return false;
                }

                // Auf den konkreten Exchange-Provider casten
                if (providerEntry.Provider is cLiamProviderExchange exProv)
                {
                    // Aufruf der Methode im Provider
                    var result = exProv.exchangeManager.CreateSharedMailboxWithOwnershipGroups(
                        name,
                        alias,
                        displayName,
                        primarySmtpAddress,
                        out string errorCode,
                        out string errorMessage
                    );

                    if (result == null)
                    {
                        LogEntry(
                            $"createSharedMailbox failed [{errorCode}] {errorMessage}",
                            LogLevels.Error);
                        return false;
                    }

                    LogEntry(
                        $"createSharedMailbox succeeded. ObjectGuid='{result.Item1}', GroupCount='{result.Item2?.Count ?? 0}'",
                        LogLevels.Info);
                    return true;
                }

                LogEntry($"Provider is not a cLiamProviderExchange, but {providerEntry.Provider.GetType().Name}", LogLevels.Warning);
                return false;
            }
            catch (Exception e)
            {
                LogException(e);
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }


        public async Task<cMsGraphResultBase> cloneTeam(Guid ProviderConfigClassID, string teamId, string name, string description, int visibility, int partsToClone, string additionalMembers, string additionalOwners)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }

                var ProviderEntry = getDataProvider(ProviderConfigClassID);
                if (ProviderEntry == null)
                {
                    LogEntry($"Could not initialize Provider config class with ID {ProviderConfigClassID}", LogLevels.Warning);
                    return null;
                }

                // Prüfen ob ProviderEntry vom Typ cLiamProviderMsTeams ist
                if (ProviderEntry.Provider is cLiamProviderMsTeams msTeamsProvider)
                {
                    // Aufruf von cloneTeam mit allen Parametern außer ProviderConfigClassID
                    return await msTeamsProvider.cloneTeam(teamId, name, description, visibility, partsToClone, additionalMembers, additionalOwners);
                }

                // Falls nicht cLiamProviderMsTeams, false zurückgeben
                return null;
            }
            catch (Exception E)
            {
                LogException(E);
                return null; // Geändert von null zu false, da Rückgabetyp bool ist
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public async Task<bool> revokePermission(Guid DataAreaId, Guid PersonId, int AccessType, Guid AccoundId)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return false;
                }

                var DA = await getDataAreaFromId(DataAreaId);
                if (DA == null)
                    return false;
                Guid myAccountId;
                if (AccoundId != null && AccoundId != Guid.Empty)
                    myAccountId = (Guid)AccoundId;
                else
                {
                    myAccountId = dataProvider.GetFragmentValue<Guid>(constFragmentNameUserBase, PersonId, "PrimaryAccount");
                    if (myAccountId == Guid.Empty)
                    {
                        LogEntry($"Account value not found.", LogLevels.Warning);
                        return false;
                    }
                    LogEntry($"Account class ID: {myAccountId}", LogLevels.Debug);
                }

                var accountEoid = dataProvider.GetFragmentValue<Guid>(constFragmentNameAccountBase, myAccountId, "Expression-ObjectId");
                if (accountEoid == Guid.Empty)
                {
                    LogEntry($"Account EOID value not found.", LogLevels.Warning);
                    return false;
                }
                LogEntry($"Account EOID: {accountEoid}", LogLevels.Debug);
                var Rows = dataProvider.GetDataList(constFragmentNameAccountAd, "UserPrincipalName",
                    string.Format("[Expression-ObjectId]='{0}'", accountEoid)
                );
                if (Rows == null)
                {
                    LogEntry($"no ad account entry list found with eoid='{accountEoid}'", LogLevels.Warning);
                    return false;
                }

                if (Rows == null || Rows.Length == 0)
                {
                    LogEntry($"no rows in account entry list found with eoid='{accountEoid}'", LogLevels.Warning);
                    return false;
                }

                // get the expression object id for the account
                var UPN = cLIAMHelper.getStringFromObject(Rows[0]["UserPrincipalName"]);
                if (string.IsNullOrEmpty(UPN))
                {
                    LogEntry($"no UserPrincipalName found for AccountAd entry", LogLevels.Warning);
                    return false;
                }


                var User = new cLiamUserInfo()
                {
                    EMail = UPN,
                    UserPrincipalName = UPN
                };

                var RetVal = await DA.dataArea.RevokePermissionAsync(User, (eLiamAccessRoles)AccessType);

                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
        /*

        [Route("getLicenseInformation"), HttpGet]
        public cC4ITLicenseM42ESM getLicenseInformation()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();
                return cC4ITLicenseM42ESM.Instance;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return null;
        }


        [Route("getLicensedModules"), HttpGet]
        public List<dynamic> getLicensedModules()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (cC4ITLicenseM42ESM.Instance == null)
                    LoadLicensingInformation();

                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                    return new List<dynamic>();

                List<string> rs = new List<string>();
                foreach (var key in cC4ITLicenseM42ESM.Instance?.Modules.Keys)
                {
                    rs.Add(key.ToString());
                }

                var ClassID = SPSDataEngineSchemaReader.ClassGetIDFromName(constFragmentNameDataAreaTargetPickupType);
                LogEntry($"Addon Config class ID: {ClassID}", LogLevels.Debug);
                var dataTable = FragmentRequest.SimpleLoad(ClassID,
                    "Id, Value",
                    AsqlHelper.BuildInCondition("ID",
                    rs));
                if (dataTable?.Rows == null || dataTable.Rows.Count <= 0)
                {
                    LogEntry($"Addon Config fragment not found: ClassID={ClassID}", LogLevels.Debug);
                    return null;
                }

                List<dynamic> rs2 = new List<dynamic>();
                foreach (DataRow p in dataTable.Rows)
                {
                    var obj = cLiamM42Helper.ToDynamic(cC4ITLicenseM42ESM.Instance.Modules[cLIAMHelper.getGuidFromObject(p["Id"])]);
                    obj.Value = cLIAMHelper.getStringFromObject(p["Value"]);
                    rs2.Add(obj);
                }
                return rs2;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return null;
        }
        */


    }
}
