using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Linq;
using System.Reflection;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using System.Threading.Tasks;
using System.Collections;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.ActiveDirectory;
using System.DirectoryServices;
using System.ComponentModel;
using System.Security.AccessControl;
using System.Threading;

namespace C4IT.LIAM
{
    /// <summary>
    /// Manages Exchange operations using PowerShell remoting
    /// </summary>
    public partial class ExchangeManager
    {
        private static readonly TimeSpan RunspaceOpenTimeout = TimeSpan.FromSeconds(30);

        private readonly cLiamProviderBase _provider;
        private readonly string _exchangeUri;
        private readonly PSCredential _credential;
        private readonly string _organizationalUnit;
        private readonly string _domain;

        /// <summary>
        /// Constructor with explicit credentials, Exchange URI and OU path
        /// </summary>
        /// <param name="exchangeUri">URL of the Exchange PowerShell endpoint (e.g. "http://exchangeserver/PowerShell")</param>
        /// <param name="credential">Explicit credentials</param>
        /// <param name="organizationalUnit">OU path where objects should be created</param>
        public ExchangeManager(cLiamProviderBase provider, string exchangeUri, PSCredential credential, string domain, string organizationalUnit)
        {
            _provider = provider ?? throw new ArgumentNullException(nameof(provider));
            _exchangeUri = exchangeUri;
            _credential = credential;
            _domain = domain;
            _organizationalUnit = organizationalUnit;
        }

        /// <summary>
        /// Creates a runspace connected to the Exchange PowerShell endpoint
        /// </summary>
        private Runspace CreateRunspace()
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                var connectionInfo = new WSManConnectionInfo(
                    new Uri(_exchangeUri),
                    "http://schemas.microsoft.com/powershell/Microsoft.Exchange",
                    _credential);

                connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
                var runspace = RunspaceFactory.CreateRunspace(connectionInfo);
                LogEntry($"Opening Exchange runspace (timeout: {RunspaceOpenTimeout.TotalSeconds:0}s) for endpoint '{_exchangeUri}'", LogLevels.Debug);

                var openTask = Task.Run(() => runspace.Open());
                if (!openTask.Wait(RunspaceOpenTimeout))
                {
                    try
                    {
                        runspace.Dispose();
                    }
                    catch (Exception disposeEx)
                    {
                        LogException(disposeEx);
                    }

                    throw new TimeoutException(
                        $"Timeout while opening Exchange runspace after {RunspaceOpenTimeout.TotalSeconds:0} seconds.");
                }

                openTask.GetAwaiter().GetResult();

                LogEntry("Exchange runspace opened successfully", LogLevels.Debug);
                return runspace;
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        // Internal helper for creation details
        private class CreationResult
        {
            public Guid ObjectGuid { get; set; }
            public List<Tuple<string, string, string, string>> Groups { get; private set; }

            public CreationResult()
            {
                Groups = new List<Tuple<string, string, string, string>>();
            }
        }

        private DirectoryEntry FindAdObject(string ldapFilter)
        {
            using (DirectoryEntry root = new DirectoryEntry("LDAP://" + _organizationalUnit,
                                                           _credential.UserName,
                                                           SecureStringToString(_credential.Password),
                                                           AuthenticationTypes.Secure))
            {
                using (DirectorySearcher ds = new DirectorySearcher(root))
                {
                    ds.Filter = ldapFilter;
                    ds.PropertiesToLoad.Add("objectGUID");
                    ds.PropertiesToLoad.Add("objectSid");
                    ds.PropertiesToLoad.Add("cn");
                    ds.PropertiesToLoad.Add("sAMAccountName");
                    SearchResult searchResult = ds.FindOne();
                    if (searchResult != null)
                        return searchResult.GetDirectoryEntry();
                    return null;
                }
            }
        }


        /// <summary>
        /// Creates a distribution group (mailing list). Optionally members can be added initially.
        /// </summary>
        public void CreateDistributionGroup(string name, string alias, List<string> initialMembers = null)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("New-DistributionGroup")
                      .AddParameter("Name", name)
                      .AddParameter("Alias", alias)
                      .AddParameter("OrganizationalUnit", _organizationalUnit);
                    ps.Invoke();

                    if (initialMembers != null)
                    {
                        foreach (var member in initialMembers)
                        {
                            ps.Commands.Clear();
                            ps.AddCommand("Add-DistributionGroupMember")
                              .AddParameter("Identity", name)
                              .AddParameter("Member", member);
                            ps.Invoke();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Creates a security group in Active Directory that can be used for Exchange permissions
        /// – direkt per LDAP und mit den im Konstruktor übergebenen Credentials & Domain.
        /// </summary>
        public bool CreateSecurityGroup(eLiamAccessRoles accessRole, string baseName, string baseDescription="")
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                // Find the naming convention for this role
                var namingConvention = _provider.NamingConventions
                    .FirstOrDefault(nc => nc.AccessRole == accessRole);
                if (namingConvention == null)
                    throw new InvalidOperationException($"No naming convention found for role '{accessRole}'");

                // Generate group name and description from templates
                string groupName = namingConvention.NamingTemplate
                    .Replace("{Name}", baseName);
                string groupDescription = namingConvention.DescriptionTemplate
                    .Replace("{Name}", baseDescription);

                // Determine scope bit for groupType
                int scopeBit;
                switch (namingConvention.Scope)
                {
                    case eLiamAccessRoleScopes.Global:
                        scopeBit = 0x2;
                        break;
                    case eLiamAccessRoleScopes.DomainLocal:
                        scopeBit = 0x4;
                        break;
                    case eLiamAccessRoleScopes.Universal:
                        scopeBit = 0x8;
                        break;
                    default:
                        scopeBit = 0x8;
                        break;
                }
                int groupType = unchecked((int)(0x80000000 | scopeBit));

                // Create the group in AD
                string ldapPath = $"LDAP://{_organizationalUnit}";
                string password = new System.Net.NetworkCredential(string.Empty, _credential.Password).Password;
                using (var root = new DirectoryEntry(
                           ldapPath,
                           _credential.UserName,
                           password,
                           AuthenticationTypes.Secure))
                {
                    var newGroup = root.Children.Add($"CN={groupName}", "group");
                    newGroup.Properties["sAMAccountName"].Value = groupName;
                    newGroup.Properties["displayName"].Value = groupName;
                    newGroup.Properties["groupType"].Value = groupType;
                    newGroup.Properties["description"].Value = groupDescription;
                    newGroup.CommitChanges();
                }

                return true;
            }
            catch (Exception ex)
            {
                LogException(ex);
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        /// <summary>
        /// Gets all shared mailboxes matching an optional filter
        /// </summary>
        public IEnumerable<PSObject> GetSharedMailboxes(string filter = null)
        {
            var result = GetSharedMailboxes(filter, out _, out _);
            return result ?? Enumerable.Empty<PSObject>();
        }

        /// <summary>
        /// Gets all shared mailboxes matching an optional filter.
        /// Returns null on failure and exposes an error code/message.
        /// </summary>
        public IEnumerable<PSObject> GetSharedMailboxes(string filter, out string errorCode, out string errorMessage)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            errorCode = "OK";
            errorMessage = string.Empty;
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-Mailbox")
                      .AddParameter("RecipientTypeDetails", "SharedMailbox");

                    if (!string.IsNullOrEmpty(filter))
                    {
                        ps.AddParameter("Filter", filter);
                    }
                    return InvokePowerShellWithTimeout(ps, PowerShellInvokeTimeout, $"Get-Mailbox SharedMailbox filter='{filter ?? string.Empty}'");
                }
            }
            catch (TimeoutException ex)
            {
                LogException(ex);
                errorCode = "EXCH_GET_SHAREDMAILBOXES_TIMEOUT";
                errorMessage = ex.Message;
                return null;
            }
            catch (Exception ex)
            {
                LogException(ex);
                errorCode = "EXCH_GET_SHAREDMAILBOXES_FAILED";
                errorMessage = ex.Message;
                return null;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Gets a shared mailbox by primary SMTP address
        /// </summary>
        public PSObject GetSharedMailboxByAddress(string primarySmtpAddress)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-Mailbox")
                      .AddParameter("RecipientTypeDetails", "SharedMailbox")
                      .AddParameter("PrimarySmtpAddress", primarySmtpAddress);

                    var results = InvokePowerShellWithTimeout(ps, PowerShellInvokeTimeout, $"Get-Mailbox SharedMailbox address='{primarySmtpAddress}'");
                    return results.Count > 0 ? results[0] : null;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return null;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Gets all distribution groups matching an optional filter
        /// </summary>
        public IEnumerable<PSObject> GetDistributionGroups(string filter = null)
        {
            var result = GetDistributionGroups(filter, out _, out _);
            return result ?? Enumerable.Empty<PSObject>();
        }

        /// <summary>
        /// Gets all distribution groups matching an optional filter.
        /// Returns null on failure and exposes an error code/message.
        /// </summary>
        public IEnumerable<PSObject> GetDistributionGroups(string filter, out string errorCode, out string errorMessage)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            errorCode = "OK";
            errorMessage = string.Empty;
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-DistributionGroup");

                    if (!string.IsNullOrEmpty(filter))
                    {
                        ps.AddParameter("Filter", filter);
                    }
                    return InvokePowerShellWithTimeout(ps, PowerShellInvokeTimeout, $"Get-DistributionGroup filter='{filter ?? string.Empty}'");
                }
            }
            catch (TimeoutException ex)
            {
                LogException(ex);
                errorCode = "EXCH_GET_DISTRIBUTIONGROUPS_TIMEOUT";
                errorMessage = ex.Message;
                return null;
            }
            catch (Exception ex)
            {
                LogException(ex);
                errorCode = "EXCH_GET_DISTRIBUTIONGROUPS_FAILED";
                errorMessage = ex.Message;
                return null;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Gets a distribution group by primary SMTP address.
        /// Die Methode bleibt unverändert.
        /// </summary>
        public PSObject GetDistributionGroupByAddress(string primarySmtpAddress)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-DistributionGroup")
                      .AddParameter("PrimarySmtpAddress", primarySmtpAddress);

                    var results = InvokePowerShellWithTimeout(ps, PowerShellInvokeTimeout, $"Get-DistributionGroup address='{primarySmtpAddress}'");
                    return results.Count > 0 ? results[0] : null;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return null;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Gets members of a distribution group
        /// </summary>
        public IEnumerable<PSObject> GetDistributionGroupMembers(string distributionListIdentity)
        {
            using (var runspace = CreateRunspace())
            using (var ps = PowerShell.Create())
            {
                ps.Runspace = runspace;
                // alle Members des DL holen
                ps.AddCommand("Get-DistributionGroupMember")
                  .AddParameter("Identity", distributionListIdentity)
                  .AddParameter("ResultSize", "Unlimited")
                  .AddParameter("ErrorAction", "Stop");
                var members = InvokePowerShellWithTimeout(ps, PowerShellInvokeTimeout, $"Get-DistributionGroupMember '{distributionListIdentity}'");
                if (ps.HadErrors || members.Count == 0)
                    return Enumerable.Empty<PSObject>();

                var result = new List<PSObject>();
                foreach (var member in members)
                {
                    // nur Gruppen (Security- oder Distributions-Gruppen)
                    var rt = member.Properties["RecipientTypeDetails"]?.Value?.ToString() ?? "";
                    if (rt.IndexOf("Group", StringComparison.OrdinalIgnoreCase) <= 0)
                        continue;

                    // SamAccountName auslesen
                    var name = member.Properties["Name"]?.Value?.ToString();
                    if (string.IsNullOrEmpty(name))
                        continue;

                    // AD-Gruppe per .NET auflösen
                    using (var ctx = new PrincipalContext(ContextType.Domain))
                    using (var grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, name))
                    {
                        if (grp == null)
                            continue;

                        // PSObject bauen
                        var obj = new PSObject();
                        obj.Properties.Add(new PSNoteProperty("SamAccountName", grp.SamAccountName));
                        obj.Properties.Add(new PSNoteProperty("Name", grp.Name));
                        obj.Properties.Add(new PSNoteProperty("Sid", grp.Sid.Value));
                        obj.Properties.Add(new PSNoteProperty("DistinguishedName", grp.DistinguishedName));
                        obj.Properties.Add(new PSNoteProperty("GroupPrincipal", grp));
                        result.Add(obj);
                    }
                }
                return result;
            }
        }


        public IEnumerable<PSObject> GetSendAsPermissionMembers(string mailboxIdentity)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;

                    // 1) Alle AD-Permissions holen
                    ps.AddCommand("Get-ADPermission")
                      .AddParameter("Identity", mailboxIdentity);
                    var perms = ps.Invoke();
                    if (ps.HadErrors) return Enumerable.Empty<PSObject>();

                    var result = new List<PSObject>();
                    using (var ps2 = PowerShell.Create())
                    {
                        ps2.Runspace = runspace;

                        foreach (var perm in perms)
                        {
                            // 2) Nur explizite (nicht geerbte) Rechte
                            var inherited = perm.Properties["IsInherited"]?.Value as bool? ?? true;
                            if (inherited) continue;

                            // 3) ExtendedRights robust auslesen
                            var rawVal = perm.Properties["ExtendedRights"]?.Value;
                            IEnumerable<object> rights = null;

                            if (rawVal is IEnumerable seq && !(rawVal is string))
                                rights = seq.Cast<object>();
                            else if (rawVal is PSObject pso && pso.BaseObject is IEnumerable baseSeq)
                                rights = baseSeq.Cast<object>();
                            else if (rawVal != null)
                                rights = new[] { rawVal };

                            // wenn gar keine Rechte da sind, weiter
                            if (rights == null)
                                continue;

                            // die Liste der Recht‑Strings extrahieren
                            var rightStrings = rights
                                .Select(r => (r as PSObject)?.Properties["RawIdentity"]?.Value?.ToString()
                                             ?? r.ToString())
                                .ToList();

                            // UND jetzt prüfen, ob *genau* "Send-As" (normaler Bindestrich) enthalten ist
                            if (!rightStrings
                                    .Any(rs => string.Equals(rs.Trim(),
                                                             "Send-As",
                                                             StringComparison.OrdinalIgnoreCase)))
                            {
                                continue; // kein Send‑As, nächste Permission
                            }

                            // 4) System‑Accounts rausfiltern
                            var userName = perm.Properties["User"]?.Value?.ToString() ?? "";
                            if (userName.StartsWith("NT AUTHORITY", StringComparison.OrdinalIgnoreCase)
                                || userName.IndexOf("SELF", StringComparison.OrdinalIgnoreCase) >= 0
                                || userName.IndexOf("Administrator", StringComparison.OrdinalIgnoreCase) >= 0)
                                continue;

                            // 5) Get-User auflösen
                            ps2.Commands.Clear();
                            ps2.AddCommand("Get-User")
                               .AddParameter("Identity", userName);
                            var users = ps2.Invoke();
                            if (users.Count > 0)
                                result.Add(users[0]);
                        }
                    }

                    return result;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return Enumerable.Empty<PSObject>();
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Liefert pro Send‑As‑Gruppe ein PSObject mit Name, SID und dem GroupPrincipal selbst.
        /// </summary>
        public IEnumerable<PSObject> GetSendAsPermissionGroups(string mailboxIdentity)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-ADPermission")
                      .AddParameter("Identity", mailboxIdentity)
                      .AddParameter("ErrorAction", "Stop");
                    var perms = InvokePowerShellWithTimeout(ps, PowerShellInvokeTimeout, $"Get-ADPermission (SendAsGroups) '{mailboxIdentity}'");
                    if (ps.HadErrors || perms.Count == 0)
                        return Enumerable.Empty<PSObject>();

                    var result = new List<PSObject>();

                    foreach (var perm in perms)
                    {
                        // 1) Nur explizite Rechte
                        var inherited = perm.Properties["IsInherited"]?.Value as bool? ?? true;
                        if (inherited) continue;

                        // 2) ExtendedRights auf "Send‑As" prüfen (nur via ToString)
                        var rawVal = perm.Properties["ExtendedRights"]?.Value;
                        IEnumerable<object> rights;
                        if (rawVal is System.Collections.IEnumerable seq && !(rawVal is string))
                            rights = seq.Cast<object>();
                        else if (rawVal is PSObject pso && pso.BaseObject is System.Collections.IEnumerable baseSeq)
                            rights = baseSeq.Cast<object>();
                        else if (rawVal != null)
                            rights = new[] { rawVal };
                        else
                            continue;

                        // ganz einfach: jede Rechte-Instanz zu String machen
                        var rightStrings = rights.Select(r => r.ToString().Trim());

                        if (!rightStrings.Any(s =>
                                string.Equals(s, "Send-As", StringComparison.OrdinalIgnoreCase)))
                        {
                            continue;
                        }

                        // 3) Nur Gruppen‑Principals (DOMAIN\Name) und keine System‑Accounts
                        var userName = perm.Properties["User"]?.Value?.ToString() ?? "";
                        var low = userName.ToLowerInvariant();
                        if (!userName.Contains("\\") ||
                            low.StartsWith("nt authority") ||
                            low.Contains("self") ||
                            low.Contains("administrator"))
                            continue;

                        // 4) Gruppe im AD auflösen
                        var sam = userName.Split('\\').Last();
                        using (var ctx = new PrincipalContext(ContextType.Domain))
                        using (var grp = GroupPrincipal.FindByIdentity(ctx, sam))
                        {
                            if (grp == null)
                                continue;

                            // 5) PSObject mit allen gewünschten Properties anlegen
                            var obj = new PSObject();
                            obj.Properties.Add(new PSNoteProperty("SamAccountName", grp.SamAccountName));
                            obj.Properties.Add(new PSNoteProperty("Name", grp.Name));
                            obj.Properties.Add(new PSNoteProperty("Sid", grp.Sid.Value));
                            obj.Properties.Add(new PSNoteProperty("DistinguishedName", grp.DistinguishedName));
                            obj.Properties.Add(new PSNoteProperty("GroupPrincipal", grp));

                            result.Add(obj);
                        }
                    }

                    return result;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return Enumerable.Empty<PSObject>();
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }




        /// <summary>
        /// Liefert pro Gruppe, die FullAccess auf das angegebene Postfach hat,
        /// ein PSObject mit Name, SID, DistinguishedName und dem GroupPrincipal.
        /// </summary>
        public IEnumerable<PSObject> GetFullAccessPermissionGroups(string mailboxIdentity)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;

                    // 1) Alle FullAccess-Berechtigungen für das Postfach holen
                    ps.AddCommand("Get-MailboxPermission")
                      .AddParameter("Identity", mailboxIdentity)
                      .AddParameter("ErrorAction", "Stop");
                    var perms = InvokePowerShellWithTimeout(ps, PowerShellInvokeTimeout, $"Get-MailboxPermission (FullAccessGroups) '{mailboxIdentity}'");
                    if (ps.HadErrors || perms.Count == 0)
                        return Enumerable.Empty<PSObject>();

                    var result = new List<PSObject>();

                    foreach (var perm in perms)
                    {
                        // 2) Eingebettete Properties ziehen
                        //    - IsInherited
                        //    - AccessRights (MultiValuedProperty)
                        var inherited = perm.Properties["IsInherited"]?.Value as bool? ?? true;
                        if (inherited)
                            continue;

                        var rawVal = perm.Properties["AccessRights"]?.Value;
                        IEnumerable<object> rights = null;

                        if (rawVal is System.Collections.IEnumerable seq && !(rawVal is string))
                            rights = seq.Cast<object>();
                        else if (rawVal is PSObject pso && pso.BaseObject is System.Collections.IEnumerable baseSeq)
                            rights = baseSeq.Cast<object>();
                        else if (rawVal != null)
                            rights = new[] { rawVal };

                        // 3) Nur FullAccess durchlassen
                        if (rights == null ||
                            !rights.Any(r =>
                            {
                                var s = (r as PSObject)?.BaseObject?.ToString() ?? r.ToString();
                                return string.Equals(s, "FullAccess", StringComparison.OrdinalIgnoreCase);
                            }))
                        {
                            continue;
                        }

                        // 4) Nur Security-Principals im DOMAIN\Name-Format und keine System‑Accounts
                        var userName = perm.Properties["User"]?.Value?.ToString() ?? "";
                        var low = userName.ToLowerInvariant();
                        if (!userName.Contains("\\")
                            || low.StartsWith("nt authority")
                            || low.Contains("self")
                            || low.Contains("administrator"))
                        {
                            continue;
                        }

                        // 5) Gruppe im AD auflösen und PSObject bauen
                        var sam = userName.Split('\\').Last();
                        using (var ctx = new PrincipalContext(ContextType.Domain))
                        using (var grp = GroupPrincipal.FindByIdentity(ctx, sam))
                        {
                            if (grp == null)
                                continue;

                            var obj = new PSObject();
                            obj.Properties.Add(new PSNoteProperty("SamAccountName", grp.SamAccountName));
                            obj.Properties.Add(new PSNoteProperty("Name", grp.Name));
                            obj.Properties.Add(new PSNoteProperty("Sid", grp.Sid.Value));
                            obj.Properties.Add(new PSNoteProperty("DistinguishedName", grp.DistinguishedName));
                            obj.Properties.Add(new PSNoteProperty("GroupPrincipal", grp));

                            result.Add(obj);
                        }
                    }

                    return result;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return Enumerable.Empty<PSObject>();
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }



        /// <summary>
        /// Gets users who have permissions on a mailbox
        /// </summary>
        public IEnumerable<PSObject> GetMailboxPermissionMembers(string id)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-MailboxPermission")
                      .AddParameter("Identity", id)
                      .AddParameter("AccessRights", "FullAccess");

                    var results = ps.Invoke().Where(p =>
                        !p.Properties["User"].Value.ToString().Contains("NT AUTHORITY") &&
                        !p.Properties["User"].Value.ToString().Contains("Administrator"));

                    var userList = new List<PSObject>();
                    using (var ps2 = PowerShell.Create())
                    {
                        ps2.Runspace = runspace;
                        foreach (var perm in results)
                        {
                            ps2.Commands.Clear();
                            ps2.AddCommand("Get-User")
                              .AddParameter("Identity", perm.Properties["User"].Value.ToString());
                            var users = ps2.Invoke();
                            if (users.Count > 0)
                                userList.Add(users[0]);
                        }
                    }
                    return userList;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return Enumerable.Empty<PSObject>();
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Sucht im angegebenen AD‑Domain/OU nach Security‑Gruppen, deren Name zum Wildcard passt.
        /// Nutzt hierfür die übergebenen Anmeldeinformationen.
        /// </summary>
        public IEnumerable<PSObject> GetSecurityGroups(string nameWildcard)
        {
            var result = GetSecurityGroups(nameWildcard, out _, out _);
            return result ?? new List<PSObject>();
        }

        /// <summary>
        /// Sucht im angegebenen AD‑Domain/OU nach Security‑Gruppen, deren Name zum Wildcard passt.
        /// Returns null on failure and exposes an error code/message.
        /// </summary>
        public IEnumerable<PSObject> GetSecurityGroups(string nameWildcard, out string errorCode, out string errorMessage)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            errorCode = "OK";
            errorMessage = string.Empty;
            try
            {
                // Credentials in NetworkCredential konvertieren
                var netCred = _credential.GetNetworkCredential();

                // PrincipalContext auf Domain oder OU
                using (var ctx = new PrincipalContext(
                    ContextType.Domain,
                    _domain,       // ← real domain or DC hostname
                    _organizationalUnit,      // ← the OU‑DN to scope your search
                    netCred.UserName,
                    netCred.Password))
                {
                    {
                        // QBE‑Gruppe für Wildcard‑Suche
                        using (var qbe = new GroupPrincipal(ctx) { Name = nameWildcard })
                        using (var searcher = new PrincipalSearcher(qbe))
                        {
                            var result = new List<PSObject>();
                            var findTask = Task.Run(() => searcher.FindAll().OfType<GroupPrincipal>().ToList());
                            if (!findTask.Wait(PowerShellInvokeTimeout))
                            {
                                throw new TimeoutException(
                                    $"Directory search operation 'GetSecurityGroups' timed out after {PowerShellInvokeTimeout.TotalSeconds:0} seconds.");
                            }

                            foreach (var principal in findTask.Result)
                            {
                                // DirectoryEntry benötigt man, um DistinguishedName auszulesen
                                var de = principal.GetUnderlyingObject() as DirectoryEntry;

                                var obj = new PSObject();
                                obj.Properties.Add(new PSNoteProperty("SamAccountName",
                                    principal.SamAccountName));
                                obj.Properties.Add(new PSNoteProperty("DisplayName",
                                    principal.Name));
                                obj.Properties.Add(new PSNoteProperty("Sid",
                                    principal.Sid?.Value ?? string.Empty));
                                obj.Properties.Add(new PSNoteProperty("DistinguishedName",
                                    de?.Properties["distinguishedName"]?.Value?.ToString()
                                    ?? string.Empty));

                                result.Add(obj);
                            }

                            return result;
                        }
                    }
                }
            }
            catch (TimeoutException ex)
            {
                LogException(ex);
                errorCode = "EXCH_GET_SECURITYGROUPS_TIMEOUT";
                errorMessage = ex.Message;
                return null;
            }
            catch (Exception ex)
            {
                LogException(ex);
                errorCode = "EXCH_GET_SECURITYGROUPS_FAILED";
                errorMessage = ex.Message;
                return null;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }
    

        /// <summary>
        /// Adds a member to a distribution group
        /// </summary>
        public void AddMemberToDistributionGroup(string groupName, string member)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Add-DistributionGroupMember")
                      .AddParameter("Identity", groupName)
                      .AddParameter("Member", member);
                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Removes a member from a distribution group
        /// </summary>
        public void RemoveMemberFromDistributionGroup(string groupName, string member)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Remove-DistributionGroupMember")
                      .AddParameter("Identity", groupName)
                      .AddParameter("Member", member)
                      .AddParameter("Confirm", false);
                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Adds mailbox permission for a user
        /// </summary>
        public void AddMailboxPermission(string mailboxName, string user, string accessRight = "FullAccess")
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Add-MailboxPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("AccessRights", accessRight)
                      .AddParameter("InheritanceType", "All");
                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Removes mailbox permission for a user
        /// </summary>
        public void RemoveMailboxPermission(string mailboxName, string user, string accessRight = "FullAccess")
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Remove-MailboxPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("AccessRights", accessRight)
                      .AddParameter("InheritanceType", "All")
                      .AddParameter("Confirm", false);
                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Adds Send As permission for a mailbox
        /// </summary>
        public async Task<bool> AddSendAsPermission(string mailboxName, string user)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Add-ADPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("ExtendedRights", "Send-As")
                      .AddParameter("AccessRights", "ExtendedRight");

                    var results = ps.Invoke();
                    return results.Count > 0;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return false;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Removes Send As permission for a mailbox
        /// </summary>
        public async Task<bool> RemoveSendAsPermission(string mailboxName, string user)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Remove-ADPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("ExtendedRights", "Send-As")
                      .AddParameter("AccessRights", "ExtendedRight")
                      .AddParameter("Confirm", false);

                    var results = ps.Invoke();
                    return results.Count > 0;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return false;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Creates a shared mailbox. Optionally initial permissions (e.g. FullAccess) can be set for members.
        /// </summary>
        public void CreateSharedMailbox(string name, string alias, List<string> initialMembers = null)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("New-Mailbox")
                      .AddParameter("Name", name)
                      .AddParameter("Alias", alias)
                      .AddParameter("Shared", true)
                      .AddParameter("OrganizationalUnit", _organizationalUnit);
                    ps.Invoke();

                    if (initialMembers != null)
                    {
                        foreach (var member in initialMembers)
                        {
                            ps.Commands.Clear();
                            ps.AddCommand("Add-MailboxPermission")
                              .AddParameter("Identity", name)
                              .AddParameter("User", member)
                              .AddParameter("AccessRights", "FullAccess")
                              .AddParameter("InheritanceType", "All");
                            ps.Invoke();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }
    }
}
