using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Runtime.CompilerServices;

using C4IT.Logging;
using C4IT.MsGraph;
using static C4IT.Logging.cLogManager;
using C4IT.Matrix42.ServerInfo;
using static C4IT.MsGraph.cMsGraphSharepoint;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace C4IT.LIAM
{
    public static class LiamInitializer
    {
        public static Guid msTeamsModuleId = new Guid("2591832a-1b25-ec11-6985-00155d300101");
        static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
        {
            return new cLiamProviderMsTeams(LiamConfiguration, ProviderData);
        }
    }

    public class cLiamProviderMsTeams : cLiamProviderBase
    {
        public readonly cMsGraphSharepoint MsSharepoint = new cMsGraphSharepoint(new cMsGraphBase());
        public const string allowedMailNickNameCharacter = @"[^A-Za-z0-9!#$%&'*+\-/=?^_`{|}~]";

        public readonly bool WithoutPrivateChannels = true;

        private string lastErrorMessage = null;

        private sealed class GraphPermissionRequirement
        {
            public string Description { get; private set; }

            public string[] AcceptedPermissions { get; private set; }

            public GraphPermissionRequirement(string description, params string[] acceptedPermissions)
            {
                Description = description;
                AcceptedPermissions = acceptedPermissions ?? new string[0];
            }
        }

        private static readonly GraphPermissionRequirement[] RequiredGraphPermissions = new[]
        {
            new GraphPermissionRequirement(
                "Team lesen",
                "Team.ReadBasic.All",
                "Group.Read.All",
                "Group.ReadWrite.All",
                "Directory.Read.All",
                "Directory.ReadWrite.All"),
            new GraphPermissionRequirement(
                "Channels lesen",
                "Channel.ReadBasic.All",
                "ChannelSettings.Read.All",
                "ChannelSettings.ReadWrite.All",
                "Group.Read.All",
                "Group.ReadWrite.All",
                "Directory.Read.All",
                "Directory.ReadWrite.All"),
            new GraphPermissionRequirement(
                "Dateien lesen und schreiben",
                "Files.ReadWrite.All",
                "Sites.ReadWrite.All",
                "Sites.FullControl.All"),
            new GraphPermissionRequirement(
                "Benutzer lesen",
                "User.Read.All",
                "User.ReadWrite.All",
                "Directory.Read.All",
                "Directory.ReadWrite.All"),
        };

        private static readonly GraphPermissionRequirement[] CloneBaseGraphPermissions = new[]
        {
            new GraphPermissionRequirement("Teams klonen", "Team.Create"),
        };

        private static readonly GraphPermissionRequirement[] CloneAppsGraphPermissions = new[]
        {
            new GraphPermissionRequirement("Apps mitklonen", "Application.Read.All", "Application.ReadWrite.All"),
        };

        private static readonly GraphPermissionRequirement[] CloneSettingsGraphPermissions = new[]
        {
            new GraphPermissionRequirement("Team-Einstellungen mitklonen", "TeamSettings.Read.All", "TeamSettings.ReadWrite.All"),
        };

        private static readonly GraphPermissionRequirement[] CloneMemberGraphPermissions = new[]
        {
            new GraphPermissionRequirement(
                "Mitglieder mitklonen",
                "GroupMember.Read.All",
                "GroupMember.ReadWrite.All",
                "Group.Read.All",
                "Group.ReadWrite.All",
                "Directory.Read.All",
                "Directory.ReadWrite.All"),
        };

        private void SetLastError(string message)
        {
            lastErrorMessage = string.IsNullOrWhiteSpace(message) ? null : message;
        }

        public cLiamProviderMsTeams(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
            base(LiamConfiguration, ProviderData)
        {
            WithoutPrivateChannels = AdditionalConfiguration.ContainsKey("WithoutPrivateChannels") ? AdditionalConfiguration["WithoutPrivateChannels"].ToLower() == "true" || AdditionalConfiguration["WithoutPrivateChannels"] == "1" : false;
        }

        public override async Task<bool> LogonAsync()
        {
            return await LogonAsync(true);
        }

        public async Task<bool> LogonAsync(bool force = false)
        {
            if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
            {
                LogEntry($"Error: License not valid", LogLevels.Error);
                SetLastError("License not valid");
                return false;
            }
            if (!force && this.MsSharepoint.Base.IsOnline)
            {
                if (!EnsureGraphPermissions(MsSharepoint.Base?.AccessToken))
                    return false;
                SetLastError(null);
                return true;
            }

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var LI = new cMsGraphLogonInfo() {
                    Tenant = this.Domain,
                    ClientID = this.Credential?.Identification,
                    ClientSecret = this.Credential?.Secret
                };
                var RetVal = await MsSharepoint.Base.LogonAsync(LI);
                if (!RetVal)
                {
                    SetLastError(MsSharepoint.Base?.LastErrorMessage ?? "MsTeams Logon fehlgeschlagen");
                    return false;
                }
                if (!EnsureGraphPermissions(MsSharepoint.Base?.AccessToken))
                    return false;
                SetLastError(null);
                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                SetLastError(E.Message);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            if (string.IsNullOrWhiteSpace(lastErrorMessage))
                SetLastError("MsTeams Logon fehlgeschlagen");

            return false;
        }

        public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int Depth = -1)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!await LogonAsync())
                    return null;

                var DataAreas = new List<cLiamDataAreaBase>();

                var DAL = await MsSharepoint.RequestTeamsListAsync();
                if (DAL == null)
                {
                    SetLastError(MsSharepoint.Base?.LastErrorMessage ?? "Konnte Teams-Liste nicht abrufen");
                    return null;
                }

                foreach (var Entry in DAL)
                {
                    if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(Entry.Key, this.DataAreaRegEx).Success)
                        continue;

                    var MsTeam = await MsSharepoint.RequestGroupInfoAsync(Entry.Value);
                    if (MsTeam == null)
                    {
                        SetLastError(MsSharepoint.Base?.LastErrorMessage ?? $"Konnte Team-Informationen für '{Entry.Key}' nicht abrufen");
                        continue;
                    }

                    var Team = new cLiamMsTeamsTeam(this, MsTeam);
                    DataAreas.Add(Team);
                }

                if (Depth > 0)
                {
                    var allChilds = new List<cLiamDataAreaBase>();
                    foreach (var Entry in DataAreas)
                    {
                        var entryChilds = await (Entry as cLiamMsTeamsTeam).getChannels(true);
                        if (entryChilds != null && entryChilds.Count > 0)
                            allChilds.AddRange(entryChilds);
                    }
                    DataAreas.AddRange(allChilds);
                }

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

            return null;
        }

        private bool EnsureGraphPermissions(string accessToken)
        {
            return EnsureGraphPermissions(accessToken, RequiredGraphPermissions, null);
        }

        private bool EnsureClonePermissions(string accessToken, int partsToClone)
        {
            var requirements = new List<GraphPermissionRequirement>(CloneBaseGraphPermissions);
            var cloneParts = (CloneTeamRequest.ClonableTeamParts)partsToClone;

            if (cloneParts.HasFlag(CloneTeamRequest.ClonableTeamParts.Apps))
                requirements.AddRange(CloneAppsGraphPermissions);
            if (cloneParts.HasFlag(CloneTeamRequest.ClonableTeamParts.Settings))
                requirements.AddRange(CloneSettingsGraphPermissions);
            if (cloneParts.HasFlag(CloneTeamRequest.ClonableTeamParts.Members))
                requirements.AddRange(CloneMemberGraphPermissions);

            return EnsureGraphPermissions(accessToken, requirements, "Team-Klonen");
        }

        private bool EnsureGraphPermissions(string accessToken, IEnumerable<GraphPermissionRequirement> requirements, string operationName)
        {
            if (!TryGetGrantedGraphPermissions(accessToken, out var granted, out var errorMessage))
            {
                SetLastError(errorMessage);
                return false;
            }

            var missing = requirements
                .Where(requirement => requirement.AcceptedPermissions == null || !requirement.AcceptedPermissions.Any(granted.Contains))
                .Select(requirement => $"{requirement.Description} ({string.Join(" / ", requirement.AcceptedPermissions)})")
                .ToList();

            if (missing.Count > 0)
            {
                var prefix = string.IsNullOrWhiteSpace(operationName)
                    ? "Fehlende Graph-Berechtigungen: "
                    : $"Fehlende Graph-Berechtigungen für {operationName}: ";
                SetLastError(prefix + string.Join(", ", missing));
                return false;
            }

            return true;
        }

        private bool TryGetGrantedGraphPermissions(string accessToken, out HashSet<string> granted, out string errorMessage)
        {
            granted = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            errorMessage = null;

            if (string.IsNullOrWhiteSpace(accessToken))
            {
                errorMessage = "Kein Access Token für Berechtigungsprüfung verfügbar";
                return false;
            }

            try
            {
                var parts = accessToken.Split('.');
                if (parts.Length < 2)
                {
                    errorMessage = "Ungültiges Access Token";
                    return false;
                }

                var payload = parts[1].Replace('-', '+').Replace('_', '/');
                switch (payload.Length % 4)
                {
                    case 2: payload += "=="; break;
                    case 3: payload += "="; break;
                }

                var payloadBytes = Convert.FromBase64String(payload);
                var payloadJson = Encoding.UTF8.GetString(payloadBytes);
                var payloadObj = JsonConvert.DeserializeObject<JObject>(payloadJson);
                if (payloadObj == null)
                {
                    errorMessage = "Token-Payload konnte nicht gelesen werden";
                    return false;
                }

                if (payloadObj.TryGetValue("roles", out var rolesToken) && rolesToken is JArray roleArray)
                {
                    foreach (var role in roleArray.Values<string>())
                    {
                        if (!string.IsNullOrWhiteSpace(role))
                            granted.Add(role);
                    }
                }

                if (!granted.Any() && payloadObj.TryGetValue("scp", out var scopeToken))
                {
                    var scopes = scopeToken.Value<string>() ?? string.Empty;
                    foreach (var scope in scopes.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
                        granted.Add(scope);
                }

                if (!granted.Any())
                {
                    errorMessage = "Keine Graph-Berechtigungen im Access Token gefunden";
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                errorMessage = "Berechtigungsprüfung fehlgeschlagen: " + ex.Message;
                return false;
            }
        }

        public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                var lstIds = UID.Split('|');

                switch (lstIds.Length)
                {
                    case 4:
                        var FO = await cLiamMsTeamsFolder.Load(this, UID);
                        return FO;
                    case 2:
                        var CH = await cLiamMsTeamsChannel.Load(this, UID);
                        return CH;
                    case 1:
                        var TM = await cLiamMsTeamsTeam.Load(this, UID);
                        return TM;
                }
                
                return null;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

        public static string getUidItem(ref string UID)
        {
            try
            {
                var p = UID.IndexOf('|');
                if (p >= 0)
                {
                    var Item = UID.Substring(0, p);
                    UID = UID.Remove(0, p + 1);
                    UID = UID.TrimStart();
                    return Item.Trim();
                }


            }
            catch { };

            var h2 = UID;
            UID = "";
            return h2;
        }

        public static cMsGraphResultPermission.ePermissionRole getMsGraphRole(eLiamAccessRoles LiamRole)
        {
            switch (LiamRole)
            {
                case eLiamAccessRoles.Owner:
                    return cMsGraphResultPermission.ePermissionRole.owner;
                case eLiamAccessRoles.Write:
                    return cMsGraphResultPermission.ePermissionRole.write;
                default:
                    return cMsGraphResultPermission.ePermissionRole.read;
            }
        }

        public static eLiamAccessRoles getLiamRole(cMsGraphResultPermission.ePermissionRole MsGraphRole)
        {
            switch (MsGraphRole)
            {
                case cMsGraphResultPermission.ePermissionRole.owner:
                    return eLiamAccessRoles.Owner;
                case cMsGraphResultPermission.ePermissionRole.write:
                    return eLiamAccessRoles.Write;
                default:
                    return eLiamAccessRoles.Read;
            }
        }

        public override Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
        {
            throw new NotImplementedException();
        }

        public override string GetLastErrorMessage()
        {
            if (!string.IsNullOrWhiteSpace(lastErrorMessage))
                return lastErrorMessage;
            return MsSharepoint?.Base?.LastErrorMessage ?? string.Empty;
        }

        public async Task<cMsGraphResultBase> cloneTeam(string teamId, string name, string description, int visibility, int partsToClone, string additionalMembers, string additionalOwners)
        {
            if (!await LogonAsync())
                return null;
            if (!EnsureClonePermissions(MsSharepoint.Base?.AccessToken, partsToClone))
                return null;

            var request = new CloneTeamRequest()
            {
                DisplayName = name,
                Visibility = (CloneTeamRequest.TeamVisibilityType)visibility,
                MailNickname = Regex.Replace(name, allowedMailNickNameCharacter, ""),
                Description = description
            };
            request.SetClonableParts((CloneTeamRequest.ClonableTeamParts)partsToClone);
            var res = await MsSharepoint.CloneTeam(teamId, request);
            return res;
        }
    }

    public class cLiamMsTeamsTeam : cLiamDataAreaBase
    {
        public new readonly cLiamProviderMsTeams Provider = null;

        private readonly cMsGraphResultGroup MsTeam = null;

        public cLiamMsTeamsTeam(cLiamProviderMsTeams Provider, cMsGraphResultGroup MsTeam) :
            base(Provider)
        {
            this.Provider = Provider;
            this.MsTeam = MsTeam;

            this.TechnicalName = getUID(MsTeam.ID);
            this.DisplayName = MsTeam.DisplayName;
            this.Level = 0;
            this.UID = getUID(MsTeam.ID);
            this.ParentUID = "";
            this.DataType = eLiamDataAreaTypes.MsTeamsTeam;
            this.SupportsOwners = true;
            this.SupportsPermissions = true;
        }

        internal static string getUID(string TeamID)
        {
            return $"{TeamID}";
        }

        internal async Task<List<cLiamDataAreaBase>> getChannels(bool OnlyPrivate)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (MsTeam == null)
                    return null;

                var RegExFilter = this.Provider?.DataAreaRegEx;
                var SP = this.Provider?.MsSharepoint;
                var WithoutPrivateChannels = this.Provider?.WithoutPrivateChannels ?? false;

                if (MsTeam.Channels == null)
                    if (SP == null || !await MsTeam.ResolveChannels(SP, OnlyPrivate))
                        return null;

                var RetVal = new List<cLiamDataAreaBase>(MsTeam.Channels.Count);
                foreach (var Entry in MsTeam.Channels.Values)
                {
                    if (!string.IsNullOrEmpty(RegExFilter) && !Regex.Match(Entry.DisplayName, RegExFilter).Success)
                        continue;
                    if (WithoutPrivateChannels && !string.IsNullOrEmpty(Entry.MembershipType) && Entry.MembershipType == "private")
                        continue;

                    var DA = new cLiamMsTeamsChannel(Provider, this, Entry);
                    RetVal.Add(DA);
                }

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

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                var RetVal = await getChannels(false);
                if (RetVal != null)
                    RetVal.AddRange(await getChildrenFromListAsync(RetVal, Depth));

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

        public static async Task<cLiamMsTeamsTeam> Load(cLiamProviderMsTeams Provider, string UID)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var arrIDs = UID.Split('|');
                if (arrIDs.Length != 1)
                    return null;

                var TeamID = cLiamProviderMsTeams.getUidItem(ref UID);

                var T = await Provider.MsSharepoint.RequestGroupInfoAsync(TeamID);
                if (T == null)
                    return null;

                var RetVal = new cLiamMsTeamsTeam(Provider, T);

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


        }

        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
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                var SP = this.Provider?.MsSharepoint;
                if (SP == null)
                {
                    LogEntry($"Could not get Sharepoint class from Provider for Team '{this.TechnicalName}'", LogLevels.Warning);
                    return null;
                }

                cMsGraphCollectionUsers lstMembers;
                if(owners)
                    lstMembers = await MsTeam.GetOwnersAsync(SP);
                else
                    lstMembers = await MsTeam.GetMembersAsync(SP);
                if (lstMembers == null)
                {
                    LogEntry($"Could not get owner list for Team '{this.TechnicalName}'", LogLevels.Warning);
                    return null;
                }

                var RetVal = new List<cLiamUserInfo>(lstMembers.Count);
                LogEntry($"Owners for Team found: {lstMembers.Count}", LogLevels.Debug);
                foreach (var MemberEntry in lstMembers.Values)
                {
                    var User = new cLiamUserInfo()
                    {
                        DisplayName = MemberEntry.DisplayName,
                        GivenName = MemberEntry.GivenName,
                        SurName = MemberEntry.SurName,
                        UserPrincipalName = MemberEntry.UserPrincipalName,
                        EMail = MemberEntry.EMail,
                        SID = null
                    };
                    RetVal.Add(User);
                }

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

        public override async Task<List<cLiamPermissionInfo>> GetPermissionsAsync(bool force = false)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                var RetVal = new List<cLiamPermissionInfo>();
                var owners = await GetOwnersAsync();
                foreach ( var owner in owners)
                {
                    var permission = new cLiamPermissionInfo()
                    {
                        User = owner,
                        AccessRole = eLiamAccessRoles.Owner
                    };
                    RetVal.Add(permission);
                }
                var members = await GetMembersAsync(false);
                foreach (var member in members)
                {
                    var permission = new cLiamPermissionInfo()
                    {
                        User = member,
                        AccessRole = eLiamAccessRoles.Write
                    };
                    RetVal.Add(permission);
                }
                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return null;
        }
        public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
            {
                LogEntry($"Error: License not valid", LogLevels.Error);
                return null;
            }
            var result = new cLiamPermissionResult();

            if (Role == eLiamAccessRoles.Read)
            {
                return result;
            }
            try
            {
                var Roles = new List<cMsGraphResultPermission.ePermissionRole>()
                {
                    cLiamProviderMsTeams.getMsGraphRole(Role)
                };
               
                var UserInfo = await Provider.MsSharepoint.RequestUserInfoAsync(User.UserPrincipalName);
                if(string.IsNullOrEmpty(UserInfo?.ID))
                {
                    LogEntry($"Could not find user {User.UserPrincipalName}", LogLevels.Error);
                    return null;
                }
                var retVal = await Provider.MsSharepoint.AddGroupMembership(MsTeam, false, UserInfo);
                if(Role == eLiamAccessRoles.Owner)
                    retVal = await Provider.MsSharepoint.AddGroupMembership(MsTeam, true, UserInfo);
                result.Valid = retVal;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
            return result;
        }

        public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
            {
                LogEntry($"Error: License not valid", LogLevels.Error);
                return false;
            }
            if (Role == eLiamAccessRoles.Read)
            {
                return false;
            }
            try
            {
                var Roles = new List<cMsGraphResultPermission.ePermissionRole>()
                {
                    cLiamProviderMsTeams.getMsGraphRole(Role)
                };

                var UserInfo = await Provider.MsSharepoint.RequestUserInfoAsync(User.UserPrincipalName);
                if (string.IsNullOrEmpty(UserInfo?.ODataId))
                {
                    LogEntry($"Could not find user {User.UserPrincipalName}", LogLevels.Error);
                    return false;
                }
                bool retVal = false;
                if (Role == eLiamAccessRoles.Owner)
                {
                    retVal = await Provider.MsSharepoint.DeleteGroupMembership(MsTeam, true, UserInfo);
                    retVal &= await Provider.MsSharepoint.DeleteGroupMembership(MsTeam, false, UserInfo);
                }
                if (Role == eLiamAccessRoles.Write)
                {
                    var members = await Provider.MsSharepoint.RequestGroupOwnersAsync(MsTeam.ID, UserInfo.ID);
                    if(members == null || members.Count == 0)
                    {
                        retVal = await Provider.MsSharepoint.DeleteGroupMembership(MsTeam, false, UserInfo);
                    }
                }
                return retVal;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
            return false;
        }

    }

    public class cLiamMsTeamsChannel : cLiamDataAreaBase
    {
        public new readonly cLiamProviderMsTeams Provider = null;
        public readonly cMsGraphResultChannel MsChannel = null;

        public cLiamMsTeamsChannel(cLiamProviderMsTeams Provider, cLiamMsTeamsTeam Team, cMsGraphResultChannel MsChannel, string TeamUID = null) : base(Provider)
        {
            this.Provider = Provider;
            this.MsChannel = MsChannel;

            this.TechnicalName = getUID(MsChannel.ID, TeamUID ?? Team?.UID ?? "*");
            this.DisplayName = MsChannel.DisplayName;
            this.Level = 1;
            this.UID = getUID(MsChannel.ID, TeamUID ?? Team?.UID ?? "*");
            if (Team != null)
                this.ParentUID = Team.UID;
            this.DataType = eLiamDataAreaTypes.MsTeamsChannel;
        }

        internal static string getUID(string ChannelID, string TeamUID)
        {
            return $"{ChannelID} | {TeamUID}";
        }

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                if (MsChannel == null)
                    return null;

                var RegExFilter = this.Provider?.DataAreaRegEx;
                var SP = this.Provider?.MsSharepoint;

                if (MsChannel.RootFolder == null)
                    if (SP == null || !await MsChannel.ResolveRootFolder(SP))
                        return null;

                if (MsChannel.RootFolder.Folders == null)
                    if (SP == null || !await MsChannel.RootFolder.ResolveFolders(SP))
                        return null;

                var DataAreas = new List<cLiamDataAreaBase>();
                foreach (var Entry in MsChannel.RootFolder.Folders.Values)
                {
                    if (!string.IsNullOrEmpty(RegExFilter) && !Regex.Match(Entry.DisplayName, RegExFilter).Success)
                        continue;

                    var DA = new cLiamMsTeamsFolder(this, Entry);
                    DataAreas.Add(DA);
                }
                DataAreas.AddRange(await getChildrenFromListAsync(DataAreas, Depth));

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

        public static async Task<cLiamMsTeamsChannel> Load(cLiamProviderMsTeams Provider, string UID)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var arrIDs = UID.Split('|');
                if (arrIDs.Length != 2)
                    return null;

                var ChannelID = cLiamProviderMsTeams.getUidItem(ref UID);
                var TeamID = cLiamProviderMsTeams.getUidItem(ref UID);

                var C = await Provider.MsSharepoint.RequestChannelInfo(TeamID, ChannelID);
                if (C == null)
                    return null;

                var RetVal = new cLiamMsTeamsChannel(Provider, null, C, TeamID);

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


        }
    }

    public class cLiamMsTeamsFolder : cLiamDataAreaBase
    {
        public new readonly cLiamProviderMsTeams Provider = null;
        public readonly cLiamMsTeamsChannel Channel = null;
        public readonly string ChannelUID = null;
        public readonly cMsGraphResultFileObject MsFolder = null;


        public cLiamMsTeamsFolder(cLiamMsTeamsChannel Channel, cMsGraphResultFileObject MsFolder) : this(Channel.Provider, Channel, Channel, MsFolder) { }

        public cLiamMsTeamsFolder(cLiamMsTeamsFolder ParentFolder, cMsGraphResultFileObject MsFolder) : this(ParentFolder.Channel.Provider, ParentFolder.Channel, ParentFolder, MsFolder) { }

        private cLiamMsTeamsFolder(cLiamProviderMsTeams Provider, cLiamMsTeamsChannel Channel, cLiamDataAreaBase ParentFolder, cMsGraphResultFileObject MsFolder, string ChannelUID = null) : base(Provider)
        {
            this.Channel = Channel;
            if (Channel == null)
                this.ChannelUID = ChannelUID;
            else
                this.ChannelUID = Channel.UID;
            this.Provider = Provider;
            this.MsFolder = MsFolder;

            this.TechnicalName = MsFolder.DisplayName;
            this.DisplayName = MsFolder.DisplayName;
            if (ParentFolder != null)
            {
                this.Level = ParentFolder.Level + 1;
                this.ParentUID = ParentFolder.UID;
            }
            this.UID = getUID(MsFolder.ID, MsFolder.DriveID, ChannelUID ?? Channel?.UID ?? "*|*");
            this.DataType = eLiamDataAreaTypes.MsTeamsFolder;
            this.SupportsPermissions = true;
        }

        internal static string getUID(string FolderID, string DriveID, string ChannelUID)
        {
            return $"{FolderID} | {DriveID} | {ChannelUID}";
        }

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                if (MsFolder == null)
                    return null;

                var RegExFilter = this.Provider?.DataAreaRegEx;
                var SP = this.Provider?.MsSharepoint;

                if (MsFolder.Folders == null)
                    if (SP == null || !await MsFolder.ResolveFolders(SP))
                        return null;

                var DataAreas = new List<cLiamDataAreaBase>();
                foreach (var Entry in MsFolder.Folders.Values)
                {
                    if (!string.IsNullOrEmpty(RegExFilter) && !Regex.Match(Entry.DisplayName, RegExFilter).Success)
                        continue;

                    var DA = new cLiamMsTeamsFolder(this.Provider, this.Channel, this, Entry, this.ChannelUID);
                    DataAreas.Add(DA);
                }
                var CL = await getChildrenFromListAsync(DataAreas, Depth);
                if (CL != null)
                    DataAreas.AddRange(CL);

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

        public static async Task<cLiamMsTeamsFolder> Load(cLiamProviderMsTeams Provider, string UID)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var arrIDs = UID.Split('|');
                if (arrIDs.Length != 4)
                    return null;

                var FolderID = cLiamProviderMsTeams.getUidItem(ref UID);
                var DriveID = cLiamProviderMsTeams.getUidItem(ref UID);

                var F = await Provider.MsSharepoint.RequestFileObjectInfo(DriveID, FolderID);
                if (F == null)
                    return null;

                var RetVal = new cLiamMsTeamsFolder(Provider, null, null, F, UID);

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


        }

        public override async Task<List<cLiamPermissionInfo>> GetPermissionsAsync(bool force = false)
        {   
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                var RetVal = new List<cLiamPermissionInfo>();
                if (this.MsFolder.Permissions == null || force)
                    if (!await this.MsFolder.ResolvePermissions(Provider.MsSharepoint))
                        return null;

                if (this.MsFolder.Permissions == null)
                    return null;

                foreach (var Perm in this.MsFolder.Permissions)
                {
                    if (!(Perm is cMsGraphResultPermission.PermissionDirect) && !(Perm is cMsGraphResultPermission.PermissionLink))
                        continue;

                    if (Perm.isInherited)
                        continue;



                    if (Perm.Identities != null)
                    {
                        foreach (var Role in Perm.Roles)
                        {
                            var AccessRole = cLiamProviderMsTeams.getLiamRole(Role);

                            foreach (var Ident in Perm.Identities)
                            {
                                if (Ident.IdentityType != cMsGraphResultPermission.cMsGraphPermissionIdentity.eIdentityType.user)
                                    continue;

                                if (string.IsNullOrEmpty(Ident.EMail))
                                    continue;

                                if (!string.IsNullOrEmpty(Ident.ID))
                                {
                                    var GroupInfo = await Provider.MsSharepoint.RequestGroupInfoAsync(Ident.ID);
                                    if (GroupInfo != null)
                                        continue;
                                }

                                var Usr = Ident.EMail.ToLowerInvariant();
                                var alreadyFound = false;
                                foreach (var Entry in RetVal)
                                {
                                    if (Usr == Entry.User.EMail.ToLowerInvariant())
                                        if (AccessRole == Entry.AccessRole)
                                        {
                                            alreadyFound = true;
                                            break;
                                        }

                                }

                                if (!alreadyFound)
                                {
                                    var UsrInfo = new cLiamUserInfo()
                                    {
                                        DisplayName = Ident.DisplayName,
                                        EMail = Ident.EMail,
                                        UserPrincipalName = Ident.EMail
                                    };
                                    var PermInfo = new cLiamPermissionInfo()
                                    {
                                        User = UsrInfo,
                                        AccessRole = AccessRole
                                    };
                                    RetVal.Add(PermInfo);
                                }
                            }
                        }
                    }
                }

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

            return null;
        }

        public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return null;
                }
                var Roles = new List<cMsGraphResultPermission.ePermissionRole>()
                {
                    cLiamProviderMsTeams.getMsGraphRole(Role)
                };
                var RetVal = await this.MsFolder.AddPermission(Provider.MsSharepoint, Roles, cMsGraphResultPermission.eDriveReceipientType.email, User.EMail);
                if (RetVal == null)
                    return null;

                var RetVal2 = new cLiamPermissionResult();
                if (RetVal.Count>= 0)
                {
                    RetVal2.Valid = true;
                    var Perm = RetVal[0];
                    if (Perm is cMsGraphResultPermission.PermissionLink Link)
                    {
                        RetVal2.UserReference = Link.WebUrl;
                    }
                    else
                        RetVal2.UserReference = this.MsFolder.WebUrl;
                }
                
                return RetVal2;
            }
            catch (Exception E)
            {
                LogException(E);
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

        public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.msTeamsModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return false;
                }
                var myRole = cLiamProviderMsTeams.getMsGraphRole(Role);
                var RetVal = await this.MsFolder.RemovePermission(Provider.MsSharepoint, myRole, User.EMail);
                return RetVal;
            }
            catch (Exception E)
            {
                LogException(E);
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

    }
}
