
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

using C4IT.Logging;
using C4IT.Matrix42.ServerInfo;
using LiamAD;
using static C4IT.Logging.cLogManager;
using static LiamAD.ADServiceGroupCreator;
using static LiamAD.cActiveDirectoryBase;

namespace C4IT.LIAM
{
    public static class LiamInitializer
    {
        static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
        {
            return new cLiamProviderAD(LiamConfiguration, ProviderData);
        }
    }

    public class cLiamProviderAD : cLiamProviderBase
    {
        public static Guid adModuleId = new Guid("e820a625-0653-ee11-b886-00155d300101");
        private const string AdditionalConfigurationAdDomainControllersKey = "AdDomainControllers";
        private const string AdditionalConfigurationActiveDirectoryDomainControllersKey = "ActiveDirectoryDomainControllers";
        public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
        private readonly ADServiceGroupCreator _serviceGroupCreator;


        public cLiamProviderAD(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
            base(LiamConfiguration, ProviderData)
        {
            _serviceGroupCreator = new ADServiceGroupCreator(this);
        }

        public List<Tuple<string, string, string, string>> CreateServiceGroups(
            string serviceName,
            string description = null,
            eLiamAccessRoleScopes gruppenbereich = eLiamAccessRoleScopes.Universal,
            ADGroupType gruppentyp = ADGroupType.Distribution,
            IEnumerable<string> ownerSidList = null,
            IEnumerable<string> memberSidList = null)
        {
            return _serviceGroupCreator.EnsureServiceGroups(
                serviceName,
                description,
                gruppenbereich,
                gruppentyp,
                ownerSidList,
                memberSidList);
        }

        public override async Task<bool> LogonAsync()
        {
            if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
            {
                LogEntry($"Error: License not valid", LogLevels.Error);
                return false;
            }
            return await LogonAsync(true);
        }

        public async Task<bool> LogonAsync(bool force = false)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var LI = new cADLogonInfo()
                {
                    Domain = Domain,
                    DomainControllers = GetConfiguredDomainControllers(),
                    User = Credential?.Identification,
                    UserSecret = Credential?.Secret,
                    TargetGroupPath = this.GroupPath
                };
                var RetVal = await activeDirectoryBase.LogonAsync(LI);
                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return false;
        }

        private string GetConfiguredDomainControllers()
        {
            var value = GetAdditionalConfigurationValue(AdditionalConfigurationAdDomainControllersKey);
            if (!string.IsNullOrWhiteSpace(value))
                return value;

            return GetAdditionalConfigurationValue(AdditionalConfigurationActiveDirectoryDomainControllersKey);
        }

        private string GetAdditionalConfigurationValue(string key)
        {
            if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
                return string.Empty;

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

            return rawValue.Trim();
        }

        public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int Depth = -1)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return new List<cLiamDataAreaBase>();
                }
                if (!await LogonAsync())
                    return null;
                if (string.IsNullOrEmpty(this.GroupPath))
                    return null;

                // 1. Alle Roh-Resultate einlesen
                var rawList = await activeDirectoryBase.RequestSecurityGroupsListAsync(this.GroupFilter);
                if (rawList == null)
                    return null;

                // 2. Nur die, die dem RegEx entsprechen und deren Wert extrahieren
                var allResults = rawList
                    .Where(entry =>
                        string.IsNullOrEmpty(this.GroupRegEx)
                        || Regex.Match(entry.Value.DisplayName, this.GroupRegEx).Success)
                    .Select(entry => (cSecurityGroupResult)entry.Value)
                    .ToList();

                // 3. ManagedBySID-Werte sammeln (ohne Null-/Leereinträge)
                var managedBySids = new HashSet<string>(
                    allResults
                        .Select(r => r.ManagedBySID)
                        .Where(m => !string.IsNullOrEmpty(m))
                );

                // 4. Nur die Gruppen, deren ID nicht in managedBySids enthalten ist
                var filteredResults = allResults
                    .Where(r => !managedBySids.Contains(r.ID))
                    .ToList();

                // 5. In DataArea-Objekte umwandeln
                var SecurityGroups = new List<cLiamDataAreaBase>();
                foreach (var secGroup in filteredResults)
                {
                    SecurityGroups.Add(new cLiamAdGroupAsDataArea(this, secGroup));
                }

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



        public override async Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
        {

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return new List<cLiamDataAreaBase>();
                }
                if (!await LogonAsync())
                    return null;
                if (string.IsNullOrEmpty(this.GroupPath))
                    return null;

                var SecurityGroups = new List<cLiamDataAreaBase>();

                var SGL = await activeDirectoryBase.RequestSecurityGroupsListAsync(groupFilter);
                if (SGL == null)
                    return null;

                foreach (var Entry in SGL)
                {
                    if (!string.IsNullOrEmpty(this.GroupRegEx) && !Regex.Match(Entry.Value.DisplayName, this.GroupRegEx).Success)
                        continue;


                    var SecurityGroup = new cLiamAdGroup2(this, (cSecurityGroupResult)Entry.Value);
                    SecurityGroups.Add(SecurityGroup);
                }
                return SecurityGroups;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return null;
        }
        public int getDepth(string path)
        {
            return getDepth(this.RootPath, path);
        }
        public static int getDepth(DirectoryInfo root, DirectoryInfo folder)
        {
            var rootDepth = root.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length;
            var folderDepth = folder.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length;
            return folderDepth - rootDepth;

        }
        public static int getDepth(string root, string folder)
        {
            return getDepth(new DirectoryInfo(root), new DirectoryInfo(folder));

        }

        public override string GetLastErrorMessage()
        {
            throw new NotImplementedException();
        }

        public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
        {
            //TODO implement LoadDataArea
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                await Task.Delay(0);
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                var res = new cLiamAdGroupAsDataArea(this, new cSecurityGroupResult()
                {
                    Path = UID
                });
                return res;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
    }


    public class cLiamAdGroupAsDataArea : cLiamDataAreaBase
    {
        public new readonly cLiamProviderAD Provider = null;
        public readonly string dn = null;
        public readonly string scope = null;
        public readonly string ManagedBySID;

        public override Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            throw new NotImplementedException();
        }
        public cLiamAdGroupAsDataArea(cLiamProviderAD Provider, cSecurityGroupResult secGroup) : base(Provider)
        {
            this.UID = secGroup.ID;
            this.TechnicalName = secGroup.Path;
            this.DisplayName = secGroup.DisplayName;
            this.Description = secGroup.Description;
            this.Provider = Provider;
            this.dn = secGroup.Path;
            this.scope = secGroup.Scope.ToString();
            this.ManagedBySID = secGroup.ManagedBySID;
        }

        public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                return await GetMembersAsync(true);
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
        private async Task<List<cLiamUserInfo>> GetMembersAsync(bool owners)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var AD = this.Provider?.activeDirectoryBase;
                if (AD == null)
                {
                    LogEntry($"Could not get ad class from Provider for folder '{this.TechnicalName}'", LogLevels.Warning);
                    return null;
                }
                cADCollectionBase lstMembers;
                if (owners && !string.IsNullOrEmpty(OwnerRef))
                    lstMembers = await AD.GetMembersAsync(OwnerRef);
                else if (owners && !string.IsNullOrEmpty(dn))
                    lstMembers = await AD.GetManagedByMembersAsync(this.dn);
                else
                    lstMembers = null;
                if (lstMembers == null)
                {
                    LogEntry($"Could not get owner list for folder '{this.TechnicalName}'", LogLevels.Warning);
                    return null;
                }

                var RetVal = new List<cLiamUserInfo>(lstMembers.Count);
                LogEntry($"Owners for folder found: {lstMembers.Count}", LogLevels.Debug);
                foreach (var MemberEntry in lstMembers.Values)
                {
                    var User = new cLiamUserInfo()
                    {
                        DisplayName = MemberEntry.DisplayName,
                        UserPrincipalName = (MemberEntry as cADUserResult).UserPrincipalName,
                        SID = MemberEntry.ID
                    };
                    RetVal.Add(User);
                }

                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
    }
    public class cLiamAdGroup2 : cLiamDataAreaBase
    {
        public new readonly cLiamProviderAD Provider = null;
        public readonly string dn = null;
        public readonly string scope = null;
        public override Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            throw new NotImplementedException();
        }
        public cLiamAdGroup2(cLiamProviderAD Provider, cSecurityGroupResult secGroup) : base(Provider)
        {
            this.DisplayName = secGroup.DisplayName;
            this.UID = secGroup.ID;
            this.TechnicalName = secGroup.DisplayName;
            this.Provider = Provider;
            this.dn = secGroup.Path;
            this.scope = secGroup.Scope.ToString();
        }
    }
}
