using C4IT_IAM;
using C4IT_IAM_Engine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text.RegularExpressions;
using C4IT.Logging;

using static C4IT.Logging.cLogManager;
using System.Reflection;
using C4IT_IAM_GET;


namespace C4IT_IAM_SET
{
    public class DataArea_FileSystem
    {
        public const string constApplicationDataPath = "%ProgramData%\\Consulting4IT GmbH\\LIAM";

        public string domainName;
        public string effectiveDomainController;
        public string username;
        public SecureString password;
        private cNetworkConnection Connection;

        public string groupPrefix;
        public string groupOUPath;
        public string baseFolder;
        public string newFolderPath;
        public string newFolder;
        public string newFolderParent;

        public PermissionGroupStrategy groupPermissionStrategy;
        public string groupDescriptionTemplate;
        public string groupNamingTemplate;
        public string groupDLTag;
        public string groupGTag;
        public string groupOwnerTag;
        public string groupReadTag;
        public string groupWriteTag;
        public string groupTraverseTag;
        public string groupWildcard;
        public IDictionary<string, string> groupCustomTags;
        public ICollection<string> ownerUserSids;
        public ICollection<string> readerUserSids;
        public ICollection<string> writerUserSids;
        public Func<string, bool> CanManagePermissionsForPath;
        public Func<string, bool> CanManageTraversePermissionsForPath;
        public string traverseBoundaryPath;
        public bool forceStrictAdGroupNames;
        public string groupNameSanitizeReplacement = Helper.DefaultGroupNameSanitizeReplacement;
        public bool preserveAdGroupNameCase;
        public bool WhatIf;

        public int ReadACLPermission = 0x200A9;
        public int WriteACLPermission = 0x301BF;
        public int OwnerACLPermission = 0x1F01FF;


        public string ConfigID;
        Dictionary<FileSystemRights, string> adGroupDic;
        public DataArea newDataArea;
        public SecurityGroups newSecurityGroups;

        public List<IAM_SecurityGroupTemplate> templates;

        public int createTraverseGroupLvl = 0;

        public DataArea_FileSystem()
        {
            var logDirectory = Environment.ExpandEnvironmentVariables(constApplicationDataPath);
            Helper.CreatePathWithWriteAccess(logDirectory);
            var LogPath = Path.Combine(logDirectory, "Logs");
            cLogManagerFile.CreateInstance(Path.Combine(LogPath, "LIAM.log"));

            DefaultLogger.LogEntry(LogLevels.Info, "=================================================");
            DefaultLogger.LogEntry(LogLevels.Info, $"LIAM engine v{Assembly.GetExecutingAssembly().GetName().Version} started");

            templates = new List<IAM_SecurityGroupTemplate>();
        }

        private string GetAdServer()
        {
            return string.IsNullOrWhiteSpace(effectiveDomainController) ? domainName : effectiveDomainController;
        }

        private ResultToken checkRequiredVariables()
        {
            ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
            resultToken.resultErrorId = 0;
            if (String.IsNullOrEmpty(ConfigID))
            {
                resultToken.resultErrorId = 30001;
                resultToken.resultMessage = "Kein ConfigID gewählt ";
                return resultToken;
            }
            if (string.IsNullOrEmpty(username) | String.IsNullOrEmpty(new NetworkCredential("", password).Password) | String.IsNullOrEmpty(domainName))
            {
                resultToken.resultErrorId = 30002;
                resultToken.resultMessage = "Fehlende Anmeldeinformationen";
                return resultToken;
            }
            if (String.IsNullOrEmpty(groupPrefix))
            {
                resultToken.resultErrorId = 30004;
                resultToken.resultMessage = "Kein Gruppen Präfix angegeben";
                return resultToken;
            }
            if (String.IsNullOrEmpty(newFolderPath))
            {
                resultToken.resultErrorId = 30005;
                resultToken.resultMessage = "Kein Pfad für neues Verzeichnis angegeben";
                return resultToken;
            }
            if (String.IsNullOrEmpty(newFolderParent))
            {
                resultToken.resultErrorId = 30006;
                resultToken.resultMessage = "Kein Pfad für neues Übergeordnetesverzeichnis angegeben";
                return resultToken;
            }
            if (String.IsNullOrEmpty(baseFolder))
            {
                resultToken.resultErrorId = 30007;
                resultToken.resultMessage = "Kein Basisverzeichnis angegeben";
                return resultToken;
            }
            return resultToken;
        }
        public ResultToken createDataArea()
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {

                ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
                resultToken.resultErrorId = 0;
                if (checkRequiredVariables().resultErrorId == 0)
                {
                    InitializeFolderContext();
                    try
                    {
                        // ImpersonationHelper.Impersonate(domainName, username, new NetworkCredential("", password).Password, delegate
                        // {
                        if (Connection != null)
                            Connection.Dispose();

                        DefaultLogger.LogEntry(LogLevels.Info, $"Establishing connection to {baseFolder}, User: {username}, Password: {Helper.MaskAllButLastAndFirst(new NetworkCredential("", password).Password)}");
                        using (Connection = new cNetworkConnection(baseFolder, username, new NetworkCredential("", password).Password))
                        {
                            var traverseBoundaryResult = ValidateTraverseBoundaryForCurrentFolder();
                            if (traverseBoundaryResult.resultErrorId != 0)
                                return traverseBoundaryResult;

                            var folderCheckResult = checkFolder();
                            if (folderCheckResult.resultErrorId == 0)
                            {
                                try
                                {
                                    createADGroups(resultToken);
                                    try
                                    {
                                        resultToken = MergeResultTokens(resultToken, createFolder());
                                        if (resultToken.resultErrorId == 0)
                                        {
                                            try
                                            {
                                                resultToken = MergeResultTokens(resultToken, SetTraversePermissions());
                                            }
                                            catch (Exception e)
                                            {
                                                resultToken.resultErrorId = 30200;
                                                resultToken.resultMessage = "Fehler beim setzen der Traverserechte \n" + e.Message;
                                            }
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        resultToken.resultErrorId = 30200;
                                        resultToken.resultMessage = "Fehler beim Erstellen der Verzeichnisse \n" + e.Message;
                                    }
                                }
                                catch (Exception e)
                                {
                                    resultToken.resultErrorId = 30100;
                                    resultToken.resultMessage = "Fehler beim Erstellen der AD Gruppen \n" + e.Message;
                                }


                            }
                            else
                            {
                                resultToken = folderCheckResult;
                            }
                            /* },
                             logonType,
                             logonProvider);
                            */
                        }
                    }
                    catch (Exception e)
                    {
                        resultToken.resultErrorId = 30000;
                        resultToken.resultMessage = "Fehler beim Herstellen der Verbindung \n " + e.Message;
                    }
                }
                else
                {
                    return checkRequiredVariables();
                }
                return resultToken;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private ResultToken MergeResultTokens(ResultToken target, ResultToken source)
        {
            if (target == null)
                return source;

            if (source == null)
                return target;

            if (source.resultErrorId != 0 || target.resultErrorId == 0)
                target.resultErrorId = source.resultErrorId;
            if (!string.IsNullOrWhiteSpace(source.resultMessage))
                target.resultMessage = source.resultMessage;
            if (!string.IsNullOrWhiteSpace(source.resultFunction))
                target.resultFunction = source.resultFunction;
            target.createdGroups.AddRange(source.createdGroups);
            target.reusedGroups.AddRange(source.reusedGroups);
            target.addedAclEntries.AddRange(source.addedAclEntries);
            target.skippedAclEntries.AddRange(source.skippedAclEntries);
            target.ensuredTraverseGroups.AddRange(source.ensuredTraverseGroups);
            target.warnings.AddRange(source.warnings);
            return target;
        }

        private ResultToken checkRequiredVariablesForEnsure()
        {
            ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
            resultToken.resultErrorId = 0;
            if (String.IsNullOrEmpty(ConfigID))
            {
                resultToken.resultErrorId = 30001;
                resultToken.resultMessage = "Kein ConfigID gewählt ";
                return resultToken;
            }
            if (string.IsNullOrEmpty(username) | String.IsNullOrEmpty(new NetworkCredential("", password).Password) | String.IsNullOrEmpty(domainName))
            {
                resultToken.resultErrorId = 30002;
                resultToken.resultMessage = "Fehlende Anmeldeinformationen";
                return resultToken;
            }
            if (String.IsNullOrEmpty(groupPrefix))
            {
                resultToken.resultErrorId = 30004;
                resultToken.resultMessage = "Kein Gruppen Präfix angegeben";
                return resultToken;
            }
            if (String.IsNullOrEmpty(newFolderPath))
            {
                resultToken.resultErrorId = 30005;
                resultToken.resultMessage = "Kein Pfad für Verzeichnis angegeben";
                return resultToken;
            }
            if (String.IsNullOrEmpty(baseFolder))
            {
                resultToken.resultErrorId = 30007;
                resultToken.resultMessage = "Kein Basisverzeichnis angegeben";
                return resultToken;
            }
            return resultToken;
        }

        private void InitializeFolderContext()
        {
            newDataArea = new DataArea();
            var folder = new IAM_Folder
            {
                configurationID = ConfigID,
                technicalName = newFolderPath,
                targetType = (int)IAM_TargetType.FileSystem,
                Parent = newFolderParent,
                ParentUID = string.IsNullOrWhiteSpace(newFolderParent) ? string.Empty : DataArea.GetUniqueDataAreaID(newFolderParent),
                baseFolder = baseFolder
            };
            newDataArea.IAM_Folders.Add(folder);

            newSecurityGroups = new SecurityGroups
            {
                username = username,
                domainName = domainName,
                effectiveDomainController = effectiveDomainController,
                password = password,
                ForceStrictAdGroupNames = forceStrictAdGroupNames,
                PreserveAdGroupNameCase = preserveAdGroupNameCase
            };
        }

        private ResultToken ValidateTraverseBoundaryForCurrentFolder()
        {
            var resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
            resultToken.resultErrorId = 0;

            var boundaryPath = GetNormalizedTraverseBoundaryPath();
            if (string.IsNullOrWhiteSpace(boundaryPath))
                return resultToken;

            var targetParent = new DirectoryInfo(newFolderPath).Parent;
            if (targetParent == null)
            {
                resultToken.resultErrorId = 30009;
                resultToken.resultMessage = $"Traverse boundary '{traverseBoundaryPath}' cannot be validated because '{newFolderPath}' has no parent directory.";
                return resultToken;
            }

            if (!Directory.Exists(boundaryPath))
            {
                resultToken.resultErrorId = 30009;
                resultToken.resultMessage = $"Traverse boundary '{traverseBoundaryPath}' does not exist or is not reachable.";
                return resultToken;
            }

            if (!IsSameOrAncestorPath(boundaryPath, targetParent.FullName))
            {
                resultToken.resultErrorId = 30009;
                resultToken.resultMessage = $"Traverse boundary '{traverseBoundaryPath}' is not a parent path of '{newFolderPath}'.";
            }

            return resultToken;
        }

        public ResultToken ensureDataAreaPermissions(bool ensureTraverseGroups = false)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                var resultToken = checkRequiredVariablesForEnsure();
                if (resultToken.resultErrorId != 0)
                    return resultToken;

                if (Connection != null)
                    Connection.Dispose();

                using (Connection = new cNetworkConnection(baseFolder, username, new NetworkCredential("", password).Password))
                {
                    if (!Directory.Exists(newFolderPath))
                    {
                        resultToken.resultErrorId = 30203;
                        resultToken.resultMessage = "Verzeichnis existiert nicht";
                        return resultToken;
                    }

                    var parentDirectory = Directory.GetParent(newFolderPath);
                    if (string.IsNullOrWhiteSpace(newFolderParent))
                        newFolderParent = parentDirectory?.FullName;

                    InitializeFolderContext();

                    var traverseBoundaryResult = ValidateTraverseBoundaryForCurrentFolder();
                    if (traverseBoundaryResult.resultErrorId != 0)
                        return traverseBoundaryResult;

                    ensureADGroups(resultToken);
                    resultToken = ensureFolderPermissions(resultToken);

                    if (resultToken.resultErrorId != 0)
                        return resultToken;

                    if (ensureTraverseGroups)
                    {
                        if (WhatIf)
                        {
                            resultToken.warnings.Add("Traverse group preview is not supported in WhatIf mode for automatic DataArea ensure.");
                            resultToken.resultMessage = "Gruppen- und ACL-Vorschau erfolgreich erstellt";
                            return resultToken;
                        }

                        var traverseResult = SetTraversePermissions();
                        if (traverseResult != null)
                        {
                            resultToken.createdGroups.AddRange(traverseResult.createdGroups);
                            resultToken.reusedGroups.AddRange(traverseResult.reusedGroups);
                            resultToken.addedAclEntries.AddRange(traverseResult.addedAclEntries);
                            resultToken.skippedAclEntries.AddRange(traverseResult.skippedAclEntries);
                            resultToken.ensuredTraverseGroups.AddRange(traverseResult.ensuredTraverseGroups);
                            resultToken.warnings.AddRange(traverseResult.warnings);
                            if (traverseResult.resultErrorId != 0)
                            {
                                resultToken.resultErrorId = traverseResult.resultErrorId;
                                resultToken.resultMessage = traverseResult.resultMessage;
                                return resultToken;
                            }
                        }
                    }

                    resultToken.resultMessage = WhatIf
                        ? "Gruppen- und ACL-Vorschau erfolgreich erstellt"
                        : "Gruppen und ACLs erfolgreich sichergestellt";
                    return resultToken;
                }
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private ResultToken SetTraversePermissions()
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
                resultToken.resultErrorId = 0;

                // Loggen der DomainContext-Parameter
                DefaultLogger.LogEntry(LogLevels.Debug, $"DomainName: {domainName}, Username: {username}");

                if (string.IsNullOrEmpty(domainName) || string.IsNullOrEmpty(username) || password is null)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "Eines der DomainContext-Parameter ist null oder leer.");
                    return resultToken;
                }

                var domainContext = new PrincipalContext(ContextType.Domain, GetAdServer(), username, new NetworkCredential("", password).Password);
                DefaultLogger.LogEntry(LogLevels.Debug, "PrincipalContext erfolgreich erstellt.");

                // Überprüfen von newDataArea und IAM_Folders
                if (newDataArea == null)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "newDataArea ist null.");
                    return resultToken;
                }

                if (newDataArea.IAM_Folders == null || newDataArea.IAM_Folders.Count == 0)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "IAM_Folders ist null oder leer.");
                    return resultToken;
                }

                DirectoryInfo newDir = new DirectoryInfo(newDataArea.IAM_Folders[0].technicalName);
                DefaultLogger.LogEntry(LogLevels.Debug, $"Neues Verzeichnis: {newDir.FullName}");

                DirectoryInfo parent = newDir.Parent;
                if (parent == null)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "Parent-Verzeichnis ist null.");
                    return resultToken;
                }
                DefaultLogger.LogEntry(LogLevels.Debug, $"Parent-Verzeichnis: {parent.FullName}");

                var lvl = DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
                DefaultLogger.LogEntry(LogLevels.Debug, $"Ebene (lvl): {lvl}");
                var currentTraverseLevel = lvl;
                var defaultTraverseLoopIndex = lvl;
                var hasTraverseBoundary = !string.IsNullOrWhiteSpace(GetNormalizedTraverseBoundaryPath());
                var processedNearestTraverseParent = false;

                // Überprüfen der Templates
                if (templates == null)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "templates ist null.");
                    return resultToken;
                }

                var traverseGroupTemplate = templates.FirstOrDefault(t => t.Type.Equals(SecurityGroupType.Traverse));
                if (traverseGroupTemplate == null)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "traverseGroupTemplate ist null.");
                    return resultToken;
                }
                DefaultLogger.LogEntry(LogLevels.Debug, $"traverseGroupTemplate gefunden");

                // Überprüfen der traverseGroupTemplate-Eigenschaften
                if (traverseGroupTemplate.WildcardTemplate == null)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "WildcardTemplate von traverseGroupTemplate ist null.");
                    return resultToken;
                }
                DefaultLogger.LogEntry(LogLevels.Debug, $"traverseGroupTemplate.WildcardTemplate: {traverseGroupTemplate.WildcardTemplate}");

                if (traverseGroupTemplate.NamingTemplate == null)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "NamingTemplate von traverseGroupTemplate ist null.");
                    return resultToken;
                }
                DefaultLogger.LogEntry(LogLevels.Debug, $"traverseGroupTemplate.NamingTemplate: {traverseGroupTemplate.NamingTemplate}");

                if (string.IsNullOrEmpty(baseFolder))
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "baseFolder ist null oder leer.");
                    return resultToken;
                }

                GroupPrincipal traverseGroup = null;

                // Überprüfen, ob createTraverseGroupLvl initialisiert ist
                DefaultLogger.LogEntry(LogLevels.Debug, $"createTraverseGroupLvl: {createTraverseGroupLvl}");

                if (createTraverseGroupLvl == -1)
                {
                    DefaultLogger.LogEntry(LogLevels.Error, "createTraverseGroupLvl ist auf -1 gesetzt.");
                    return resultToken;
                }

                while (parent != null && (hasTraverseBoundary || defaultTraverseLoopIndex >= createTraverseGroupLvl))
                {
                    DefaultLogger.LogEntry(LogLevels.Debug, $"Verarbeite Ebene {currentTraverseLevel}.");

                    if (parent == null)
                    {
                        DefaultLogger.LogEntry(LogLevels.Error, "Parent ist null innerhalb der Schleife.");
                        break;
                    }

                    var canManageTraversePath = CanManageTraversePermissionsForPath ?? CanManagePermissionsForPath;
                    if (canManageTraversePath != null && !canManageTraversePath(parent.FullName))
                    {
                        DefaultLogger.LogEntry(LogLevels.Debug, $"Überspringe Traverse-Verarbeitung für nicht verwaltbaren NTFS-Pfad: {parent.FullName}");
                        if (IsTraverseBoundaryPath(parent.FullName))
                            break;

                        parent = parent.Parent;
                        if (parent != null)
                        {
                            currentTraverseLevel = hasTraverseBoundary
                                ? currentTraverseLevel + 1
                                : DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
                            if (!hasTraverseBoundary)
                                defaultTraverseLoopIndex--;
                            DefaultLogger.LogEntry(LogLevels.Debug, $"Neue Ebene (lvl) nach Überspringen: {currentTraverseLevel}");
                        }
                        else
                        {
                            DefaultLogger.LogEntry(LogLevels.Debug, "Parent nach Überspringen ist null.");
                        }
                        continue;
                    }

                    DefaultLogger.LogEntry(LogLevels.Debug, $"Hole ACL für Ordner: {parent.FullName}");
                    AuthorizationRuleCollection ACLs = null;
                    try
                    {
                        ACLs = parent.GetAccessControl(AccessControlSections.Access).GetAccessRules(true, true, typeof(NTAccount));
                        DefaultLogger.LogEntry(LogLevels.Debug, "ACLs erfolgreich abgerufen.");
                    }
                    catch (Exception ex)
                    {
                        DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Abrufen der ACLs: {ex.Message}");
                        continue; // Weiter zur nächsten Iteration
                    }

                    if (ACLs == null)
                    {
                        DefaultLogger.LogEntry(LogLevels.Error, "ACLs ist null.");
                        continue;
                    }

                    GroupPrincipal parentTraverseGroup = null;
                    var parentTraverseAclExists = false;
                    string relativePathRaw = DataArea.GetRelativePath(parent.FullName, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
                    relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
                    DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath vor Normalisierung: {relativePathRaw}");

                    var relativePathSegments = relativePathRaw.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
                    var sanitizedSegments = relativePathSegments.Select(i => Helper.SanitizePathSegment(i, groupNameSanitizeReplacement)).ToArray();
                    var relativePath = sanitizedSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(sanitizedSegments, groupNameSanitizeReplacement) : string.Empty;
                    DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath nach Normalisierung: {relativePath}");
                    var folderName = sanitizedSegments.Length > 0
                        ? sanitizedSegments[sanitizedSegments.Length - 1]
                        : Helper.SanitizePathSegment(Path.GetFileName(parent.FullName.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), groupNameSanitizeReplacement);
                    var traverseTags = GetTraverseReplacementTags(parent.FullName);
                    var rootContext = Helper.GetRootPathTemplateContext(baseFolder, groupNameSanitizeReplacement);
                    var boundedTraverseContext = Helper.GetBoundedAdGroupTemplateContext(
                        traverseGroupTemplate.NamingTemplate,
                        true,
                        relativePath,
                        sanitizedSegments,
                        folderName,
                        traverseTags,
                        Helper.MaxAdGroupNameLength,
                        $"Traverse fuer '{parent.FullName}'",
                        "AD-Gruppenname",
                        rootContext,
                        preserveAdGroupNameCase,
                        groupNameSanitizeReplacement);
                    var boundedTraverseDescriptionContext = Helper.GetBoundedAdGroupTemplateContext(
                        traverseGroupTemplate.DescriptionTemplate,
                        true,
                        relativePath,
                        sanitizedSegments,
                        folderName,
                        traverseTags,
                        Helper.MaxAdGroupDescriptionLength,
                        $"Traverse fuer '{parent.FullName}'",
                        "AD-Gruppenbeschreibung",
                        rootContext,
                        preserveAdGroupNameCase,
                        groupNameSanitizeReplacement);
                    var adjustedTraverseSegments = boundedTraverseContext.SanitizedSegments ?? Array.Empty<string>();
                    var adjustedTraverseRelativePath = adjustedTraverseSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(adjustedTraverseSegments, groupNameSanitizeReplacement) : string.Empty;
                    var adjustedTraverseFolderName = boundedTraverseContext.FolderName;
                    var adjustedTraverseDescriptionSegments = boundedTraverseDescriptionContext.SanitizedSegments ?? Array.Empty<string>();
                    var adjustedTraverseDescriptionRelativePath = adjustedTraverseDescriptionSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(adjustedTraverseDescriptionSegments, groupNameSanitizeReplacement) : string.Empty;
                    var adjustedTraverseDescriptionFolderName = boundedTraverseDescriptionContext.FolderName;
                    var traverseNameTemplate = Helper.ApplyAdGroupNameCasing(
                        Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName, rootContext, groupNameSanitizeReplacement).ReplaceTags(traverseTags),
                        preserveAdGroupNameCase);
                    var traverseDescriptionTemplate = Helper.ApplyAdGroupNameCasing(
                        Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, adjustedTraverseDescriptionRelativePath, adjustedTraverseDescriptionSegments, adjustedTraverseDescriptionFolderName, rootContext, groupNameSanitizeReplacement).ReplaceTags(traverseTags),
                        preserveAdGroupNameCase);

                    string traverseRegex = null;
                    try
                    {
                        traverseRegex = Helper.ApplyAdGroupNameCasing(
                            Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.WildcardTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName, rootContext, groupNameSanitizeReplacement).ReplaceTags(traverseTags),
                            preserveAdGroupNameCase);
                        DefaultLogger.LogEntry(LogLevels.Debug, $"traverseRegex: {traverseRegex}");
                    }
                    catch (Exception ex)
                    {
                        DefaultLogger.LogEntry(LogLevels.Error, $"Fehler bei der Erstellung von traverseRegex: {ex.Message}");
                        continue;
                    }

                    var hasTraverseWildcard = !string.IsNullOrWhiteSpace(traverseRegex);
                    foreach (FileSystemAccessRule acl in ACLs)
                    {
                        if (!hasTraverseWildcard)
                            break;

                        var searchString = acl.IdentityReference.Value;
                        var aclSplit = searchString.Split('\\');
                        if (aclSplit.Length == 2)
                        {
                            searchString = aclSplit[1];
                        }
                        DefaultLogger.LogEntry(LogLevels.Debug, $"Suche GroupPrincipal für: {searchString}");

                        var princ = GroupPrincipal.FindByIdentity(domainContext, searchString);
                        if (princ != null)
                        {
                            DefaultLogger.LogEntry(LogLevels.Debug, $"Gefundene Gruppe: {princ.Name}");
                        }
                        else
                        {
                            DefaultLogger.LogEntry(LogLevels.Debug, $"Keine Gruppe gefunden für: {searchString}");
                        }

                        if (princ != null && Regex.IsMatch(princ.Name, traverseRegex, RegexOptions.IgnoreCase))
                        {
                            parentTraverseGroup = princ;
                            parentTraverseAclExists = true;
                            DefaultLogger.LogEntry(LogLevels.Debug, $"parentTraverseGroup gesetzt: {parentTraverseGroup.Name}");
                        }
                        if (parentTraverseGroup != null)
                            break;
                    }

                    if (parentTraverseGroup == null && hasTraverseWildcard && !forceStrictAdGroupNames)
                    {
                        parentTraverseGroup = FindTraverseGroupByWildcard(domainContext, traverseRegex);
                        if (parentTraverseGroup != null)
                        {
                            resultToken.reusedGroups.Add(parentTraverseGroup.Name);
                            resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name);
                            DefaultLogger.LogEntry(LogLevels.Debug, $"Vorhandene Traverse-Gruppe per Wildcard wiederverwendet: {parentTraverseGroup.Name}");
                        }
                    }

                    if (parentTraverseGroup == null && !string.IsNullOrWhiteSpace(traverseNameTemplate))
                    {
                        for (var loop = 0; loop < 20; loop++)
                        {
                            var candidateName = traverseNameTemplate.ReplaceLoopTag(loop);
                            parentTraverseGroup = GroupPrincipal.FindByIdentity(domainContext, candidateName);
                            if (parentTraverseGroup == null)
                                continue;

                            resultToken.reusedGroups.Add(candidateName);
                            resultToken.ensuredTraverseGroups.Add(candidateName);
                            DefaultLogger.LogEntry(LogLevels.Debug, $"Vorhandene Traverse-Gruppe wiederverwendet: {candidateName}");
                            break;
                        }
                    }

                    if (parentTraverseGroup == null && !string.IsNullOrWhiteSpace(traverseNameTemplate))
                    {
                        DefaultLogger.LogEntry(LogLevels.Debug, "Erstelle neue TraverseGroup.");
                        if (newSecurityGroups == null)
                        {
                            DefaultLogger.LogEntry(LogLevels.Error, "newSecurityGroups ist null.");
                            continue;
                        }

                        IAM_SecurityGroup newTraverseGroup = null;
                        var loop = 0;
                        do
                        {
                            try
                            {
                                newTraverseGroup = new IAM_SecurityGroup()
                                {
                                    Name = traverseNameTemplate.ReplaceLoopTag(loop),
                                    description = traverseDescriptionTemplate.ReplaceLoopTag(loop),
                                    technicalName = "CN=" + traverseNameTemplate.ReplaceLoopTag(loop) + "," + groupOUPath,
                                    Scope = traverseGroupTemplate.Scope
                                };
                                DefaultLogger.LogEntry(LogLevels.Debug, $"Erstellte TraverseGroup: {newTraverseGroup.Name} (Loop: {loop})");
                                loop++;
                            }
                            catch (Exception ex)
                            {
                                DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Erstellen von newTraverseGroup: {ex.Message}");
                                break;
                            }
                        } while (newSecurityGroups.GroupAllreadyExisting(newTraverseGroup.Name) && loop < 20);

                        if (newTraverseGroup != null)
                        {
                            if (string.IsNullOrEmpty(groupOUPath))
                            {
                                DefaultLogger.LogEntry(LogLevels.Error, "groupOUPath ist null oder leer.");
                                continue;
                            }

                            if (parent.Parent != null)
                            {
                                DefaultLogger.LogEntry(LogLevels.Debug, "Parent.Parent ist nicht null. Erstelle AD-Gruppe.");
                                if (WhatIf)
                                {
                                    resultToken.createdGroups.Add(newTraverseGroup.Name);
                                    resultToken.ensuredTraverseGroups.Add(newTraverseGroup.Name);
                                    resultToken.warnings.Add($"Traverse-Gruppe würde angelegt werden: {newTraverseGroup.Name}");
                                    resultToken.addedAclEntries.Add(newTraverseGroup.Name);
                                    parentTraverseAclExists = true;
                                }
                                else
                                {
                                    try
                                    {
                                        newSecurityGroups.CreateADGroup(groupOUPath, newTraverseGroup, null);
                                        DefaultLogger.LogEntry(LogLevels.Debug, $"AD-Gruppe erstellt: {newTraverseGroup.Name}");
                                        resultToken.createdGroups.Add(newTraverseGroup.Name);
                                        resultToken.ensuredTraverseGroups.Add(newTraverseGroup.Name);
                                    }
                                    catch (Exception ex)
                                    {
                                        DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Erstellen der AD-Gruppe: {ex.Message}");
                                        continue;
                                    }

                                    parentTraverseGroup = GroupPrincipal.FindByIdentity(domainContext, newTraverseGroup.Name);
                                    if (parentTraverseGroup == null)
                                    {
                                        DefaultLogger.LogEntry(LogLevels.Error, $"parentTraverseGroup konnte nach Erstellung der Gruppe nicht gefunden werden: {newTraverseGroup.Name}");
                                        continue;
                                    }

                                    try
                                    {
                                        var accesscontrol = parent.GetAccessControl();
                                        accesscontrol.AddAccessRule(new FileSystemAccessRule(parentTraverseGroup.Sid,
                                                FileSystemRights.Read, InheritanceFlags.None, PropagationFlags.None,
                                                AccessControlType.Allow));
                                        DefaultLogger.LogEntry(LogLevels.Debug, $"Setze Traverse-ACL auf: {parent.FullName} für {parentTraverseGroup.DistinguishedName}");
                                        parent.SetAccessControl(accesscontrol);
                                        resultToken.addedAclEntries.Add(parentTraverseGroup.Name);
                                        parentTraverseAclExists = true;
                                    }
                                    catch (Exception ex)
                                    {
                                        DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Setzen der ACL: {ex.Message}");
                                        continue;
                                    }
                                }
                            }
                            else
                            {
                                DefaultLogger.LogEntry(LogLevels.Debug, "Parent.Parent ist null. Traverse-ACL kann nicht gesetzt werden.");
                            }
                        }
                    }

                    if (parentTraverseGroup != null && !parentTraverseAclExists)
                    {
                        try
                        {
                            var accessControl = parent.GetAccessControl();
                            var rules = accessControl.GetAccessRules(true, true, typeof(SecurityIdentifier)).Cast<FileSystemAccessRule>();
                            var hasAcl = rules.Any(rule =>
                                rule.AccessControlType == AccessControlType.Allow
                                && rule.IdentityReference.Value == parentTraverseGroup.Sid.Value
                                && (rule.FileSystemRights & FileSystemRights.Read) == FileSystemRights.Read);

                            if (hasAcl)
                            {
                                resultToken.skippedAclEntries.Add(parentTraverseGroup.Name);
                            }
                            else
                            {
                                if (!WhatIf)
                                {
                                    accessControl.AddAccessRule(new FileSystemAccessRule(parentTraverseGroup.Sid,
                                            FileSystemRights.Read, InheritanceFlags.None, PropagationFlags.None,
                                            AccessControlType.Allow));
                                    parent.SetAccessControl(accessControl);
                                }
                                resultToken.addedAclEntries.Add(parentTraverseGroup.Name);
                            }
                        }
                        catch (Exception ex)
                        {
                            DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Sicherstellen der Traverse-ACL: {ex.Message}");
                            continue;
                        }
                    }

                    if (parentTraverseGroup != null)
                    {
                        if (!processedNearestTraverseParent)
                        {
                            DefaultLogger.LogEntry(LogLevels.Debug, "Verarbeite SecurityGroups bei oberster Ebene.");
                            foreach (var currentSecGroup in newSecurityGroups.IAM_SecurityGroups)
                            {
                                if (currentSecGroup == null)
                                {
                                    DefaultLogger.LogEntry(LogLevels.Error, "currentSecGroup ist null.");
                                    continue;
                                }
                                if (currentSecGroup.Scope != GroupScope.Global)
                                    continue;

                                if (WhatIf)
                                {
                                    resultToken.warnings.Add($"Traverse-Gruppe '{parentTraverseGroup.Name}' würde Mitglied '{currentSecGroup.Name}' erhalten.");
                                    continue;
                                }

                                if (!TryEnsureGlobalGroupMembershipWithRetry(domainContext, parentTraverseGroup, currentSecGroup))
                                    continue;
                            }
                            traverseGroup = parentTraverseGroup;
                            processedNearestTraverseParent = true;
                        }
                        else
                        {
                            if (traverseGroup != null && parentTraverseGroup != null)
                            {
                                try
                                {
                                    if (!parentTraverseGroup.Members.Contains(traverseGroup))
                                    {
                                        if (WhatIf)
                                        {
                                            resultToken.warnings.Add($"Traverse-Gruppe '{parentTraverseGroup.Name}' würde verschachtelte Gruppe '{traverseGroup.Name}' erhalten.");
                                        }
                                        else
                                        {
                                            if (!TryEnsureNestedTraverseGroupMembershipWithRetry(parentTraverseGroup, traverseGroup))
                                                continue;
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Hinzufügen der Traverse-Gruppe: {ex.Message}");
                                    continue;
                                }
                            }
                        }
                        try
                        {
                            if (!WhatIf)
                            {
                                parentTraverseGroup.Save();
                                DefaultLogger.LogEntry(LogLevels.Debug, $"parentTraverseGroup gespeichert: {parentTraverseGroup.Name}");
                            }
                        }
                        catch (Exception ex)
                        {
                            DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Speichern der parentTraverseGroup: {ex.Message}");
                        }
                    }
                    else
                    {
                        DefaultLogger.LogEntry(LogLevels.Debug, "parentTraverseGroup ist null.");
                    }

                    if (parentTraverseGroup != null && !resultToken.ensuredTraverseGroups.Contains(parentTraverseGroup.Name))
                        resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name);

                    if (IsTraverseBoundaryPath(parent.FullName))
                        break;

                    // Aktualisiere parent und lvl für die nächste Iteration
                    parent = parent.Parent;
                    if (parent != null)
                    {
                        currentTraverseLevel = hasTraverseBoundary
                            ? currentTraverseLevel + 1
                            : DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
                        if (!hasTraverseBoundary)
                            defaultTraverseLoopIndex--;
                        DefaultLogger.LogEntry(LogLevels.Debug, $"Neue Ebene (lvl) nach Aktualisierung: {currentTraverseLevel}");
                    }
                    else
                    {
                        DefaultLogger.LogEntry(LogLevels.Debug, "Parent nach Aktualisierung ist null.");
                    }
                }

                return resultToken;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private string GetNormalizedTraverseBoundaryPath()
        {
            return NormalizeDirectoryPath(traverseBoundaryPath);
        }

        private bool IsTraverseBoundaryPath(string path)
        {
            var boundaryPath = GetNormalizedTraverseBoundaryPath();
            return !string.IsNullOrWhiteSpace(boundaryPath)
                && PathsEqual(boundaryPath, path);
        }

        private Dictionary<string, string> GetTraverseReplacementTags(string currentPath)
        {
            var visibleSegments = GetVisibleTraversePathSegments(currentPath);
            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
            {
                { "TRAVERSE_NAME", Helper.SanitizePathSegment(GetLastPathSegment(currentPath), groupNameSanitizeReplacement) },
                { "TRAVERSE_VISIBLEPATH", Helper.JoinSanitizedPathSegments(visibleSegments.Select(i => Helper.SanitizePathSegment(i, groupNameSanitizeReplacement)), groupNameSanitizeReplacement) }
            };
        }

        private IEnumerable<string> GetVisibleTraversePathSegments(string currentPath)
        {
            var normalizedCurrentPath = NormalizeDirectoryPath(currentPath);
            var boundaryPath = GetNormalizedTraverseBoundaryPath();
            if (string.IsNullOrWhiteSpace(boundaryPath))
                boundaryPath = NormalizeDirectoryPath(baseFolder);

            var visibleRoot = GetParentPath(boundaryPath);
            if (string.IsNullOrWhiteSpace(visibleRoot))
                visibleRoot = boundaryPath;

            var currentSegments = SplitPathSegments(normalizedCurrentPath);
            var rootSegments = SplitPathSegments(visibleRoot);
            if (currentSegments.Length <= rootSegments.Length)
                return currentSegments;

            var isRootPrefix = rootSegments
                .Select((segment, index) => new { segment, index })
                .All(i => string.Equals(i.segment, currentSegments[i.index], StringComparison.OrdinalIgnoreCase));
            return isRootPrefix
                ? currentSegments.Skip(rootSegments.Length)
                : currentSegments;
        }

        private GroupPrincipal FindTraverseGroupByWildcard(PrincipalContext domainContext, string wildcardPattern)
        {
            if (domainContext == null || string.IsNullOrWhiteSpace(wildcardPattern))
                return null;

            Regex wildcardRegex;
            try
            {
                wildcardRegex = new Regex(wildcardPattern, RegexOptions.IgnoreCase);
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                return null;
            }

            var basePath = "LDAP://" + GetAdServer();
            if (!string.IsNullOrWhiteSpace(groupOUPath))
                basePath += "/" + groupOUPath;

            DirectoryEntry entry = new DirectoryEntry
            {
                Path = basePath,
                Username = username,
                Password = new NetworkCredential("", password).Password,
                AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
            };

            DirectorySearcher search = new DirectorySearcher(entry)
            {
                Filter = "(objectClass=group)"
            };
            search.PageSize = 100000;
            search.PropertiesToLoad.Add("sAMAccountName");
            search.PropertiesToLoad.Add("objectSid");

            string matchedSid = null;
            string matchedName = null;
            var matchCount = 0;

            foreach (SearchResult result in search.FindAll())
            {
                if (!result.Properties.Contains("sAMAccountName") || result.Properties["sAMAccountName"].Count == 0)
                    continue;

                var samAccountName = result.Properties["sAMAccountName"][0]?.ToString();
                if (string.IsNullOrWhiteSpace(samAccountName) || !wildcardRegex.IsMatch(samAccountName))
                    continue;

                matchCount++;
                if (matchCount > 1)
                {
                    DefaultLogger.LogEntry(LogLevels.Warning, $"Multiple AD groups matched traverse wildcard '{wildcardPattern}' in '{basePath}'. Regex-based reuse is skipped.");
                    search.Dispose();
                    entry.Dispose();
                    return null;
                }

                matchedName = samAccountName;
                matchedSid = result.Properties.Contains("objectSid") && result.Properties["objectSid"].Count > 0
                    ? new SecurityIdentifier((byte[])result.Properties["objectSid"][0], 0).Value
                    : null;
            }

            search.Dispose();
            entry.Dispose();

            if (string.IsNullOrWhiteSpace(matchedSid))
                return null;

            DefaultLogger.LogEntry(LogLevels.Debug, $"Reusing existing traverse AD group '{matchedName}' via wildcard '{wildcardPattern}'.");
            return GroupPrincipal.FindByIdentity(domainContext, IdentityType.Sid, matchedSid);
        }

        private static string NormalizeDirectoryPath(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
                return string.Empty;

            var normalized = path.Trim().Replace('/', '\\').TrimEnd('\\');
            if (normalized.StartsWith(@"\\", StringComparison.Ordinal))
                return @"\\" + string.Join("\\", SplitPathSegments(normalized));

            return normalized;
        }

        private static bool PathsEqual(string left, string right)
        {
            return string.Equals(NormalizeDirectoryPath(left), NormalizeDirectoryPath(right), StringComparison.OrdinalIgnoreCase);
        }

        private static bool IsSameOrAncestorPath(string ancestorPath, string path)
        {
            var ancestor = NormalizeDirectoryPath(ancestorPath);
            var current = NormalizeDirectoryPath(path);
            return string.Equals(ancestor, current, StringComparison.OrdinalIgnoreCase)
                || current.StartsWith(ancestor + "\\", StringComparison.OrdinalIgnoreCase);
        }

        private static string GetParentPath(string path)
        {
            var segments = SplitPathSegments(path);
            if (segments.Length <= 1)
                return string.Empty;

            if (NormalizeDirectoryPath(path).StartsWith(@"\\", StringComparison.Ordinal))
                return @"\\" + string.Join("\\", segments.Take(segments.Length - 1));

            return string.Join("\\", segments.Take(segments.Length - 1));
        }

        private static string GetLastPathSegment(string path)
        {
            var segments = SplitPathSegments(path);
            return segments.Length == 0 ? string.Empty : segments[segments.Length - 1];
        }

        private static string[] SplitPathSegments(string path)
        {
            return (path ?? string.Empty)
                .Trim()
                .Replace('/', '\\')
                .Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
        }

        private bool TryEnsureGlobalGroupMembershipWithRetry(PrincipalContext domainContext, GroupPrincipal parentTraverseGroup, IAM_SecurityGroup currentSecGroup)
        {
            if (domainContext == null || parentTraverseGroup == null || currentSecGroup == null || string.IsNullOrWhiteSpace(currentSecGroup.UID))
                return false;

            return RetryTraverseMembershipAction(
                currentSecGroup.Name,
                currentSecGroup.CreatedNewEntry,
                () =>
                {
                    using (var groupPrincipal = GroupPrincipal.FindByIdentity(domainContext, IdentityType.Sid, currentSecGroup.UID))
                    {
                        if (groupPrincipal == null)
                            return false;

                        if (parentTraverseGroup.Members.Contains(groupPrincipal))
                            return true;

                        DefaultLogger.LogEntry(LogLevels.Debug, $"Füge {groupPrincipal.DistinguishedName} zur Traverse-Gruppe {parentTraverseGroup.DistinguishedName} hinzu");
                        parentTraverseGroup.Members.Add(groupPrincipal);
                        parentTraverseGroup.Save();
                        return true;
                    }
                });
        }

        private bool TryEnsureNestedTraverseGroupMembershipWithRetry(GroupPrincipal parentTraverseGroup, GroupPrincipal traverseGroup)
        {
            if (parentTraverseGroup == null || traverseGroup == null)
                return false;

            return RetryTraverseMembershipAction(
                traverseGroup.Name,
                true,
                () =>
                {
                    if (parentTraverseGroup.Members.Contains(traverseGroup))
                        return true;

                    DefaultLogger.LogEntry(LogLevels.Debug, $"Füge {traverseGroup.DistinguishedName} zur Traverse-Gruppe {parentTraverseGroup.DistinguishedName} hinzu");
                    parentTraverseGroup.Members.Add(traverseGroup);
                    parentTraverseGroup.Save();
                    return true;
                });
        }

        private bool RetryTraverseMembershipAction(string memberName, bool allowRetry, Func<bool> tryAction)
        {
            if (tryAction == null)
                return false;

            var retryDelaysMs = allowRetry
                ? new[] { 0, 250, 500, 1000, 2000, 5000 }
                : new[] { 0 };
            var delayIndex = 0;
            var maxWait = allowRetry ? TimeSpan.FromMinutes(3) : TimeSpan.Zero;
            var waitStopwatch = Stopwatch.StartNew();
            Exception lastException = null;

            while (true)
            {
                var delayMs = retryDelaysMs[Math.Min(delayIndex, retryDelaysMs.Length - 1)];
                if (delayMs > 0)
                    System.Threading.Thread.Sleep(delayMs);
                if (delayIndex < retryDelaysMs.Length - 1)
                    delayIndex++;

                try
                {
                    if (tryAction())
                    {
                        if (waitStopwatch.ElapsedMilliseconds > 0)
                            DefaultLogger.LogEntry(LogLevels.Debug, $"Traverse-Mitgliedschaft für '{memberName}' nach {waitStopwatch.Elapsed.TotalSeconds:F1}s erfolgreich.");
                        return true;
                    }
                }
                catch (Exception ex)
                {
                    lastException = ex;
                    DefaultLogger.LogEntry(LogLevels.Debug, $"Traverse-Mitgliedschaft für '{memberName}' noch nicht möglich: {ex.Message}");
                }

                if (!allowRetry || waitStopwatch.Elapsed >= maxWait)
                    break;
            }

            var suffix = lastException == null ? string.Empty : $" Letzte Exception: {lastException.Message}";
            DefaultLogger.LogEntry(LogLevels.Warning, $"Traverse-Mitgliedschaft für '{memberName}' konnte nach {waitStopwatch.Elapsed.TotalSeconds:F1}s nicht sichergestellt werden.{suffix}");
            return false;
        }

        private ResultToken checkFolder()
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
                resultToken.resultErrorId = 0;
                newSecurityGroups.effectiveDomainController = effectiveDomainController;
                if (Directory.Exists(newDataArea.IAM_Folders[0].technicalName))
                {
                    resultToken.resultMessage = "New folder " + newDataArea.IAM_Folders[0].technicalName + " already exists";
                    resultToken.resultErrorId = 30201;

                }
                return resultToken;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private static bool HasMatchingAccessRule(DirectoryInfo directory, SecurityIdentifier sid, FileSystemRights rights)
        {
            var rules = directory.GetAccessControl(AccessControlSections.Access)
                .GetAccessRules(true, true, typeof(SecurityIdentifier))
                .Cast<FileSystemAccessRule>();

            foreach (var rule in rules)
            {
                if (rule.AccessControlType != AccessControlType.Allow)
                    continue;

                if (!(rule.IdentityReference is SecurityIdentifier ruleSid))
                    continue;

                if (!string.Equals(ruleSid.Value, sid.Value, StringComparison.OrdinalIgnoreCase))
                    continue;

                if ((rule.FileSystemRights & rights) == rights)
                    return true;
            }

            return false;
        }

        private ResultToken ensureFolderPermissions(ResultToken resultToken)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                var directory = new DirectoryInfo(newDataArea.IAM_Folders[0].technicalName);
                foreach (var currentSecGroup in newSecurityGroups.IAM_SecurityGroups)
                {
                    if (WhatIf && string.IsNullOrWhiteSpace(currentSecGroup?.UID) && currentSecGroup?.CreatedNewEntry == true)
                    {
                        resultToken.addedAclEntries.Add(currentSecGroup.Name);
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(currentSecGroup?.UID))
                    {
                        resultToken.warnings.Add($"Keine SID für Gruppe '{currentSecGroup?.Name}' verfügbar.");
                        continue;
                    }

                    if (!(groupPermissionStrategy == PermissionGroupStrategy.AGDLP && currentSecGroup.Scope == GroupScope.Local
                        || groupPermissionStrategy == PermissionGroupStrategy.AGP && currentSecGroup.Scope == GroupScope.Global))
                    {
                        continue;
                    }

                    var sid = new SecurityIdentifier(currentSecGroup.UID);
                    if (HasMatchingAccessRule(directory, sid, currentSecGroup.rights))
                    {
                        resultToken.skippedAclEntries.Add(currentSecGroup.Name);
                        continue;
                    }

                    if (!WhatIf)
                        DataArea.AddDirectorySecurity(newDataArea.IAM_Folders[0].baseFolder, newDataArea.IAM_Folders[0].technicalName, sid, currentSecGroup.rights, AccessControlType.Allow);

                    resultToken.addedAclEntries.Add(currentSecGroup.Name);
                }

                return resultToken;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private void ensureADGroups(ResultToken resultToken)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                newSecurityGroups.IAM_SecurityGroups.Clear();
                newSecurityGroups.GenerateNewSecurityGroups(baseFolder,
                                                            newDataArea.IAM_Folders[0].technicalName,
                                                            groupPrefix,
                                                            groupOUPath,
                                                            groupPermissionStrategy,
                                                            groupTraverseTag,
                                                            groupReadTag,
                                                            groupWriteTag,
                                                            groupOwnerTag,
                                                            groupDLTag,
                                                            groupGTag,
                                                            groupCustomTags,
                                                            templates,
                                                            ReadACLPermission,
                                                            WriteACLPermission,
                                                            OwnerACLPermission,
                                                            0,
                                                            0,
                                                            groupNameSanitizeReplacement,
                                                            preserveAdGroupNameCase);

                List<UserPrincipal> owners = getUserPrincipalBySid(ownerUserSids);
                List<UserPrincipal> writers = getUserPrincipalBySid(writerUserSids);
                List<UserPrincipal> readers = getUserPrincipalBySid(readerUserSids);

                for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
                {
                    List<UserPrincipal> users;
                    if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupOwnerTag.ToUpper()))
                        users = owners;
                    else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupWriteTag.ToUpper()))
                        users = writers;
                    else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupReadTag.ToUpper()))
                        users = readers;
                    else
                        users = null;

                    if (WhatIf)
                    {
                        var existingGroup = newSecurityGroups.PreviewADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], newDataArea.IAM_Folders[0].technicalName);
                        newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry = existingGroup == null;
                    }
                    else
                    {
                        newSecurityGroups.EnsureADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], users, newDataArea.IAM_Folders[0].technicalName);
                    }

                    if (newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry)
                        resultToken.createdGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
                    else
                        resultToken.reusedGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
                }
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private ResultToken createFolder()
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
                resultToken.resultErrorId = 0;


                if (!Directory.Exists(newFolderParent))
                {
                    resultToken.resultMessage = "Übergeordnetesverzeichnis " + newDataArea.IAM_Folders[0].Parent + " des neuen Ordners " + newDataArea.IAM_Folders[0].technicalName + " existiert nicht";
                    resultToken.resultErrorId = 30202;
                    return resultToken;
                }
                else
                {
                    if (WhatIf)
                    {
                        newDataArea.IAM_Folders[0].UID = DataArea.GetUniqueDataAreaID(newDataArea.IAM_Folders[0].technicalName);
                        resultToken.warnings.Add($"Verzeichnis würde erstellt werden: {newDataArea.IAM_Folders[0].technicalName}");

                        for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
                        {
                            var currentSecGroup = newSecurityGroups.IAM_SecurityGroups[i];
                            if (groupPermissionStrategy == PermissionGroupStrategy.AGDLP && currentSecGroup.Scope == GroupScope.Local
                                || groupPermissionStrategy == PermissionGroupStrategy.AGP && currentSecGroup.Scope == GroupScope.Global)
                            {
                                resultToken.addedAclEntries.Add(currentSecGroup.Name);
                            }
                        }

                        resultToken.resultErrorId = 0;
                        resultToken.resultMessage = "Verzeichnis-, Gruppen- und ACL-Vorschau erfolgreich erstellt";
                        return resultToken;
                    }

                    DefaultLogger.LogEntry(LogLevels.Debug, $"Creating folder: {newDataArea.IAM_Folders[0].technicalName}");
                    DirectoryInfo newDir = Directory.CreateDirectory(newDataArea.IAM_Folders[0].technicalName);
                    newDataArea.IAM_Folders[0].UID = DataArea.GetUniqueDataAreaID(newDir.FullName);
                    for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
                    {
                        var currentSecGroup = newSecurityGroups.IAM_SecurityGroups[i];
                        var sid = new SecurityIdentifier(currentSecGroup.UID);
                        if (groupPermissionStrategy == PermissionGroupStrategy.AGDLP && currentSecGroup.Scope == GroupScope.Local
                            || groupPermissionStrategy == PermissionGroupStrategy.AGP && currentSecGroup.Scope == GroupScope.Global)
                            DataArea.AddDirectorySecurity(newDataArea.IAM_Folders[0].baseFolder, newDataArea.IAM_Folders[0].technicalName, sid, currentSecGroup.rights, AccessControlType.Allow);
                    }
                    AddDirectorySecurityACLDenyFolderDeletion(newDataArea.IAM_Folders[0].technicalName);

                    resultToken.resultErrorId = 0;
                    resultToken.resultMessage = "Verzeichnis erfolgreich erstellt";
                }
                return resultToken;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private void AddDirectorySecurityACLDenyFolderDeletion(string technicalName)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            // Create a new DirectoryInfo object.
            DirectoryInfo dInfo = new DirectoryInfo(technicalName);
            //DirectoryInfo dInfoBaseFolder = new DirectoryInfo(baseFolderTechnicalName);

            // Get a DirectorySecurity object that represents the 
            // current security settings.
            DirectorySecurity dSecurity = dInfo.GetAccessControl();

            // Add the FileSystemAccessRule to the security settings. 
            var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);

            dSecurity.AddAccessRule(new FileSystemAccessRule(everyone,
                                            FileSystemRights.Delete, InheritanceFlags.None, PropagationFlags.None,
                                            AccessControlType.Deny));
            // Set the new access settings.
            dInfo.SetAccessControl(dSecurity);

            LogMethodEnd(MethodBase.GetCurrentMethod());
        }

        private void createADGroups(ResultToken resultToken)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                Dictionary<FileSystemRights, string> currentGroupDIC = new Dictionary<FileSystemRights, string>();
                adGroupDic = new Dictionary<FileSystemRights, string>();

                var existingADGroupCount = 0;
                do
                {
                    newSecurityGroups.IAM_SecurityGroups.Clear();
                    newSecurityGroups.GenerateNewSecurityGroups(baseFolder,
                                                                newDataArea.IAM_Folders[0].technicalName,
                                                                groupPrefix,
                                                                groupOUPath,
                                                                groupPermissionStrategy,
                                                                groupTraverseTag,
                                                                groupReadTag,
                                                                groupWriteTag,
                                                                groupOwnerTag,
                                                                groupDLTag,
                                                                groupGTag,
                                                                groupCustomTags,
                                                                templates,
                                                                ReadACLPermission,
                                                                WriteACLPermission,
                                                                OwnerACLPermission,
                                                                existingADGroupCount,
                                                                0,
                                                                groupNameSanitizeReplacement,
                                                                preserveAdGroupNameCase);
                    /*
                    if (existingADGroupCount > 0 && !templates.All(t => t.Type == SecurityGroupType.Traverse || Regex.IsMatch(t.NamingTemplate, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})")))
                    {
                        var nt = templates.First(t => t.Type == SecurityGroupType.Traverse || Regex.IsMatch(t.NamingTemplate, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})")).NamingTemplate;
                        DefaultLogger.LogEntry(LogLevels.Debug, $"Naming template: {nt}");
                        throw new Exception("AD groups already existing and loop tag not found");
                    }
                    */
                    existingADGroupCount++;

                } while (newSecurityGroups.GroupsAllreadyExisting(groupOUPath) && existingADGroupCount < 1000);
                if(newSecurityGroups.GroupsAllreadyExisting(groupOUPath) && existingADGroupCount>= 1000)
                {
                    throw new Exception("Unique AD Group could not be generated after 1000 iterations");
                }
                List<UserPrincipal> users;
                List<UserPrincipal> owners = getUserPrincipalBySid(ownerUserSids);
                List<UserPrincipal> writers = getUserPrincipalBySid(writerUserSids);
                List<UserPrincipal> readers = getUserPrincipalBySid(readerUserSids);
                for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
                {
                    if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupOwnerTag.ToUpper()))
                        users = owners;
                    else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupWriteTag.ToUpper()))
                        users = writers;
                    else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupReadTag.ToUpper()))
                        users = readers;
                    else
                        users = null;

                    if (WhatIf)
                    {
                        var existingGroup = newSecurityGroups.PreviewADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], newDataArea.IAM_Folders[0].technicalName);
                        newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry = existingGroup == null;
                    }
                    else
                    {
                        newSecurityGroups.CreateADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], users);
                    }

                    if (resultToken != null)
                    {
                        if (newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry)
                            resultToken.createdGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
                        else
                            resultToken.reusedGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
                    }
                }
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private List<UserPrincipal> getUserPrincipalBySid(ICollection<string> UserSids)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                List<UserPrincipal> result = new List<UserPrincipal>();
                if (UserSids != null)
                {
                    foreach (var sid in UserSids)
                    {
                        if (!string.IsNullOrEmpty(sid))
                        {
                            UserPrincipal user = getUserPrincipalBySid(sid);
                            if (user != null && !result.Any(item => item.DistinguishedName == user.DistinguishedName))
                            {
                                result.Add(user);
                            }
                        }
                    }
                }
                return result;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        private UserPrincipal getUserPrincipalBySid(string sid)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());

            try
            {
                PrincipalContext ctx = new PrincipalContext(ContextType.Domain, GetAdServer(), username, new NetworkCredential("", password).Password);
                UserPrincipal user;
                user = UserPrincipal.FindByIdentity(ctx, IdentityType.Sid, (sid));
                return user;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(E);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }
        private string getTraverseRegex()
        {
            return groupWildcard
                .Replace("{{PREFIX}}", groupPrefix)
                .Replace("{{SCOPETAG}}", groupDLTag)
                .Replace("{{GROUPTYPEPOSTFIX}}", groupTraverseTag);
        }
    }
}
