using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

using C4IT.Logging;
using C4IT.Matrix42.ServerInfo;
using static C4IT.Logging.cLogManager;

namespace C4IT.LIAM
{
    // Modifikation der getDataAreasAsync Methode im Exchange-Provider
    // Diese Erweiterung stellt sicher, dass die Daten im gleichen Format wie andere Provider zurückgegeben werden
    public partial class cLiamProviderExchange
    {
        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(LiamInitializer.exchangeModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return new List<cLiamDataAreaBase>();
                }

                if (!await LogonAsync())
                    return null;

                var DataAreas = new List<cLiamDataAreaBase>();

                // Get Shared Mailboxes
                var sharedMailboxes = _exchangeManager.GetSharedMailboxes();
                foreach (var mailbox in sharedMailboxes)
                {
                    string displayName = mailbox.Properties["DisplayName"]?.Value?.ToString() ?? "N/A";
                    string email = mailbox.Properties["PrimarySmtpAddress"]?.Value?.ToString() ?? "N/A";
                    string alias = mailbox.Properties["Alias"]?.Value?.ToString() ?? "N/A";

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

                    // Erstellen von zwei AD-Gruppen für jede Mailbox (FullAccess und SendAs)
                    string fullAccessGroupName = $"{alias}_FullAccess";
                    string sendAsGroupName = $"{alias}_SendAs";

                    // Annahme: Gruppen werden angelegt wenn sie nicht existieren
                    // Dies würde in einer realen Implementierung durch einen Aufruf der entsprechenden Exchange/AD-Funktionen erfolgen
                    LogEntry($"Creating/verifying AD groups for mailbox {alias}: {fullAccessGroupName} and {sendAsGroupName}", LogLevels.Info);

                    // Erstellen des SharedMailbox-Objekts mit Verweisen auf die Gruppen
                    var sharedMailbox = new cLiamExchangeSharedMailbox(this, new cExchangeResultSharedMailbox
                    {
                        DisplayName = displayName,
                        EmailAddress = email,
                        Alias = alias,
                        PSObject = mailbox,
                        // Die Owner- und Write-Felder mit den AD-Gruppen füllen
                        OwnerGroup = fullAccessGroupName,  // FullAccess-Gruppe als Owner
                        WriteGroup = sendAsGroupName        // SendAs-Gruppe als Write
                    });

                    DataAreas.Add(sharedMailbox);
                }

                // Get Distribution Groups
                var distributionGroups = _exchangeManager.GetDistributionGroups();
                foreach (var group in distributionGroups)
                {
                    string displayName = group.Properties["DisplayName"]?.Value?.ToString() ?? "N/A";
                    string email = group.Properties["PrimarySmtpAddress"]?.Value?.ToString() ?? "N/A";
                    string alias = group.Properties["Alias"]?.Value?.ToString() ?? "N/A";

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

                    var distributionGroup = new cLiamExchangeDistributionGroup(this, new cExchangeResultDistributionGroup
                    {
                        DisplayName = displayName,
                        EmailAddress = email,
                        Alias = alias,
                        PSObject = group,
                        // Die Owner- und Write-Felder mit der AD-Gruppe füllen
                        OwnerGroup = $"{alias}_Members",  // Die Gruppe selbst als Owner
                        WriteGroup = $"{alias}_Members"   // Die Gruppe selbst als Write
                    });

                    DataAreas.Add(distributionGroup);
                }

                return DataAreas;
            }
            catch (Exception E)
            {
                LogException(E);
                LastException = E;
                LastErrorMessage = $"Failed to get Exchange data areas: {E.Message}";
                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(LiamInitializer.exchangeModuleId))
                {
                    LogEntry($"Error: License not valid", LogLevels.Error);
                    return new List<cLiamDataAreaBase>();
                }

                if (!await LogonAsync())
                    return null;

                var securityGroups = new List<cLiamDataAreaBase>();

                // Für Exchange müssen wir die AD-Gruppen abrufen
                // Da wir keinen direkten AD-Zugriff haben, werden wir die Distribution Groups als Beispiel verwenden
                // In einer realen Implementierung würde hier ein AD-Zugriff stattfinden

                var distributionGroups = _exchangeManager.GetDistributionGroups();
                foreach (var group in distributionGroups)
                {
                    string displayName = group.Properties["DisplayName"]?.Value?.ToString() ?? "N/A";
                    string alias = group.Properties["Alias"]?.Value?.ToString() ?? "N/A";

                    // Hier müssten wir eigentlich prüfen, ob die Gruppe dem Filter entspricht
                    // Da wir keinen echten LDAP-Filter haben, überprüfen wir einfach auf einen Stringvergleich
                    if (!string.IsNullOrEmpty(groupFilter) && !displayName.Contains(groupFilter) && !alias.Contains(groupFilter))
                        continue;

                    // Zusätzlich zu den eigentlichen Distribution Groups fügen wir auch die assoziierten AD-Gruppen hinzu
                    var adGroup = new cLiamExchangeSecurityGroup(this, new cExchangeResultSecurityGroup
                    {
                        DisplayName = displayName,
                        Alias = alias,
                        PSObject = group,
                        DN = $"CN={alias},OU=Exchange Groups,DC=example,DC=com", // Beispiel DN
                        SID = Guid.NewGuid().ToString() // Beispiel SID
                    });

                    securityGroups.Add(adGroup);

                    // Füge auch FullAccess- und SendAs-Gruppen hinzu
                    var fullAccessGroup = new cLiamExchangeSecurityGroup(this, new cExchangeResultSecurityGroup
                    {
                        DisplayName = $"{displayName} FullAccess",
                        Alias = $"{alias}_FullAccess",
                        PSObject = group,
                        DN = $"CN={alias}_FullAccess,OU=Exchange Groups,DC=example,DC=com", // Beispiel DN
                        SID = Guid.NewGuid().ToString(), // Beispiel SID
                        Scope = "DomainLocal"
                    });

                    var sendAsGroup = new cLiamExchangeSecurityGroup(this, new cExchangeResultSecurityGroup
                    {
                        DisplayName = $"{displayName} SendAs",
                        Alias = $"{alias}_SendAs",
                        PSObject = group,
                        DN = $"CN={alias}_SendAs,OU=Exchange Groups,DC=example,DC=com", // Beispiel DN
                        SID = Guid.NewGuid().ToString(), // Beispiel SID
                        Scope = "Global"
                    });

                    securityGroups.Add(fullAccessGroup);
                    securityGroups.Add(sendAsGroup);
                }

                return securityGroups;
            }
            catch (Exception E)
            {
                LogException(E);
                LastException = E;
                LastErrorMessage = $"Failed to get security groups: {E.Message}";
                return null;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        // Methode zum Erstellen einer Shared Mailbox mit assoziierten AD-Gruppen
        public async Task<bool> CreateSharedMailboxWithGroups(string name, string alias, string userPrincipalName, string organizationalUnit = null)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!await LogonAsync())
                    return false;

                // 1. Erstelle die Shared Mailbox
                _exchangeManager.CreateSharedMailbox(name, alias, userPrincipalName, organizationalUnit);

                // 2. Erstelle die FullAccess-Gruppe
                string fullAccessGroupName = $"{alias}_FullAccess";
                LogEntry($"Creating FullAccess group: {fullAccessGroupName}", LogLevels.Info);
                _exchangeManager.CreateDistributionGroup(
                    $"{name} FullAccess",
                    fullAccessGroupName,
                    organizationalUnit);

                // 3. Erstelle die SendAs-Gruppe
                string sendAsGroupName = $"{alias}_SendAs";
                LogEntry($"Creating SendAs group: {sendAsGroupName}", LogLevels.Info);
                _exchangeManager.CreateDistributionGroup(
                    $"{name} SendAs",
                    sendAsGroupName,
                    organizationalUnit);

                // 4. Konfiguriere die Berechtigungen
                _exchangeManager.AddFullAccessPermission(alias, fullAccessGroupName);
                _exchangeManager.AddSendAsPermission(alias, sendAsGroupName);

                return true;
            }
            catch (Exception E)
            {
                LogException(E);
                LastException = E;
                LastErrorMessage = $"Failed to create shared mailbox with groups: {E.Message}";
                return false;
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
    }

    // Erweiterung der Exchange-Result-Klassen um AD-Gruppen-Informationen
    public class cExchangeResultSharedMailbox : cExchangeResultBase
    {
        public string OwnerGroup { get; set; } // FullAccess-Gruppe
        public string WriteGroup { get; set; } // SendAs-Gruppe
    }

    public class cExchangeResultDistributionGroup : cExchangeResultBase
    {
        public string OwnerGroup { get; set; } // Members/Owner-Gruppe
        public string WriteGroup { get; set; } // Members-Gruppe
    }

    public class cExchangeResultSecurityGroup : cExchangeResultBase
    {
        public string DN { get; set; }        // Distinguished Name
        public string SID { get; set; }       // Security Identifier
        public string Scope { get; set; }     // Global, DomainLocal, etc.
    }

    // Erweiterung der cLiamExchangeSharedMailbox-Klasse
    public partial class cLiamExchangeSharedMailbox : cLiamDataAreaBase
    {
        private readonly cExchangeResultSharedMailbox Mailbox;

        public cLiamExchangeSharedMailbox(cLiamProviderExchange Provider, cExchangeResultSharedMailbox Mailbox) :
            base(Provider)
        {
            this.Provider = Provider;
            this.Mailbox = Mailbox;

            this.DisplayName = Mailbox.DisplayName;
            this.TechnicalName = Mailbox.EmailAddress;
            this.UID = $"SharedMailbox|{Mailbox.Alias}";
            this.Level = 0;
            this.DataType = eLiamDataAreaTypes.Unknown; // No specific type for Exchange in the enum
            this.SupportsPermissions = true;

            // Setze Owner- und WriteGroupIdentifier für die Kompatibilität mit bestehenden Aktivitäten
            this.OwnerRef = Mailbox.OwnerGroup;
        }

        // Getter-Methoden für die AD-Gruppen
        public string GetFullAccessGroup()
        {
            return Mailbox.OwnerGroup;
        }

        public string GetSendAsGroup()
        {
            return Mailbox.WriteGroup;
        }
    }

    // Klasse für Exchange Security Groups (AD-Gruppen)
    public class cLiamExchangeSecurityGroup : cLiamDataAreaBase
    {
        private readonly cExchangeResultSecurityGroup Group;

        public cLiamExchangeSecurityGroup(cLiamProviderExchange Provider, cExchangeResultSecurityGroup Group) :
            base(Provider)
        {
            this.Provider = Provider;
            this.Group = Group;

            this.DisplayName = Group.DisplayName;
            this.TechnicalName = Group.Alias;
            this.UID = Group.SID;
            this.Level = 0;
            this.DataType = eLiamDataAreaTypes.ActiveDirectoryGroup;
            this.SupportsPermissions = false;
        }

        // Eigenschaften für Kompatiblität mit dem AD-Provider
        public string dn => Group.DN;
        public string scope => Group.Scope ?? "Unknown";

        public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
        {
            await Task.Delay(0); // Just to make the method async
            return new List<cLiamDataAreaBase>(); // AD-Gruppen haben keine Kinder
        }
    }
}