﻿using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Linq;
using System.Reflection;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using System.Threading.Tasks;

namespace C4IT.LIAM
{
    /// <summary>
    /// Manages Exchange operations using PowerShell remoting
    /// </summary>
    public class ExchangeManager
    {
        private readonly string _exchangeUri;
        private readonly PSCredential _credential;
        private readonly string _organizationalUnit;

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

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

                // Set authentication mechanism to Kerberos by default
                connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;

                var runspace = RunspaceFactory.CreateRunspace(connectionInfo);
                runspace.Open();
                return runspace;
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Creates a distribution group (mailing list). Optionally members can be added initially.
        /// </summary>
        /// <param name="name">Name of the group</param>
        /// <param name="alias">Alias of the group</param>
        /// <param name="initialMembers">List of members to be added to the group (optional)</param>
        public void CreateDistributionGroup(string name, string alias, List<string> initialMembers = null)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;

                    // Create the distribution group
                    ps.AddCommand("New-DistributionGroup")
                      .AddParameter("Name", name)
                      .AddParameter("Alias", alias)
                      .AddParameter("OrganizationalUnit", _organizationalUnit);
                    ps.Invoke();

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

        /// <summary>
        /// Creates a security group in Active Directory that can be used for Exchange permissions
        /// </summary>
        /// <param name="name">Name of the security group</param>
        /// <param name="description">Description of the security group</param>
        /// <returns>True if successful</returns>
        public bool CreateSecurityGroup(string name, string description)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;

                    // Create the security group using New-ADGroup cmdlet
                    ps.AddCommand("New-ADGroup")
                      .AddParameter("Name", name)
                      .AddParameter("GroupScope", "Universal")
                      .AddParameter("GroupCategory", "Security")
                      .AddParameter("DisplayName", name)
                      .AddParameter("Path", _organizationalUnit)
                      .AddParameter("Description", description);

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

        /// <summary>
        /// Gets all shared mailboxes matching an optional filter
        /// </summary>
        /// <param name="filter">Optional filter string</param>
        /// <returns>Collection of shared mailboxes as PSObjects</returns>
        public IEnumerable<PSObject> GetSharedMailboxes(string filter = null)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-Mailbox")
                      .AddParameter("RecipientTypeDetails", "SharedMailbox");

                    if (!string.IsNullOrEmpty(filter))
                    {
                        ps.AddParameter("Filter", filter);
                    }

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

        /// <summary>
        /// Gets a shared mailbox by primary SMTP address
        /// </summary>
        /// <param name="primarySmtpAddress">The primary SMTP address of the mailbox</param>
        /// <returns>The mailbox as a PSObject, or null if not found</returns>
        public PSObject GetSharedMailboxByAddress(string primarySmtpAddress)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-Mailbox")
                      .AddParameter("RecipientTypeDetails", "SharedMailbox")
                      .AddParameter("PrimarySmtpAddress", primarySmtpAddress);

                    var results = ps.Invoke();
                    return results.Count > 0 ? results[0] : null;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return null;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Gets all distribution groups matching an optional filter
        /// </summary>
        /// <param name="filter">Optional filter string</param>
        /// <returns>Collection of distribution groups as PSObjects</returns>
        public IEnumerable<PSObject> GetDistributionGroups(string filter = null)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-DistributionGroup");

                    if (!string.IsNullOrEmpty(filter))
                    {
                        ps.AddParameter("Filter", filter);
                    }

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

        /// <summary>
        /// Gets a distribution group by primary SMTP address
        /// </summary>
        /// <param name="primarySmtpAddress">The primary SMTP address of the group</param>
        /// <returns>The distribution group as a PSObject, or null if not found</returns>
        public PSObject GetDistributionGroupByAddress(string primarySmtpAddress)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-DistributionGroup")
                      .AddParameter("PrimarySmtpAddress", primarySmtpAddress);

                    var results = ps.Invoke();
                    return results.Count > 0 ? results[0] : null;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                return null;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Gets members of a distribution group
        /// </summary>
        /// <param name="groupName">The name of the distribution group</param>
        /// <returns>Collection of members as PSObjects</returns>
        public IEnumerable<PSObject> GetDistributionGroupMembers(string groupName)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-DistributionGroupMember")
                      .AddParameter("Identity", groupName);

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

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

                    // Filter out default permissions like NT AUTHORITY\SELF
                    var results = ps.Invoke().Where(p =>
                        !p.Properties["User"].Value.ToString().Contains("NT AUTHORITY") &&
                        !p.Properties["User"].Value.ToString().Contains("Administrator"));

                    // Now, get the full user objects for each permission
                    var userList = new List<PSObject>();
                    using (var ps2 = PowerShell.Create())
                    {
                        ps2.Runspace = runspace;
                        foreach (var perm in results)
                        {
                            ps2.Commands.Clear();
                            ps2.AddCommand("Get-User")
                              .AddParameter("Identity", perm.Properties["User"].Value.ToString());

                            var users = ps2.Invoke();
                            if (users.Count > 0)
                            {
                                userList.Add(users[0]);
                            }
                        }
                    }

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

        /// <summary>
        /// Gets security groups matching a filter
        /// </summary>
        /// <param name="filter">LDAP filter to match groups</param>
        /// <returns>Collection of security groups as PSObjects</returns>
        public IEnumerable<PSObject> GetSecurityGroups(string filter)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Get-ADGroup")
                      .AddParameter("Filter", filter)
                      .AddParameter("Properties", new string[] { "DisplayName", "DistinguishedName", "Sid" });

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

        /// <summary>
        /// Adds a member to a distribution group
        /// </summary>
        /// <param name="groupName">Name of the distribution group</param>
        /// <param name="member">User to be added as a member</param>
        public void AddMemberToDistributionGroup(string groupName, string member)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Add-DistributionGroupMember")
                      .AddParameter("Identity", groupName)
                      .AddParameter("Member", member);

                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Removes a member from a distribution group
        /// </summary>
        /// <param name="groupName">Name of the distribution group</param>
        /// <param name="member">User to be removed</param>
        public void RemoveMemberFromDistributionGroup(string groupName, string member)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Remove-DistributionGroupMember")
                      .AddParameter("Identity", groupName)
                      .AddParameter("Member", member)
                      .AddParameter("Confirm", false);

                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Adds mailbox permission for a user
        /// </summary>
        /// <param name="mailboxName">Name of the mailbox</param>
        /// <param name="user">User or group to be granted permission</param>
        /// <param name="accessRight">Access right (default: FullAccess)</param>
        public void AddMailboxPermission(string mailboxName, string user, string accessRight = "FullAccess")
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Add-MailboxPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("AccessRights", accessRight)
                      .AddParameter("InheritanceType", "All");

                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Removes mailbox permission for a user
        /// </summary>
        /// <param name="mailboxName">Name of the mailbox</param>
        /// <param name="user">User or group to have permission removed</param>
        /// <param name="accessRight">Access right (default: FullAccess)</param>
        public void RemoveMailboxPermission(string mailboxName, string user, string accessRight = "FullAccess")
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Remove-MailboxPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("AccessRights", accessRight)
                      .AddParameter("InheritanceType", "All")
                      .AddParameter("Confirm", false);

                    ps.Invoke();
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
            finally
            {
                LogMethodEnd(MethodBase.GetCurrentMethod());
            }
        }

        /// <summary>
        /// Adds Send As permission for a mailbox
        /// </summary>
        /// <param name="mailboxName">Name of the mailbox</param>
        /// <param name="user">User or group to be granted permission</param>
        /// <returns>True if successful</returns>
        public async Task<bool> AddSendAsPermission(string mailboxName, string user)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Add-ADPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("ExtendedRights", "Send-As")
                      .AddParameter("AccessRights", "ExtendedRight");

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

        /// <summary>
        /// Removes Send As permission for a mailbox
        /// </summary>
        /// <param name="mailboxName">Name of the mailbox</param>
        /// <param name="user">User or group to have permission removed</param>
        /// <returns>True if successful</returns>
        public async Task<bool> RemoveSendAsPermission(string mailboxName, string user)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddCommand("Remove-ADPermission")
                      .AddParameter("Identity", mailboxName)
                      .AddParameter("User", user)
                      .AddParameter("ExtendedRights", "Send-As")
                      .AddParameter("AccessRights", "ExtendedRight")
                      .AddParameter("Confirm", false);

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


        /// <summary>
        /// Creates a shared mailbox. Optionally initial permissions (e.g. FullAccess) can be set for members.
        /// </summary>
        /// <param name="name">Name of the mailbox</param>
        /// <param name="alias">Alias of the mailbox</param>
        /// <param name="initialMembers">List of users to be added as access rights (optional)</param>
        public void CreateSharedMailbox(string name, string alias, List<string> initialMembers = null)
        {
            LogMethodBegin(MethodBase.GetCurrentMethod());
            try
            {
                using (var runspace = CreateRunspace())
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;

                    // Create new shared mailbox
                    ps.AddCommand("New-Mailbox")
                      .AddParameter("Name", name)
                      .AddParameter("Alias", alias)
                      .AddParameter("Shared", true)
                      .AddParameter("OrganizationalUnit", _organizationalUnit);
                    ps.Invoke();

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

    }
}