using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using IntraService.Common.Helpers;
using IntraService.Domain.ADsync;
using IntraService.Domain.Models;
using IntraService.Domain.Helpers;
using IntraService.Security;
using IntraService.Helpers;

public partial class ADCheck : System.Web.UI.Page
{

    protected string Template = "";

    protected void Page_Load(object sender, EventArgs e)
    {
        var sb = new StringBuilder();
        var identity = Context.User.Identity as IIntraServiceIdentity;
        var login = identity != null ? identity.Login : Context.User.Identity.Name;
        SecurityIdentifier sid = null;
        if (Context.User.Identity as WindowsIdentity != null)
        {
            sid = (Context.User.Identity as WindowsIdentity).User;
            sb.Append("<tr>").Append("<td>Sid:</td>").AppendFormat("<td>{0}</td>", sid).Append("</tr>");
        }
        if (string.IsNullOrEmpty(login))
        {
            sb.Append("<tr>").Append("<td>Ошибка:</td>").Append("<td>Логин в Context.User.Identity.Name не задан</td>").Append("</tr>");
            Template = sb.ToString();
            return;
        }

        using(var _dc = new IntraServiceDataContext())
        {

            // При использовании windows-авторизации пользователь авторизуется по его windows-логину
            User user = null;
            //if(!login.Contains("\\"))
            //{
            //    if (identity != null)
            //        sb.Append("<tr>").Append("<td>Ошибка:</td>").Append("<td>Домен не задан, не Windows Identity. Логин:  ").Append(login).Append("</td></tr>");
            //    else
            //    {
            //        sb.Append("<tr>").Append("<td>Ошибка:</td>").Append("<td>Логин или Домен в Context.User.Identity.Name не задан</td>").Append("</tr>");
            //    }                
            //}
            var loginSplit = login.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries);
            var userDomain = loginSplit.Length > 1 ? loginSplit[0] : "";// login.Substring(0, login.IndexOf('\\'));
            var userName = loginSplit.Length > 1 ? loginSplit[1] : login;// login.Substring(login.IndexOf('\\') + 1);
            sb.Append("<tr>").Append("<td>Домен:</td>").AppendFormat("<td>{0}</td>", userDomain).Append("</tr>");
            sb.Append("<tr>").Append("<td>Логин:</td>").AppendFormat("<td>{0}</td>", userName).Append("</tr>");
            ADSyncProfile profile;
            try
            {
                var users = _dc.Users.Where(u => (u.Login == userName||u.Login==login) && u.ADGuid.HasValue);
                sb.Append("<tr>").Append("<td>Кол-во пользователей с одним логином в базе:</td>").AppendFormat("<td>{0}</td>", users.Count()).Append("</tr>");
                if (users.Count() > 0)
                {
                    foreach (var userDb in users)
                    {
                        var pathIds =
                            userDb.Company.Path.Split('|').Where(x => !string.IsNullOrEmpty(x)).Select(
                                x => Convert.ToInt32(x)).ToArray();
                        profile =
                            _dc.ADSyncProfiles.FirstOrDefault(
                                p => p.Enabled && p.CompanyId != null && pathIds.Contains((int) p.CompanyId));
                        if(profile==null)
                            sb.Append("<tr>").Append("<td>Ошибка:</td>").Append("<td>Профиль для пользователя не найден (поиск по подразделению)</td>").Append("</tr>");
                        if (profile != null && String.IsNullOrEmpty(profile.DomainNames))
                            sb.Append("<tr>").Append("<td>Ошибка:</td>").Append("<td>Не указаны доменные имена</td>").Append("</tr>");
                        if (profile != null && !String.IsNullOrEmpty(profile.DomainNames))
                        {
                            string[] domainNames =
                                profile.DomainNames.Split(',').Select(dn => dn.Trim().ToUpper()).ToArray();
                            sb.Append("<tr>").Append("<td>Доменные имена:</td>").AppendFormat("<td>{0}</td>", string.Join(", ", domainNames)).Append("</tr>");

                            if (!domainNames.Contains(userDomain.ToUpper()))
                            {
                                sb.Append("<tr>").Append("<td>Ошибка:</td>").Append("<td>Доменные имена не содержат доменное имя пользователя</td>").Append("</tr>");
                            }
                            if (domainNames.Contains(userDomain.ToUpper()))
                            {
                                if (sid != null)
                                {
                                    //ищем пользователя в AD
                                    var searchPath = "GC://" + profile.LDAPPathBeginNoPort;

                                    var entry = ActiveDirectoryHelper.GetDirectoryEntry(searchPath, profile);                                    

                                    var search = new DirectorySearcher(entry,
                                                                       String.Format("(&({1}={0})(objectSid={2}))",
                                                                                     userName,
                                                                                     profile.UserLogin.Replace("{", "").
                                                                                         Replace("}", ""),
                                                                                     sid.ToString()))
                                                     {SearchScope = SearchScope.Subtree};
                                    SearchResult result = search.FindOne();
                                    if(result==null)
                                        sb.Append("<tr>").Append("<td>Ошибка:</td>").AppendFormat("<td>Пользователь не найден. Search: {0}</td>", search).Append("</tr>");
                                    if (result != null) //пользователь найден
                                    {
                                        var userEntry = result.GetDirectoryEntry();
                                        var userGroups = new List<Guid>();
                                        //список всех групп (distribution, security), в которые входит пользователь
                                        var userDistributionGroups = new List<Guid>();
                                        //список distribution групп, в которые входит пользователь
                                        foreach (object value in result.Properties["memberOf"])
                                        {
                                            sb.Append("<tr>").Append("<td>Группа в которую входит пользователь:</td>").AppendFormat("<td>{0}</td>", value).Append("</tr>");

                                            var groupEntrySearchPath = "LDAP://" + profile.LDAPPathBegin + "/" + value;

                                            var groupEntry = ActiveDirectoryHelper.GetDirectoryEntry(groupEntrySearchPath, profile);

                                            var grouGuid = new Guid((byte[]) groupEntry.Properties["objectguid"][0]);
                                            sb.Append("<tr>").Append("<td>GUID этой группы:</td>").AppendFormat("<td>{0}</td>", grouGuid).Append("</tr>");
                                            userGroups.Add(grouGuid);
                                            if (
                                                new[] { "2", "4", "8" }.Contains(ADUser.ParseFieldTemplate("{groupType}",
                                                                                                         groupEntry)))
                                            {
                                                sb.Append("<tr>").Append("<td>Группа входит в distribution:</td>").Append("<td>Да</td>").Append("</tr>");
                                                userDistributionGroups.Add(grouGuid);
                                            }
                                        }

                                        //если хотя бы одна группа входит в список исключенных, то аутентификация не пройдена
                                        var excludedGroups =
                                            _dc.ADSyncExcludedGroups.Where(g => g.ProfileId == profile.Id).Select(
                                                g => new { g.Guid, g.Name }).ToArray();
                                        sb.Append("<tr>").Append("<td>Группы исключения:</td>").AppendFormat("<td>{0}</td>", string.Join(", ", excludedGroups.Select(x=>x.Name).ToArray())).Append("</tr>");
                                        if (excludedGroups.Any(u => userGroups.Contains(u.Guid)))
                                        {
                                            sb.Append("<tr>").Append("<td>Группа входит в группы исключения:</td>").Append("<td>Да</td>").Append("</tr>");
                                            Template = sb.ToString();
                                            return;
                                        }
                                        var userGuid = new Guid((byte[]) userEntry.Properties["objectguid"][0]);
                                        if (userDb.ADGuid == userGuid)
                                        {
                                            user = userDb;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }

                }
            }
            catch (Exception ex)
            {

                sb.Append("<tr>").Append("<td>Ошибка:</td>").AppendFormat("<td>{0}</td>", ex).Append("</tr>");
                Template = sb.ToString();
                return;
            }
            sb.Append("<tr>").Append("<td>Создание пользователя:</td>").Append("<td>Да</td>").Append("</tr>");
            // если пользователь с заданным логином не найден, попытка его создания с параметрами из AD
            if (user == null)
            {
                List<ADSyncProfile> profiles = new List<ADSyncProfile>();
                //ищем подходящий профиль синхронизации (профиль содержит доменное имя, под которым заходит пользователь)
                try
                {
                    profiles =
                        _dc.ADSyncProfiles.ToList().Where(
                            p =>
                            p.Enabled && !String.IsNullOrEmpty(p.DomainNames) &&
                            p.DomainNames.Split(',').Select(x => x.Trim()).Any(
                                n => n.IndexOf(userDomain, StringComparison.OrdinalIgnoreCase) >= 0)).ToList();
                }
                catch
                {
                    profiles = new List<ADSyncProfile>();
                }
                profile = null;
                //Профиль не найден
                if (!profiles.Any())
                {
                    sb.Append("<tr>").Append("<td>Профиль не найден:</td>").Append("<td>Да</td>").Append("</tr>");
                }
                //Найдено более одного профиля
                else if (profiles.Count > 1)
                {
                    var workingProfiles = new List<ADSyncProfile>();
                    var profileOuLevel = 0;
                    foreach (var p in profiles)
                    {                       
                        try
                        {
                            var searchPath = String.Format("LDAP://{0}", p.LDAPPath.ToUpper().Replace("LDAP://", ""));
                            DirectoryEntry entry = null;
                            try
                            {
                                entry = ActiveDirectoryHelper.GetDirectoryEntry(searchPath, profile);
                            }
                            catch
                            {
                                throw new Exception("Ошибка при получении данных по пользователю");
                            }
                            // поиск пользователя по имени в домене
                            var userSearch = new DirectorySearcher(entry, "(&(objectClass=user)(samaccountName=" + userName + "))");
                            if (userSearch.FindOne() == null) continue;
                        }
                        catch (Exception ex)
                        {
                            sb.Append("<tr>").Append(string.Format("<td>При попытке чтения пользователя по профилю \"{0}\" возникла ошибка:</td>", p.Name))
                                .AppendFormat("<td>{0}</td>", ex.Message).Append("</tr>");
                            continue;
                        }
                        //добавим только те профили, в которых нашёлся пользователь
                        workingProfiles.Add(p);
                        var ldap = p.LDAPPath.ToLower();
                        //посчитаем количество вхождений подстроки "ou=" в ldap-пути, чтоб вычислить уровень вложенности
                        //для профилей, работающих через CN выставляем plevel=100, т.к. они приоритетней
                        var plevel = p.UseOU ? (ldap.Length - ldap.Replace("ou=", "").Length) / "ou=".Length : 100;

                        //В противном случае выбираем профиль, который имеет большую вложенность OU
                        if (plevel > profileOuLevel)
                        {
                            profile = p;
                            profileOuLevel = plevel;
                        }
                    }
                    if (profiles.Count > 1)
                    {
                        sb.Append("<tr>")
                          .Append("<td>По домену пользователя найдено несколько профилей:</td>")
                          .Append(string.Format("<td>{0}</td>", string.Join("<br/>", profiles.Select(p => p.Name))))
                          .Append("</tr>");
                    }
                    if (workingProfiles.Count > 1)
                    {
                        sb.Append("<tr>")
                          .Append("<td>Пользователь определяется в нескольких профилях:</td>")
                          .Append(string.Format("<td>{0}</td>", string.Join("<br/>", workingProfiles.Select(p => p.Name))))
                          .Append("</tr>");
                    }
                }
                //Найден один профиль
                else
                {
                    profile = profiles.First();
                }
                if (profile != null)
                {
                    sb.Append("<tr>").Append("<td>Текущий профиль:</td>").AppendFormat("<td>{0}</td>", profile.Name).Append("</tr>");
                    //ищем пользователя в AD
                    var searchPath = "GC://" + profile.LDAPPathBeginNoPort;

                    var entry = ActiveDirectoryHelper.GetDirectoryEntry(searchPath, profile);

                    var search = new DirectorySearcher(entry,
                                                       String.Format("({1}={0})", userName,
                                                                     profile.UserLogin.Replace("{", "").Replace("}", "")))
                                     {SearchScope = SearchScope.Subtree};
                    SearchResult result = search.FindOne();
                    if(result==null)
                        sb.Append("<tr>").Append("<td>Пользователь не найден в AD:</td>").AppendFormat("<td>{0}</td>", String.Format("({1}={0})", userName,
                                                                     profile.UserLogin.Replace("{", "").Replace("}", ""))).Append("</tr>");
                    if (result != null) //пользователь найден
                    {
                        var userEntry = result.GetDirectoryEntry();
                        var userGroups = new List<Guid>();
                            //список всех групп (distribution, security), в которые входит пользователь
                        var userDistributionGroups = new List<Guid>();
                        var userOuGuid = new Guid((byte[])userEntry.Parent.Properties["objectguid"][0]); //ou guid
                            //список distribution групп, в которые входит пользователь
                        foreach (object value in result.Properties["memberOf"])
                        {
                            sb.Append("<tr>").Append("<td>Группа в которую входит пользователь:</td>").AppendFormat("<td>{0}</td>", value).Append("</tr>");

                            var groupEntrySearchPath = "LDAP://" + profile.LDAPPathBegin + "/" + value;

                            var groupEntry = ActiveDirectoryHelper.GetDirectoryEntry(groupEntrySearchPath, profile);
                            
                            var grouGuid = new Guid((byte[]) groupEntry.Properties["objectguid"][0]);
                            
                            sb.Append("<tr>").Append("<td>GUID этой группы:</td>").AppendFormat("<td>{0}</td>", grouGuid).Append("</tr>");
                            userGroups.Add(grouGuid);
                            if (new[] { "2", "4", "8" }.Contains(ADUser.ParseFieldTemplate("{groupType}", groupEntry)))
                            {
                                sb.Append("<tr>").Append("<td>Группа входит в distribution:</td>").Append("<td>Да</td>").Append("</tr>");
                                userDistributionGroups.Add(grouGuid);
                            }
                        }

                        //если хотя бы одна группа входит в список исключенных, то аутентификация не пройдена
                        var excludedGroups =
                            _dc.ADSyncExcludedGroups.Where(g => g.ProfileId == profile.Id).Select(g => new { g.Guid, g.Name}).ToArray();
                        sb.Append("<tr>").Append("<td>Группы исключения:</td>").AppendFormat("<td>{0}</td>", string.Join(", ", excludedGroups.Select(x => x.Name).ToArray())).Append("</tr>");
                        if (excludedGroups.Select(s => s.Guid).Any(userGroups.Contains))
                        {
                            sb.Append("<tr>").Append("<td>Группа входит в группы исключения:</td>").Append("<td>Да</td>").Append("</tr>");
                            Template = sb.ToString();
                            return;
                        }

                        //ищем подразделение, в которое входит сотрудник
                    var company =
                        _dc.Companies.FirstOrDefault(
                            c =>
                            c.ADGuid.HasValue &&
                            ((profile.DepartmentsSyncDisabled == true && c.Id == profile.CompanyId) ||
                             ((profile.DepartmentsSyncDisabled ?? false) &&
                              ((profile.UseOU && c.ADGuid == userOuGuid) ||
                               userDistributionGroups.Contains((Guid) c.ADGuid)))));

                        if (company != null)
                        {
                            sb.Append("<tr>").Append("<td>Компания:</td>").AppendFormat("<td>{0}</td>", company.FullName).Append("</tr>");
                            var userGuid = new Guid((byte[]) userEntry.Properties["objectguid"][0]);
                            var newUser = new User
                                              {
                                                  RoleId = profile.Role.Id,
                                                  CompanyId = company.Id,
                                                  Login = ADUser.ParseFieldTemplate(profile.UserLogin, userEntry),
                                                  Password = RequestHelper.MD5Encode("d807t1fcc82d1f2a9bb018ca6738a59d"),
                                                  Name = ADUser.ParseFieldTemplate(profile.UserName, userEntry),
                                                  Email = ADUser.ParseFieldTemplate(profile.UserEmail, userEntry),
                                                  Comments =
                                                      ADUser.ParseFieldTemplate(profile.UserDescription, userEntry),
                                                  Position = ADUser.ParseFieldTemplate(profile.UserPosition, userEntry),
                                                  ADGuid = userGuid,
                                                  ServiceDeskPath = company.ServiceDeskPath
                                              };

                            var adUserPhone = ADUser.ParseFieldTemplate(profile.UserPhone, userEntry);

                            if (!string.IsNullOrEmpty(adUserPhone))
                            {
                                var phones = adUserPhone.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                var isFirst = true;

                                foreach (var p in phones.Where(x=>!String.IsNullOrEmpty(x.Trim())))
                                {
                                    newUser.UserPhones.Add(new UserPhone(PhoneHelper.NormalizePhone(p), isMain: isFirst));

                                    isFirst = false;
                                }

                            }


                            try
                            {
                                var unique = _dc.Settings.FirstOrDefault(s => s.Key.IndexOf("UniqueLogin") > -1);
                                var uniqueLogin = unique != null
                                                      ? unique.Value.ToUpperInvariant() == "TRUE" ? true : false
                                                      : false;

                                if (uniqueLogin &&
                                    _dc.Users.Where(
                                        x => x.Login == newUser.Login && (x.Id != newUser.Id || newUser.Id == 0))
                                        .Count() >= 1)
                                    throw new Exception(string.Format("Пользователь '{0}' не уникален и настройка уникальности логина включена",
                                                                      newUser.Login));
                            }
                            catch (Exception ex) //не удалось создать пользователя
                            {
                                sb.Append("<tr>").Append("<td>Ошибка:</td>").AppendFormat("<td>{0}</td>", ex).Append("</tr>");
                                Template = sb.ToString();
                                return;
                            }
                        }
                        else //не найдено подразделение, в котором числится сотрудник
                        {
                            sb.Append("<tr>").Append("<td>Компания для пользователя не найдена:</td>").Append("<td>Да</td>").Append("</tr>");
                            Template = sb.ToString();
                            return;
                        }
                    }
                    else //пользователь не найден
                    {
                        sb.Append("<tr>").Append("<td>Пользователь не найден:</td>").Append("<td>Да</td>").Append("</tr>");
                        Template = sb.ToString();
                        return;
                    }
                }
                else //профиль синхронизации не найден
                {
                    sb.Append("<tr>").Append("<td>Профиль не найден:</td>").Append("<td>Да</td>").Append("</tr>");
                    Template = sb.ToString();
                    return;
                }
            }
            Template = sb.ToString();
        }
    }
}
