using DAL.Models.Entities;
using DAL.Models.Filters;
using HRD.LDAPService;
using HRD.WebApi.Repositories;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DAL.Repositories
{
    public class EmployeeToWebAppRepository : BaseRepository<EmployeeToWebApp>
    {
        private readonly LdapManager _ldapManager;

        public EmployeeToWebAppRepository(WebApiContext context, LdapManager ldapManager) : base(context)
        {
            _ldapManager = ldapManager;
        }

        public async Task<bool> DeleteEmloyeeToWebAppAsync(int webAppId)
        {
            var list = await GetListByAsync(x => x.WebAppId == webAppId);
            foreach (var item in list)
            {
                if (!await DeleteByIdAsync(item.EmployeeId))
                {
                    throw new Exception($"Couldn't delete the Employee-Id: {item.EmployeeId}");
                }
            }
            return true;
        }

        public async Task<List<EmployeeToWebApp>> GetListByFilterAsync(EmployeeFilter filter, bool asNoTracking = true)
        {
            var items = this.RepositoryContext.Set<EmployeeToWebApp>().AsQueryable();

            if (filter.EmployeeId != null && filter.EmployeeId != 0)
            {
                items = items.Where(x => x.EmployeeId == filter.EmployeeId);
            }

            return asNoTracking ? await items.AsNoTracking().ToListAsync() : await items.ToListAsync();
        }

        private async Task<bool> AddUserToAdGroup(EmployeeToWebApp entity, int webRoleId, int? departementId)
        {
            return await ProcessGroup(entity, "adding", webRoleId, departementId);
        }

        private async Task<bool> RemoveUserFromAdGroup(EmployeeToWebApp entity, int webRoleId, int? departementId)
        {
            return await ProcessGroup(entity, "deleting", webRoleId, departementId);
        }

        private async Task<bool> ProcessGroup(EmployeeToWebApp entity, string action, int webRoleId, int? departementId)
        {
            string groupSuffix = null;
            if (entity == default) throw new ArgumentNullException(nameof(entity));
            if (entity == default) throw new ArgumentNullException(nameof(entity));

            var department = await GetDepartmentById(departementId);

            var employee = await GetEmployeeById(entity.EmployeeId);
            if (employee == default) throw new ArgumentNullException(nameof(employee));

            var webapp = await GetWebAppById(entity.WebAppId);
            if (webapp == default) throw new ArgumentNullException(nameof(webapp));

            bool result = true;
            var isAdding = action == "adding";

            var webappRole = await GetWebAppRoleById(webRoleId);

            try
            {
                if (department != default && !string.IsNullOrEmpty(department.AdGroupDepartmentName))
                {
                    groupSuffix = $"{webapp.AdWebAppName}_{department.AdGroupDepartmentName}";
                    if (_ldapManager.IsWindreamSuffixGroup(groupSuffix))
                    {
                        if (isAdding) result = _ldapManager.AD_AddUserloginToGroup(employee.LoginName, groupSuffix);
                        else result = _ldapManager.AD_RemoveUserFromGroup(employee.LoginName, groupSuffix);
                    }
                }

                if (webappRole != default)
                {
                    groupSuffix = $"{webapp.AdWebAppName}_{webappRole.WebAppRoleName}";
                    if (isAdding) result = _ldapManager.AD_AddUserloginToGroup(employee.LoginName, groupSuffix);
                    else result = _ldapManager.AD_RemoveUserFromGroup(employee.LoginName, groupSuffix);

                    if (_ldapManager.IsWindreamAdminGroup(groupSuffix))
                    {
                        groupSuffix = $"{webapp.AdWebAppName}_User";
                        if (isAdding) result = _ldapManager.AD_AddUserloginToGroup(employee.LoginName, groupSuffix);
                        else result = _ldapManager.AD_RemoveUserFromGroup(employee.LoginName, groupSuffix);
                    }
                }
            }
            catch (Exception ex)
            {
                WriteLogException(ex, $"An error occurred while '{action}' the '{employee.LoginName}' into '{groupSuffix}'.");
                return false;
            }

            if (!result)
            {
                WriteLogError($"An error occurred while '{action}' the '{employee.LoginName}' into '{groupSuffix}'.");
                return false;
            };
            return true;
        }

        public override async Task<bool> UpdateAsync(EmployeeToWebApp entity, bool saveEntity = true)
        {
            if (entity == default)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            var orgEntity = await base.GetByIdAsync(entity.EmployeeToWebAppId, true);
            var oldWebAppRoleId = 0;
            var newWebAppRoleId = 0;
            if (orgEntity.WebAppRoleId != entity.WebAppRoleId)
            {
                oldWebAppRoleId = orgEntity.WebAppRoleId;
                newWebAppRoleId = entity.WebAppRoleId;
            }
            int? oldDepartmentId = 0;
            int? newDepartmentId = 0;
            if (orgEntity.DepartmentId != entity.DepartmentId)
            {
                oldDepartmentId = orgEntity.DepartmentId;
                newDepartmentId = entity.DepartmentId;
            }
            orgEntity = null;

            if ((oldDepartmentId == 0 && oldWebAppRoleId == 0) ||
                 await RemoveUserFromAdGroup(entity, oldWebAppRoleId, oldDepartmentId)
              && await AddUserToAdGroup(entity, newWebAppRoleId, newDepartmentId))
            {
                var result = await base.UpdateAsync(entity, saveEntity);
                return result;
            }
            return false;
        }

        public override async Task<bool> UpdateListAsync(List<EmployeeToWebApp> entities, bool saveEntity = true)
        {
            var result = true;
            for (int i = 0; i < entities.Count; i++)
            {
                if (!await UpdateAsync(entities[i], saveEntity)) result = false;
            }
            return result;
        }

        public override async Task<bool> AddAsync(EmployeeToWebApp entity, bool saveEntity = true)
        {
            if (await AddUserToAdGroup(entity, entity.WebAppRoleId, entity.DepartmentId))
            {
                var result = await base.AddAsync(entity, saveEntity);
                return result;
            }
            return false;
        }

        public override async Task<bool> AddListAsync(List<EmployeeToWebApp> entities, bool saveEntity = true)
        {
            var result = true;
            for (int i = 0; i < entities.Count; i++)
            {
                if (!await AddAsync(entities[i], saveEntity)) result = false;
            }
            return result;
        }

        public override async Task<bool> DeleteByIdAsync(int id, bool saveEntity = true)
        {
            try
            {
                var entity = await base.GetByIdAsync(id);
                if (entity == default)
                {
                    throw new ArgumentNullException(nameof(entity));
                }

                if (await RemoveUserFromAdGroup(entity, entity.WebAppRoleId, entity.DepartmentId))
                {
                    var result = await base.DeleteByIdAsync(id, saveEntity);
                    return result;
                }
                return false;
            }
            catch (Exception ex)
            {
                WriteLogException(ex, $"An error occurred while deleting the Id {id}");
                return false;
            }
        }

        public async Task<WebApp> GetWebAppById(int entityId)
        {
            try
            {
                return await this.RepositoryContext.Set<WebApp>().FindAsync(entityId);
            }
            catch (Exception ex)
            {
                base.WriteLogException(ex, $"{typeof(WebApp).Name} id:{entityId}");
            }
            return default;
        }

        public async Task<Department> GetDepartmentById(int? entityId)
        {
            try
            {
                return await this.RepositoryContext.Set<Department>().FindAsync(entityId);
            }
            catch (Exception ex)
            {
                WriteLogException(ex, $"{typeof(Department).Name} id:{entityId}");
            }
            return default;
        }

        public async Task<Employee> GetEmployeeById(int entityId)
        {
            try
            {
                return await this.RepositoryContext.Set<Employee>().FindAsync(entityId);
            }
            catch (Exception ex)
            {
                WriteLogException(ex, $"{typeof(Employee).Name} id:{entityId}");
            }
            return default;
        }

        public async Task<WebAppRole> GetWebAppRoleById(int entityId)
        {
            try
            {
                return await this.RepositoryContext.Set<WebAppRole>().FindAsync(entityId);
            }
            catch (Exception ex)
            {
                WriteLogException(ex, $"{typeof(WebAppRole).Name} id:{entityId}");
            }
            return default;
        }
    }
}