using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using C4IT.Matrix42.ServerInfo;
using System.Text.RegularExpressions;
using LiamNtfs;
using System.DirectoryServices;
using System.Security.Principal;

namespace C4IT.LIAM
{
    public class cLiamProviderExchange : cLiamProviderBase
    {
        public static Guid exchangeModuleId = new Guid("A1E213C3-6517-EA11-4881-000C2980FD95");
        public readonly ExchangeManager exchangeManager;
        internal readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();

        private string exchangeUri;
        private PSCredential credential;
        private string organizationalUnit;
        private string lastErrorCode = string.Empty;
        private string lastErrorMessage;
        private bool isLoggedOn = false;

        public cLiamProviderExchange(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
            base(LiamConfiguration, ProviderData)
        {
            exchangeUri = ProviderData.RootPath;

            if (!string.IsNullOrEmpty(ProviderData.GroupPath))
                organizationalUnit = ProviderData.GroupPath;
            else
                organizationalUnit = ProviderData.Domain;

            // Credential erstellen
            var securePassword = new SecureString();
            foreach (char c in ProviderData.Credential.Secret)
            {
                securePassword.AppendChar(c);
            }
            credential = new PSCredential(ProviderData.Credential.Identification, securePassword);
            // ExchangeManager initialisieren
            exchangeManager = new ExchangeManager(this, exchangeUri, credential, ProviderData.Domain, organizationalUnit);
            // AD-Zugriff initialisieren
            var LI = new cNtfsLogonInfo()
            {
                Domain = Domain,
                User = Credential?.Identification,
                UserSecret = Credential?.Secret,
                TargetGroupPath = this.GroupPath
            };

            // Asynchrone Initialisierung starten
            _ = activeDirectoryBase.LogonAsync(LI).ConfigureAwait(false);
        }

        /// <summary>
        /// Extrahiert den GUID-Wert aus den Properties.
        /// Zunächst wird versucht, "objectGUID" zu lesen – ist dieser leer, wird "GUID" verwendet.
        /// </summary>
        internal static string ExtractObjectGuid(dynamic properties)
        {
            // Erstversuch: objectGUID (wie in AD)
            var value = properties["objectGUID"]?.Value;
            if (value == null || string.IsNullOrEmpty(value.ToString()))
            {
                // Alternative: GUID (wie von den Exchange-Cmdlets zurückgegeben)
                value = properties["GUID"]?.Value;
            }
            if (value is byte[] guidBytes)
                return new Guid(guidBytes).ToString();
            if (value is Guid guid)
                return guid.ToString();
            return value?.ToString() ?? string.Empty;
        }

        public string GetLastErrorCode()
        {
            return lastErrorCode;
        }

        private void ClearLastError()
        {
            lastErrorCode = string.Empty;
            lastErrorMessage = string.Empty;
        }

        private void SetLastError(string code, string message)
        {
            lastErrorCode = string.IsNullOrWhiteSpace(code) ? "EXCH_UNKNOWN_ERROR" : code;
            lastErrorMessage = message ?? string.Empty;
            LogEntry($"[{lastErrorCode}] {lastErrorMessage}", LogLevels.Error);
        }

        public override async Task<bool> LogonAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                ClearLastError();
                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    SetLastError("EXCH_LOGON_LICENSE_INVALID", "License not valid or Exchange module not licensed");
                    return false;
                }

                try
                {
                    var testMailboxes = exchangeManager.GetSharedMailboxes(
                        "Name -like '*'",
                        out string errorCode,
                        out string errorMessage);
                    if (testMailboxes == null)
                    {
                        SetLastError(errorCode, $"Failed to connect to Exchange: {errorMessage}");
                        isLoggedOn = false;
                        return false;
                    }

                    if (testMailboxes != null)
                    {
                        LogEntry("Successfully connected to Exchange", LogLevels.Info);
                        isLoggedOn = true;
                        ClearLastError();
                        return true;
                    }
                }
                catch (Exception ex)
                {
                    LogException(ex);
                    SetLastError("EXCH_LOGON_EXCEPTION", $"Failed to connect to Exchange: {ex.Message}");
                    isLoggedOn = false;
                    return false;
                }

                SetLastError("EXCH_LOGON_FAILED", "Unknown error connecting to Exchange");
                return false;
            }
            catch (Exception E)
            {
                LogException(E);
                SetLastError("EXCH_LOGON_EXCEPTION", $"Exception during Exchange logon: {E.Message}");
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override string GetLastErrorMessage()
        {
            return lastErrorMessage;
        }

        public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int MaxDepth = -1)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                ClearLastError();
                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    SetLastError("EXCH_GET_DATAAREAS_LICENSE_INVALID", "License not valid or Exchange module not licensed");
                    return new List<cLiamDataAreaBase>();
                }

                if (!isLoggedOn && !await LogonAsync())
                    return null;

                var DataAreas = new List<cLiamDataAreaBase>();

                // Shared Mailboxes
                var sharedMailboxes = exchangeManager.GetSharedMailboxes(
                    null,
                    out string sharedErrorCode,
                    out string sharedErrorMessage);
                if (sharedMailboxes == null)
                {
                    SetLastError(sharedErrorCode, $"Failed to read shared mailboxes: {sharedErrorMessage}");
                    return null;
                }

                foreach (var mailbox in sharedMailboxes)
                {
                    var displayName = mailbox.Properties["DisplayName"]?.Value?.ToString();
                    var alias = mailbox.Properties["Alias"]?.Value?.ToString();
                    var primarySmtpAddress = mailbox.Properties["PrimarySmtpAddress"]?.Value?.ToString();
                    var objectGuid = ExtractObjectGuid(mailbox.Properties);

                    // Filterung via Regex
                    if (!string.IsNullOrEmpty(this.DataAreaRegEx) &&
                        !Regex.Match(displayName, this.DataAreaRegEx).Success)
                        continue;

                    var exchangeMailbox = new cLiamExchangeSharedMailbox(this, displayName, primarySmtpAddress, alias, objectGuid);
                    await exchangeMailbox.ResolvePermissionGroupsAsync();
                    DataAreas.Add(exchangeMailbox);
                }

                // Distribution Groups
                var distributionGroups = exchangeManager.GetDistributionGroups(
                    null,
                    out string distErrorCode,
                    out string distErrorMessage);
                if (distributionGroups == null)
                {
                    SetLastError(distErrorCode, $"Failed to read distribution groups: {distErrorMessage}");
                    return null;
                }

                foreach (var group in distributionGroups)
                {
                    var displayName = group.Properties["DisplayName"]?.Value?.ToString();
                    var alias = group.Properties["Alias"]?.Value?.ToString();
                    var primarySmtpAddress = group.Properties["PrimarySmtpAddress"]?.Value?.ToString();
                    var objectGuid = ExtractObjectGuid(group.Properties);

                    if (!string.IsNullOrEmpty(this.DataAreaRegEx) &&
                        !Regex.Match(displayName, this.DataAreaRegEx).Success)
                        continue;

                    var exchangeGroup = new cLiamExchangeDistributionGroup(this, displayName, primarySmtpAddress, alias, objectGuid);
                    await exchangeGroup.ResolvePermissionGroupsAsync();
                    DataAreas.Add(exchangeGroup);
                }

                ClearLastError();
                return DataAreas;
            }
            catch (Exception E)
            {
                LogException(E);
                SetLastError("EXCH_GET_DATAAREAS_EXCEPTION", E.Message);
            }
            finally
            {
                LogMethodEnd(CM);
            }
            return null;
        }

        public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var dataType = cLIAMHelper.getUidItem(ref UID);
                if (string.IsNullOrEmpty(dataType))
                    return null;

                if (!int.TryParse(dataType, out int dataTypeInt))
                    return null;

                var primarySmtpAddress = cLIAMHelper.getUidItem(ref UID);
                if (string.IsNullOrEmpty(primarySmtpAddress))
                    return null;

                if (!isLoggedOn && !await LogonAsync())
                    return null;

                switch ((eLiamDataAreaTypes)dataTypeInt)
                {
                    case eLiamDataAreaTypes.ExchangeSharedMailbox:
                        return await cLiamExchangeSharedMailbox.Load(this, primarySmtpAddress);
                    case eLiamDataAreaTypes.ExchangeDistributionGroup:
                        return await cLiamExchangeDistributionGroup.Load(this, primarySmtpAddress);
                    default:
                        return null;
                }
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                ClearLastError();
                if (!cC4ITLicenseM42ESM.Instance.IsValid)
                {
                    SetLastError("EXCH_GET_SECURITYGROUPS_LICENSE_INVALID", "License not valid or Exchange module not licensed");
                    return new List<cLiamDataAreaBase>();
                }

                if (!isLoggedOn && !await LogonAsync())
                    return null;

                var securityGroups = new List<cLiamDataAreaBase>();
                var groups = exchangeManager.GetSecurityGroups(
                    groupFilter,
                    out string errorCode,
                    out string errorMessage);
                if (groups == null)
                {
                    SetLastError(errorCode, $"Failed to read security groups: {errorMessage}");
                    return null;
                }

                foreach (var group in groups)
                {
                    var displayName = group.Properties["DisplayName"]?.Value?.ToString();
                    var sid = group.Properties["Sid"]?.Value?.ToString();
                    var dn = group.Properties["DistinguishedName"]?.Value?.ToString();
                    var objectGuid = ExtractObjectGuid(group.Properties);

                    if (!string.IsNullOrEmpty(this.GroupRegEx) &&
                        !Regex.Match(displayName, this.GroupRegEx).Success)
                        continue;

                    var securityGroup = new cLiamExchangeSecurityGroup(this, displayName, sid, dn, objectGuid);
                    securityGroups.Add(securityGroup);
                }

                ClearLastError();
                return securityGroups;
            }
            catch (Exception E)
            {
                LogException(E);
                SetLastError("EXCH_GET_SECURITYGROUPS_EXCEPTION", E.Message);
            }
            finally
            {
                LogMethodEnd(CM);
            }
            return null;
        }

        // Hilfsmethoden zur Interaktion mit Exchange



        internal async Task<bool> AddMemberToGroup(string groupName, string member, bool isSharedMailbox)
        {
            try
            {
                if (isSharedMailbox)
                    exchangeManager.AddMailboxPermission(groupName, member);
                else
                    exchangeManager.AddMemberToDistributionGroup(groupName, member);
                return true;
            }
            catch (Exception ex)
            {
                LogException(ex);
                lastErrorMessage = $"Error adding member to group: {ex.Message}";
                return false;
            }
        }

        internal async Task<bool> RemoveMemberFromGroup(string groupName, string member, bool isSharedMailbox)
        {
            try
            {
                if (isSharedMailbox)
                    exchangeManager.RemoveMailboxPermission(groupName, member);
                else
                    exchangeManager.RemoveMemberFromDistributionGroup(groupName, member);
                return true;
            }
            catch (Exception ex)
            {
                LogException(ex);
                lastErrorMessage = $"Error removing member from group: {ex.Message}";
                return false;
            }
        }

        public async Task<cLiamExchangeSecurityGroup> GetManagedByGroup(string groupDn)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (activeDirectoryBase.adContext == null)
                {
                    LogEntry("Active Directory context not initialized", LogLevels.Error);
                    return null;
                }

                string ldapPath = $"LDAP://{groupDn}";
                DirectoryEntry groupEntry = null;
                try
                {
                    groupEntry = new DirectoryEntry(
                        ldapPath,
                        this.Credential?.Identification,
                        this.Credential?.Secret,
                        AuthenticationTypes.Secure | AuthenticationTypes.Sealing);

                    if (groupEntry.Properties.Contains("managedBy") && groupEntry.Properties["managedBy"].Value != null)
                    {
                        string managedByDn = groupEntry.Properties["managedBy"].Value.ToString();
                        string managedByLdapPath = $"LDAP://{managedByDn}";
                        using (DirectoryEntry managedByEntry = new DirectoryEntry(
                            managedByLdapPath,
                            this.Credential?.Identification,
                            this.Credential?.Secret,
                            AuthenticationTypes.Secure | AuthenticationTypes.Sealing))
                        {
                            if (managedByEntry.SchemaClassName == "group")
                            {
                                byte[] sidBytes = (byte[])managedByEntry.Properties["objectSid"].Value;
                                SecurityIdentifier sid = new SecurityIdentifier(sidBytes, 0);
                                string displayName = managedByEntry.Properties["displayName"]?.Value?.ToString()
                                                     ?? managedByEntry.Properties["name"]?.Value?.ToString();
                                return new cLiamExchangeSecurityGroup(this, displayName, sid.Value, managedByDn, sid.Value);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogException(ex);
                }
                finally
                {
                    groupEntry?.Dispose();
                }
                return null;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
    }

    public class cLiamExchangeSecurityGroup : cLiamDataAreaBase
    {
        public new readonly cLiamProviderExchange Provider = null;
        public readonly string sid = null;
        public readonly string dn = null;

        // objectGuid wird nun als TechnicalName genutzt
        public cLiamExchangeSecurityGroup(cLiamProviderExchange Provider, string displayName, string sid, string dn, string objectGuid) : base(Provider)
        {
            this.Provider = Provider;
            this.TechnicalName = displayName;
            this.DisplayName = displayName;
            this.UID = sid;
            this.sid = sid;
            this.dn = dn;
        }

        public override Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            return Task.FromResult(new List<cLiamDataAreaBase>());
        }
    }

    public class cLiamExchangeSharedMailbox : cLiamDataAreaBase
    {
        public new readonly cLiamProviderExchange Provider = null;
        public readonly string PrimarySmtpAddress = null;
        public readonly string Alias = null;
        public string OwnerGroupIdentifier = "S-1-0-0";
        public string FullAccessGroupSid = "S-1-0-0";
        public string SendAsGroupSid = "S-1-0-0";

        // objectGuid wird für TechnicalName genutzt
        public cLiamExchangeSharedMailbox(cLiamProviderExchange Provider, string displayName, string primarySmtpAddress, string alias, string objectGuid) : base(Provider)
        {
            this.Provider = Provider;
            this.TechnicalName = objectGuid;
            this.DisplayName = displayName;
            this.PrimarySmtpAddress = primarySmtpAddress;
            this.Alias = alias;
            this.UID = getUID(primarySmtpAddress);
            this.Level = 0;
            this.DataType = eLiamDataAreaTypes.ExchangeSharedMailbox;
            this.SupportsOwners = true;
            this.SupportsPermissions = true;
        }

        internal static string getUID(string primarySmtpAddress)
        {
            return $"{(int)eLiamDataAreaTypes.ExchangeSharedMailbox}|{primarySmtpAddress}";
        }

        public static async Task<cLiamExchangeSharedMailbox> Load(cLiamProviderExchange Provider, string primarySmtpAddress)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var mailbox = Provider.exchangeManager.GetSharedMailboxByAddress(primarySmtpAddress);
                if (mailbox == null)
                    return null;

                var displayName = mailbox.Properties["DisplayName"]?.Value?.ToString();
                var alias = mailbox.Properties["Alias"]?.Value?.ToString();
                var objectGuid = cLiamProviderExchange.ExtractObjectGuid(mailbox.Properties);

                var mailboxDataArea = new cLiamExchangeSharedMailbox(Provider, displayName, primarySmtpAddress, alias, objectGuid);
                await mailboxDataArea.ResolvePermissionGroupsAsync();
                return mailboxDataArea;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var members = Provider.exchangeManager.GetMailboxPermissionMembers(this.TechnicalName);
                if (members == null)
                    return new List<cLiamUserInfo>();

                var ownersList = new List<cLiamUserInfo>();
                foreach (var member in members)
                {
                    var userInfo = new cLiamUserInfo
                    {
                        DisplayName = member.Properties["DisplayName"]?.Value?.ToString(),
                        UserPrincipalName = member.Properties["UserPrincipalName"]?.Value?.ToString(),
                        EMail = member.Properties["PrimarySmtpAddress"]?.Value?.ToString(),
                        SID = member.Properties["Sid"]?.Value?.ToString()
                    };
                    ownersList.Add(userInfo);
                }
                return ownersList;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                bool success = false;
                switch (Role)
                {
                    case eLiamAccessRoles.Owner:
                        success = await Provider.AddMemberToGroup(this.DisplayName, User.UserPrincipalName, true);
                        break;
                    case eLiamAccessRoles.Write:
                        success = await Provider.exchangeManager.AddSendAsPermission(this.DisplayName, User.UserPrincipalName);
                        break;
                    default:
                        LogEntry($"Unsupported permission role for Exchange mailbox: {Role}", LogLevels.Warning);
                        return new cLiamPermissionResult { Valid = false };
                }
                return new cLiamPermissionResult
                {
                    Valid = success,
                    UserReference = User.UserPrincipalName
                };
            }
            catch (Exception E)
            {
                LogException(E);
                return new cLiamPermissionResult { Valid = false };
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                bool success = false;
                switch (Role)
                {
                    case eLiamAccessRoles.Owner:
                        success = await Provider.RemoveMemberFromGroup(this.DisplayName, User.UserPrincipalName, true);
                        break;
                    case eLiamAccessRoles.Write:
                        success = await Provider.exchangeManager.RemoveSendAsPermission(this.DisplayName, User.UserPrincipalName);
                        break;
                    default:
                        LogEntry($"Unsupported permission role for Exchange mailbox: {Role}", LogLevels.Warning);
                        return false;
                }
                return success;
            }
            catch (Exception E)
            {
                LogException(E);
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            return new List<cLiamDataAreaBase>();
        }

        public async Task ResolvePermissionGroupsAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var fullAccessNamingConvention = Provider.NamingConventions.FirstOrDefault(i =>
                    i.AccessRole == eLiamAccessRoles.ExchangeSMBFullAccess);

                var sendAsNamingConvention = Provider.NamingConventions.FirstOrDefault(i =>
                    i.AccessRole == eLiamAccessRoles.ExchangeSMBSendAs);

                if (fullAccessNamingConvention == null || sendAsNamingConvention == null)
                {
                    LogEntry("Naming conventions for Exchange mailbox permissions not found", LogLevels.Warning);
                    return;
                }

                try
                {
                    var fullAccessPermissions = Provider.exchangeManager.GetFullAccessPermissionGroups(this.TechnicalName);
                    if (fullAccessPermissions != null)
                    {
                        foreach (var permission in fullAccessPermissions)
                        {
                            string samAccountName = permission.Properties["SamAccountName"]?.Value?.ToString();
                            string sid = permission.Properties["Sid"]?.Value?.ToString();

                            if (!string.IsNullOrEmpty(samAccountName) && !string.IsNullOrEmpty(sid))
                            {
                                //if (Regex.IsMatch(samAccountName, fullAccessNamingConvention.Wildcard, RegexOptions.IgnoreCase))
                                {
                                    this.FullAccessGroupSid = sid;
                                    LogEntry($"Found FullAccess group {samAccountName} (SID: {sid}) for mailbox {this.DisplayName}", LogLevels.Debug);
                                    string dn = permission.Properties["DistinguishedName"]?.Value?.ToString();
                                    if (!string.IsNullOrEmpty(dn))
                                    {
                                        var managedByGroup = await Provider.GetManagedByGroup(dn);
                                        if (managedByGroup != null)
                                        {
                                            this.OwnerGroupIdentifier = managedByGroup.sid;
                                            LogEntry($"Found owner group {managedByGroup.TechnicalName} (SID: {managedByGroup.sid}) for mailbox {this.DisplayName}", LogLevels.Debug);
                                        }
                                    }
                                }
                            }
                        }
                    }

                    var sendAsPermissions = Provider.exchangeManager.GetSendAsPermissionGroups(this.TechnicalName);
                    if (sendAsPermissions != null)
                    {
                        foreach (var permission in sendAsPermissions)
                        {
                            string recipientType = permission.Properties["RecipientType"]?.Value?.ToString();
                            string samAccountName = permission.Properties["SamAccountName"]?.Value?.ToString();
                            string sid = permission.Properties["Sid"]?.Value?.ToString();

                            if (!string.IsNullOrEmpty(samAccountName) && !string.IsNullOrEmpty(sid))
                            {
                                //if (Regex.IsMatch(samAccountName, sendAsNamingConvention.Wildcard, RegexOptions.IgnoreCase))
                                {
                                    this.SendAsGroupSid = sid;
                                    LogEntry($"Found SendAs group {samAccountName} (SID: {sid}) for mailbox {this.DisplayName}", LogLevels.Debug);
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogException(ex);
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
    }

    public class cLiamExchangeDistributionGroup : cLiamDataAreaBase
    {
        public new readonly cLiamProviderExchange Provider = null;
        public readonly string PrimarySmtpAddress = null;
        public readonly string Alias = null;
        public string OwnerGroupIdentifier = "S-1-0-0";
        public string MemberGroupSid = "S-1-0-0";

        // objectGuid wird als TechnicalName gesetzt
        public cLiamExchangeDistributionGroup(cLiamProviderExchange Provider, string displayName, string primarySmtpAddress, string alias, string objectGuid) : base(Provider)
        {
            this.Provider = Provider;
            this.TechnicalName = objectGuid;
            this.DisplayName = displayName;
            this.PrimarySmtpAddress = primarySmtpAddress;
            this.Alias = alias;
            this.UID = getUID(primarySmtpAddress);
            this.Level = 0;
            this.DataType = eLiamDataAreaTypes.ExchangeDistributionGroup;
            this.SupportsOwners = true;
            this.SupportsPermissions = true;
        }

        internal static string getUID(string primarySmtpAddress)
        {
            return $"{(int)eLiamDataAreaTypes.ExchangeDistributionGroup}|{primarySmtpAddress}";
        }

        public static async Task<cLiamExchangeDistributionGroup> Load(cLiamProviderExchange Provider, string primarySmtpAddress)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var group = Provider.exchangeManager.GetDistributionGroupByAddress(primarySmtpAddress);
                if (group == null)
                    return null;

                var displayName = group.Properties["DisplayName"]?.Value?.ToString();
                var alias = group.Properties["Alias"]?.Value?.ToString();
                var objectGuid = cLiamProviderExchange.ExtractObjectGuid(group.Properties);

                var groupDataArea = new cLiamExchangeDistributionGroup(Provider, displayName, primarySmtpAddress, alias, objectGuid);
                await groupDataArea.ResolvePermissionGroupsAsync();
                return groupDataArea;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var members = Provider.exchangeManager.GetDistributionGroupMembers(this.TechnicalName);
                if (members == null)
                    return new List<cLiamUserInfo>();

                var membersList = new List<cLiamUserInfo>();
                foreach (var member in members)
                {
                    var userInfo = new cLiamUserInfo
                    {
                        DisplayName = member.Properties["DisplayName"]?.Value?.ToString(),
                        UserPrincipalName = member.Properties["UserPrincipalName"]?.Value?.ToString(),
                        EMail = member.Properties["PrimarySmtpAddress"]?.Value?.ToString(),
                        SID = member.Properties["Sid"]?.Value?.ToString()
                    };
                    membersList.Add(userInfo);
                }
                return membersList;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (Role != eLiamAccessRoles.Owner)
                {
                    LogEntry($"Only Owner role is supported for distribution groups, requested: {Role}", LogLevels.Warning);
                    return new cLiamPermissionResult { Valid = false };
                }

                bool success = await Provider.AddMemberToGroup(this.DisplayName, User.UserPrincipalName, false);
                return new cLiamPermissionResult
                {
                    Valid = success,
                    UserReference = User.UserPrincipalName
                };
            }
            catch (Exception E)
            {
                LogException(E);
                return new cLiamPermissionResult { Valid = false };
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (Role != eLiamAccessRoles.Owner)
                {
                    LogEntry($"Only Owner role is supported for distribution groups, requested: {Role}", LogLevels.Warning);
                    return false;
                }

                return await Provider.RemoveMemberFromGroup(this.DisplayName, User.UserPrincipalName, false);
            }
            catch (Exception E)
            {
                LogException(E);
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            return new List<cLiamDataAreaBase>();
        }

        public async Task ResolvePermissionGroupsAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var namingConvention = Provider.NamingConventions
       .FirstOrDefault(nc => nc.AccessRole == eLiamAccessRoles.ExchangeMLMember);
                if (namingConvention == null)
                {
                    LogEntry("Naming convention for DL-Member not found", LogLevels.Warning);
                    return;
                }

                try
                {
                    // ruft alle Gruppen auf dem DL ab
                    var memberGroups = Provider.exchangeManager
                        .GetDistributionGroupMembers(this.TechnicalName);

                    foreach (var group in memberGroups)
                    {
                        var sam = group.Properties["SamAccountName"]?.Value?.ToString();
                        var sid = group.Properties["Sid"]?.Value?.ToString();
                        if (string.IsNullOrEmpty(sam) || string.IsNullOrEmpty(sid))
                            continue;

                        // falls gewünscht: Filter nach Namenskonvention
                        // if (!Regex.IsMatch(sam, namingConvention.Wildcard, RegexOptions.IgnoreCase))
                        //     continue;

                        // hier beispielsweise in eine List<string> MemberGroupSids aufnehmen
                        this.MemberGroupSid = sid;
                        LogEntry($"Found DL-member group {sam} (SID: {sid}) for distribution list {this.DisplayName}", LogLevels.Debug);

                        // optional: falls die Gruppe ein ManagedBy hat
                        var dn = group.Properties["DistinguishedName"]?.Value?.ToString();
                        if (!string.IsNullOrEmpty(dn))
                        {
                            var mgr = await Provider.GetManagedByGroup(dn);
                            if (mgr != null)
                            {
                                this.OwnerGroupIdentifier = mgr.sid;
                                LogEntry($"Found owner group {mgr.TechnicalName} (SID: {mgr.sid}) for distribution list {this.DisplayName}", LogLevels.Debug);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogException(ex);
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
    }
}
