using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.DirectoryServices;
using System.DirectoryServices.Protocols;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace FineUIPro.Web
{

    public sealed class LdapAuthentication
    {
        private bool _allowAnyCertificates;

        private string _domain;

        private string _path;

        private string _userNameProxyUser;

        private string _passwordProxyUser;

        private AuthenticationTypes _authenticationType;

        public static readonly string LdapUriString = "LDAP";

        public static readonly int LdapPort = 389;

        public static readonly int LdapPortSsl = 636;

        private X509Certificate _locallyVerificationCertificate;

        private string _locallyVerificationCertificateSerialNumber;

        private bool _useSecureSocketLayer;

        private bool _verifyCertificateLocally;

        private LdapDirectoryIdentifier _directoryIdentifier;

        public bool AllowAnyCertificates
        {
            get
            {
                return _allowAnyCertificates;
            }
            set
            {
                _allowAnyCertificates = value;
            }
        }

        public string Domain
        {
            get
            {
                return _domain;
            }
            set
            {
                _domain = value;
            }
        }

        public string Path
        {
            get
            {
                return _path;
            }
            set
            {
                _path = value;
            }
        }

        public string UserNameProxyUser
        {
            get
            {
                return _userNameProxyUser;
            }
            set
            {
                _userNameProxyUser = value;
            }
        }

        public string PasswordProxyUser
        {
            get
            {
                return _passwordProxyUser;
            }
            set
            {
                _passwordProxyUser = value;
            }
        }

        public AuthenticationTypes AuthenticationType
        {
            get
            {
                return _authenticationType;
            }
            set
            {
                _authenticationType = value;
            }
        }

        public X509Certificate LocallyVerificationCertificate
        {
            get
            {
                return _locallyVerificationCertificate;
            }
            set
            {
                _locallyVerificationCertificate = value;
            }
        }

        public string LocallyVerificationCertificateSerialNumber
        {
            get
            {
                return _locallyVerificationCertificateSerialNumber;
            }
            set
            {
                _locallyVerificationCertificateSerialNumber = value;
            }
        }

        public bool UseSecureSocketLayer
        {
            get
            {
                return _useSecureSocketLayer;
            }
            set
            {
                lock (this)
                {
                    _useSecureSocketLayer = value;
                }
            }
        }

        public bool VerifyCertificateLocally
        {
            get
            {
                return _verifyCertificateLocally;
            }
            set
            {
                lock (this)
                {
                    _verifyCertificateLocally = value;
                }
            }
        }

        public LdapDirectoryIdentifier DirectoryIdentifier
        {
            get
            {
                if (_directoryIdentifier == null)
                {
                    lock (this)
                    {
                        SetDirectoryIdentifier();
                    }
                }
                return _directoryIdentifier;
            }
        }

        public LdapAuthentication()
        {
            _authenticationType = AuthenticationTypes.FastBind;
        }

        private void SetDirectoryIdentifier()
        {
            int num = 0;
            _directoryIdentifier = new LdapDirectoryIdentifier(portNumber: (!UseSecureSocketLayer) ? LdapPort : LdapPortSsl, server: _domain);
        }

        public DirectoryEntry GetDirectoryEntryOfProxyUser()
        {
            string text = $"{LdapUriString}://{_domain}/{_path}";
            using (DirectoryEntry result = new DirectoryEntry(text, _userNameProxyUser, _passwordProxyUser, _authenticationType))
            {
                try
                {
                    return result;
                }
                catch (Exception innerException)
                {
                    throw new Exception(text, innerException);
                }
            }
        }

        private bool OnLdapConnectionVerifyServerCertificate(LdapConnection connection, X509Certificate serverCertificate)
        {
            if (AllowAnyCertificates)
            {
                return true;
            }
            if (!VerifyCertificateLocally)
            {
                return true;
            }
            if (LocallyVerificationCertificate != null)
            {
                if (LocallyVerificationCertificate.Equals(serverCertificate))
                {
                    return true;
                }
                return false;
            }
            if (!string.IsNullOrEmpty(LocallyVerificationCertificateSerialNumber))
            {
                if (serverCertificate.GetSerialNumberString() == LocallyVerificationCertificateSerialNumber)
                {
                    return true;
                }
                return false;
            }
            X509Store x509Store = new X509Store("My");
            x509Store.Open(OpenFlags.ReadOnly);
            X509Certificate2Enumerator enumerator = x509Store.Certificates.GetEnumerator();
            while (enumerator.MoveNext())
            {
                X509Certificate2 current = enumerator.Current;
                if (current.SerialNumber == serverCertificate.GetSerialNumberString())
                {
                    return true;
                }
            }
            return false;
        }

        public bool CheckPassword(string userName, string password)
        {
            Collection<string> groupMemberships = null;
            LdapUser ldapUserInfo = null;
            return CheckPassword(userName, password, readGroupMemberships: false, out groupMemberships, readLdapUserInfo: false, out ldapUserInfo);
        }

        public bool CheckPassword(string userName, string password, out Collection<string> groupMemberships)
        {
            LdapUser ldapUserInfo = null;
            return CheckPassword(userName, password, readGroupMemberships: true, out groupMemberships, readLdapUserInfo: false, out ldapUserInfo);
        }

        public bool CheckPassword(string userName, string password, out LdapUser ldapUserInfo)
        {
            Collection<string> groupMemberships = null;
            return CheckPassword(userName, password, readGroupMemberships: false, out groupMemberships, readLdapUserInfo: true, out ldapUserInfo);
        }

        public bool CheckPassword(string userName, string password, out Collection<string> groupMemberships, out LdapUser ldapUserInfo)
        {
            return CheckPassword(userName, password, readGroupMemberships: true, out groupMemberships, readLdapUserInfo: true, out ldapUserInfo);
        }

        private bool CheckPassword(string userName, string password, bool readGroupMemberships, out Collection<string> groupMemberships, bool readLdapUserInfo, out LdapUser ldapUserInfo)
        {
            if (string.IsNullOrEmpty(userName))
            {
                throw new ArgumentNullException("userName");
            }
            if (string.IsNullOrEmpty(password))
            {
                throw new ArgumentNullException("password");
            }
            if (password.Length >= 128)
            {
                throw new Exception();
            }
            groupMemberships = null;
            ldapUserInfo = null;
            List<string> list = new List<string>();
            DirectoryEntry directoryEntryOfProxyUser = GetDirectoryEntryOfProxyUser();
            if (directoryEntryOfProxyUser == null)
            {
                throw new Exception();
            }
            lock (this)
            {

                BLL.ErrLogInfo.WriteLog("lock锁这里");
                using (LdapConnection ldapConnection = new LdapConnection(DirectoryIdentifier))
                {
                    try
                    {

                        BLL.ErrLogInfo.WriteLog("ldap验证");

                        ldapConnection.SessionOptions.SecureSocketLayer = UseSecureSocketLayer;
                        if (UseSecureSocketLayer && (AllowAnyCertificates || VerifyCertificateLocally))
                        {
                            LdapSessionOptions sessionOptions = ldapConnection.SessionOptions;
                            sessionOptions.VerifyServerCertificate = (VerifyServerCertificateCallback)Delegate.Combine(sessionOptions.VerifyServerCertificate, new VerifyServerCertificateCallback(OnLdapConnectionVerifyServerCertificate));
                        }
                        ldapConnection.Credential = new NetworkCredential(UserNameProxyUser, PasswordProxyUser);
                        ldapConnection.AuthType = AuthType.Basic;
                        ldapConnection.SessionOptions.ProtocolVersion = 3;
                        ldapConnection.Bind();
                    }
                    catch (DirectoryServicesCOMException innerException)
                    {
                
                        BLL.ErrLogInfo.WriteLog($"ldap验证失败:{innerException.Message}");
                        throw new Exception("Proxyuser_NotFound", innerException);
                    }
                    catch (Exception ex)
                    {
                        BLL.ErrLogInfo.WriteLog($"捕获异常信息:{ex.Message}");
                        throw ex;
                    }
                    finally
                    {
                        if (UseSecureSocketLayer && (AllowAnyCertificates || VerifyCertificateLocally))
                        {
                            LdapSessionOptions sessionOptions2 = ldapConnection.SessionOptions;
                            sessionOptions2.VerifyServerCertificate = (VerifyServerCertificateCallback)Delegate.Remove(sessionOptions2.VerifyServerCertificate, new VerifyServerCertificateCallback(OnLdapConnectionVerifyServerCertificate));
                        }
                    }
                    string ldapFilter = $" (cn={userName})";
                    if (readGroupMemberships)
                    {
                        list.Add("groupmembership");
                    }
                    if (readLdapUserInfo)
                    {
                        BLL.ErrLogInfo.WriteLog($"readLdapUserInfo信息:{readLdapUserInfo}");
                        GetUserAttrs(list);
                    }
                    SearchRequest request = new SearchRequest(_path, ldapFilter, System.DirectoryServices.Protocols.SearchScope.Subtree, list.ToArray());
                    SearchResponse searchResponse = (SearchResponse)ldapConnection.SendRequest(request);
                    if (searchResponse == null || searchResponse.Entries == null || searchResponse.Entries.Count < 1)
                    {
                        throw new Exception();
                    }
                    BLL.ErrLogInfo.WriteLog($"searchResponse不等于空");
                    SearchResultEntry searchResultEntry = searchResponse.Entries[0];
                    string distinguishedName = searchResultEntry.DistinguishedName;
                    BLL.ErrLogInfo.WriteLog($"distinguishedName={distinguishedName}");
                    if (readGroupMemberships)
                    {
                        groupMemberships = ReadGroupMembershipsInternal(searchResultEntry);
                    }
                    if (readLdapUserInfo)
                    {
                        ldapUserInfo = GetUserInfoFromDirectoryEntry(searchResultEntry);
                    }
                    ldapConnection.Credential = new NetworkCredential(distinguishedName, password);
                    try
                    {
                        ldapConnection.Bind();
                    }
                    catch (LdapException innerException2)
                    {
                        BLL.ErrLogInfo.WriteLog($"ldap错误={innerException2.Message}");
                        throw new Exception("LdapAuthentication_WrongPassword", innerException2);
                    }
                    return true;
                }
            }
        }

        private static void GetUserAttrs(List<string> attrs)
        {
            attrs.Add("basfANRED");
            attrs.Add("basfBuilding");
            attrs.Add("basfCCCompany");
            attrs.Add("basfCCPN");
            attrs.Add("basfCompanyID");
            attrs.Add("basfContractStatus");
            attrs.Add("basfCostCenter");
            attrs.Add("basfEPSapGroup");
            attrs.Add("basfEPSapUserID");
            attrs.Add("basfCCPN");
            attrs.Add("basfGCDID");
            attrs.Add("basfIDtype");
            attrs.Add("basfPersg");
            attrs.Add("basfPersonID");
            attrs.Add("basfRegion");
            attrs.Add("basfRPID");
            attrs.Add("basfSBNR");
            attrs.Add("basfSex");
            attrs.Add("basfSIAMID");
            attrs.Add("dn");
            attrs.Add("cn");
            attrs.Add("uid");
            attrs.Add("co");
            attrs.Add("employeeStatus");
            attrs.Add("facsimileTelephoneNumber");
            attrs.Add("fullName");
            attrs.Add("givenName");
            attrs.Add("GroupMembership");
            attrs.Add("InternetEmailAddress");
            attrs.Add("l");
            attrs.Add("mobile");
            attrs.Add("o");
            attrs.Add("ou");
            attrs.Add("postalCode");
            attrs.Add("roomNumber");
            attrs.Add("s");
            attrs.Add("sa");
            attrs.Add("surname");
            attrs.Add("telephoneNumber");
            attrs.Add("title");
            attrs.Add("mail");
            attrs.Add("sn");
            attrs.Add("employeeNumber");
            attrs.Add("basfUDMSID");
            attrs.Add("postalAddress");
            attrs.Add("c");
        }

        private static LdapUser GetUserInfoFromDirectoryEntry(SearchResultEntry entry)
        {
            LdapUser ldapUser = new LdapUser();
            if (entry.Attributes.Contains("cn"))
            {
                ldapUser.UserName = (string)entry.Attributes["cn"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("mail"))
            {
                ldapUser.EmailAddress = (string)entry.Attributes["mail"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("givenName"))
            {
                ldapUser.FirstName = (string)entry.Attributes["givenName"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("sn"))
            {
                ldapUser.LastName = (string)entry.Attributes["sn"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("fullName"))
            {
                ldapUser.FullName = (string)entry.Attributes["fullName"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("o"))
            {
                ldapUser.Company = (string)entry.Attributes["o"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("ou"))
            {
                ldapUser.OrgCode = (string)entry.Attributes["ou"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("telephoneNumber"))
            {
                ldapUser.Phone = (string)entry.Attributes["telephoneNumber"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("basfGCDID"))
            {
                ldapUser.GcdId = (string)entry.Attributes["basfGCDID"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("basfPersonID"))
            {
                ldapUser.PersonId = (string)entry.Attributes["basfPersonID"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("employeeNumber"))
            {
                ldapUser.PersonalNr = (string)entry.Attributes["employeeNumber"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("basfCCPN"))
            {
                ldapUser.Ccpn = (string)entry.Attributes["basfCCPN"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("basfUDMSID"))
            {
                ldapUser.UdmsId = (string)entry.Attributes["basfUDMSID"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("postalAddress"))
            {
                ldapUser.Street = (string)entry.Attributes["postalAddress"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("l"))
            {
                ldapUser.City = (string)entry.Attributes["l"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("postalCode"))
            {
                ldapUser.ZipCode = (string)entry.Attributes["postalCode"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("c"))
            {
                ldapUser.Country = (string)entry.Attributes["c"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("facsimileTelephoneNumber"))
            {
                ldapUser.Fax = (string)entry.Attributes["facsimileTelephoneNumber"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("mobile"))
            {
                ldapUser.MobilePhone = (string)entry.Attributes["mobile"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("basfcompanyid"))
            {
                ldapUser.CompanyId = (string)entry.Attributes["basfcompanyid"].GetValues(typeof(string))[0];
            }
            if (entry.Attributes.Contains("basfidtype"))
            {
                ldapUser.BasfIdType = (string)entry.Attributes["basfidtype"].GetValues(typeof(string))[0];
            }
            return ldapUser;
        }

        private static Collection<string> ReadGroupMembershipsInternal(SearchResultEntry entry)
        {
            Collection<string> collection = new Collection<string>();
            object[] values = entry.Attributes["groupmembership"].GetValues(typeof(string));
            for (int i = 0; i < values.Length; i++)
            {
                string item = (string)values[i];
                collection.Add(item);
            }
            return collection;
        }

        private static LdapUser GetUserInfoFromSearchResult(DirectoryEntry entry)
        {
            LdapUser ldapUser = new LdapUser();
            if (entry.Properties.Contains("cn"))
            {
                ldapUser.UserName = entry.Properties["cn"].Value.ToString();
            }
            if (entry.Properties.Contains("mail"))
            {
                ldapUser.EmailAddress = entry.Properties["mail"].Value.ToString();
            }
            if (entry.Properties.Contains("givenName"))
            {
                ldapUser.FirstName = entry.Properties["givenName"].Value.ToString();
            }
            if (entry.Properties.Contains("sn"))
            {
                ldapUser.LastName = entry.Properties["sn"].Value.ToString();
            }
            if (entry.Properties.Contains("fullName"))
            {
                ldapUser.FullName = entry.Properties["fullName"].Value.ToString();
            }
            if (entry.Properties.Contains("o"))
            {
                ldapUser.Company = entry.Properties["o"].Value.ToString();
            }
            if (entry.Properties.Contains("ou"))
            {
                ldapUser.OrgCode = entry.Properties["ou"].Value.ToString();
            }
            if (entry.Properties.Contains("telephoneNumber"))
            {
                ldapUser.Phone = entry.Properties["telephoneNumber"].Value.ToString();
            }
            if (entry.Properties.Contains("basfGCDID"))
            {
                ldapUser.GcdId = entry.Properties["basfGCDID"].Value.ToString();
            }
            if (entry.Properties.Contains("basfPersonID"))
            {
                ldapUser.PersonId = entry.Properties["basfPersonID"].Value.ToString();
            }
            if (entry.Properties.Contains("employeeNumber"))
            {
                ldapUser.PersonalNr = entry.Properties["employeeNumber"].Value.ToString();
            }
            if (entry.Properties.Contains("basfCCPN"))
            {
                ldapUser.Ccpn = entry.Properties["basfCCPN"].Value.ToString();
            }
            if (entry.Properties.Contains("basfUDMSID"))
            {
                ldapUser.UdmsId = entry.Properties["basfUDMSID"].Value.ToString();
            }
            if (entry.Properties.Contains("postalAddress"))
            {
                ldapUser.Street = entry.Properties["postalAddress"].Value.ToString();
            }
            if (entry.Properties.Contains("l"))
            {
                ldapUser.City = entry.Properties["l"].Value.ToString();
            }
            if (entry.Properties.Contains("postalCode"))
            {
                ldapUser.ZipCode = entry.Properties["postalCode"].Value.ToString();
            }
            if (entry.Properties.Contains("c"))
            {
                ldapUser.Country = entry.Properties["c"].Value.ToString();
            }
            if (entry.Properties.Contains("facsimileTelephoneNumber"))
            {
                ldapUser.Fax = entry.Properties["facsimileTelephoneNumber"].Value.ToString();
            }
            if (entry.Properties.Contains("mobile"))
            {
                ldapUser.MobilePhone = entry.Properties["mobile"].Value.ToString();
            }
            if (entry.Properties.Contains("basfcompanyid"))
            {
                ldapUser.CompanyId = entry.Properties["basfcompanyid"].Value.ToString();
            }
            if (entry.Properties.Contains("basfidtype"))
            {
                ldapUser.BasfIdType = entry.Properties["basfidtype"].Value.ToString();
            }
            return ldapUser;
        }

    }
}