using Lada.Framework.Data.Domains;
using Lada.Framework.Data.Domains.Categories;
using Lada.Framework.Data.Domains.Identity;
using Lada.Framework.Data.Repositories;
using Lada.Framework.DTO;
using Lada.Framework.DTO.SmartTable;
using Lada.Framework.Infrastructures.Helpers;
using Ladaer.BackEnd.Infrastructures.DTO;
using Ladaer.BackEnd.Infrastructures.DTO.Items.Categories;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Categories;
using Ladaer.BackEnd.Infrastructures.DTO.Results.Categories;
using Ladaer.BackEnd.Infrastructures.Services.Identity;
using Microsoft.EntityFrameworkCore;

namespace Ladaer.BackEnd.Infrastructures.Services.Categories
{
    public interface IMenuService
    {
        Task<SmartTableResult<MenuItem>> Search(SmartTableParam param);
        Task<List<MenuTreeItem>> GetTree(AppType appType, string keyword);

        Task<List<MenuTreeItem>> GetMenuList(int userId, AppType appType);
        Task<MenuResult> CreateOrUpdate(MenuModel model);
        Task<MenuModel> GetById(int menuId);
        Task<BaseResult> Delete(int menuId);
        Task<BaseResult> UpdateStatus(MenuStatusModel model, MenuStatus newStatus);
    }
    public class MenuService : IMenuService
    {
        private readonly IRepository<Menu> _menuRepository;
        private readonly IUserService _userService;
        public MenuService(IRepository<Menu> menuRepository, IUserService userService)
        {
            _menuRepository = menuRepository;
            _userService = userService;
        }

        #region SEARCH - GET
        public async Task<SmartTableResult<MenuItem>> Search(SmartTableParam param)
        {
            var query = _menuRepository.Query();
            if (param.Search.PredicateObject != null)
            {
                dynamic search = param.Search.PredicateObject;
                if (search.Keyword != null)
                {
                    string keyword = search.Keyword;
                    keyword = keyword.Trim().ToLower();
                    query = query.Where(x => x.Label.ToLower().Contains(keyword));
                }
                if (search.CreatedStart != null && search.CreatedEnd != null)
                {
                    DateTimeOffset start = search.CreatedStart;
                    DateTimeOffset end = search.CreatedEnd;
                    DateTimeOffset startParam = start.StartOfDay();
                    DateTimeOffset endParam = end.EndOfDay();
                    query = query.Where(x => x.CreatedTime >= startParam && x.CreatedTime <= endParam);
                }

                if (search.Status != null)
                {
                    MenuStatus status = search.Status;
                    query = query.Where(x => x.Status == status);
                }

                if (search.AppType != null)
                {
                    AppType appType = search.AppType;
                    query = query.Where(x => x.AppType == appType);

                }
                if (search.MenuType != null)
                {
                    MenuType menuType = search.MenuType;
                    query = query.Where(x => x.Kind == menuType);

                }
                if (search.ParentId != null)
                {
                    int parentId = search.ParentId;
                    query = query.Where(x => x.ParentId == parentId);

                }
                if (search.MenuLevel != null)
                {
                    int menuLevel = search.MenuLevel;
                    query = query.Where(x => x.MenuLevel == menuLevel);

                }

            }
            param.Sort = new Sort() { Predicate = "DisplayOrder", Reverse = false };
            var gridData = await query.ToSmartTableResultAsync(param, x => x.ToItem());
            if (gridData.Items.Count() > 0)
            {
                #region Set Parent Name
                var parentIds = gridData.Items.Select(i => i.ParentId).ToList();
                if (parentIds.Count > 0)
                {
                    var parents = _menuRepository.Query().Where(m => parentIds.Contains(m.Id)).ToList();
                    foreach (var item in gridData.Items)
                    {
                        if (item.ParentId > 0)
                        {
                            var parent = parents.FirstOrDefault(p => p.Id == item.ParentId);
                            item.ParentName = parent.Label;
                        }
                        else item.ParentName = "";
                    }
                }
                #endregion


            }
            return gridData;
        }


        public async Task<List<MenuTreeItem>> GetTree(AppType appType, string keyword)
        {
            var result = new List<MenuTreeItem>();
            var menuList = new List<Menu>();
            var query = _menuRepository.Query().Where(m => m.AppType == appType);
            if (!string.IsNullOrEmpty(keyword))
            {
                keyword = keyword.Trim();
                menuList = await query.Where(t => t.Label.Contains(keyword)).ToListAsync();
            }
            else
            {
                menuList = await query.ToListAsync();
            }
            //var allTransactionRules = await _transactionRuleRepository.Query().ToListAsync();

            var rootItems = menuList.Where(c => c.ParentId == 0).OrderBy(c => c.DisplayOrder).ToList();
            foreach (var item in rootItems)
            {
                //result.Add(new CategoryTreeItem() { Id=item.Id,Name= });
                result.AddRange(GetChilds(item, menuList, 0));
            }
            return result;

        }


        public async Task<List<MenuTreeItem>> GetMenuList(int userId, AppType appType)
        {
            var result = new List<MenuTreeItem>();
            if (appType == 0 || (int)appType > 3)
            {
                return new List<MenuTreeItem>();
            }

            //Lấy danh sách role của người dùng
            var userRoles = await _userService.GetRolesAsync(userId);

            if (userRoles.Any())
            {
                var allowAllRoles = new List<string>() { "Founder" };
                var query = _menuRepository.Query().Where(m => m.AppType == appType && m.Status == MenuStatus.Active);
                var allMenus = await query.ToListAsync();

                //Kiểm tra xem có phải founder không
                if (userRoles.Any(r => r.ToLower().Trim() == "founder"))
                {
                    result = allMenus.Where(m => m.MenuLevel == 1).OrderBy(m => m.DisplayOrder).Select(m => m.ToTreeItem()).ToList();
                    foreach (var item in result)
                    {
                        item.Childs = new List<MenuTreeItem>();
                        item.Childs.AddRange(GetChildMenu(item, allMenus));
                    }

                }
                else
                {
                    //Nếu người dùng không có quyền gì
                    if (userRoles.Count == 0)
                    {
                        allMenus = allMenus.Where(m => string.IsNullOrEmpty(m.AllowRoles)).OrderBy(m => m.DisplayOrder).ToList();
                        result = allMenus.Where(m => m.MenuLevel == 1).OrderBy(m => m.DisplayOrder).Select(m => m.ToTreeItem()).ToList();
                        foreach (var item in result)
                        {
                            item.Childs = new List<MenuTreeItem>();
                            item.Childs.AddRange(GetChildMenu(item, allMenus));
                        }

                    }
                    //Người dùng có quyền
                    else
                    {
                        var allAllowMenus = allMenus.Where(m => string.IsNullOrEmpty(m.AllowRoles)).OrderBy(m => m.DisplayOrder).ToList();

                        foreach (var role in userRoles)
                        {
                            var allowMenus = allMenus.Where(m => m.AllowRoles.Contains(role)).ToList();
                            allAllowMenus.AddRange(allowMenus);
                        }
                        allAllowMenus = allAllowMenus.Distinct().ToList();
                        result = allAllowMenus.Where(m => m.MenuLevel == 1).OrderBy(m => m.DisplayOrder).Select(m => m.ToTreeItem()).ToList();
                        foreach (var item in result)
                        {
                            item.Childs = new List<MenuTreeItem>();
                            item.Childs.AddRange(GetChildMenu(item, allAllowMenus));
                        }

                    }
                }




            }



            return result;


        }

        #endregion

        #region CRUD
        public async Task<MenuResult> CreateOrUpdate(MenuModel model)
        {
            var menu = model.ToEntity();

            //Cập nhật thông tin chung của thực thể
            menu = menu.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

            if (menu.Id > 0)
            {
                //Cập nhật
                return await Update(menu);
            }
            else
            {
                //Thêm mới
                return await Create(menu);
            }
        }


        public async Task<MenuModel> GetById(int menuId)
        {
            var result = new MenuModel();
            if (menuId <= 0)
            {
                return result;
            }
            var menuForUpdate = await _menuRepository.Query().FirstOrDefaultAsync(c => c.Id == menuId);
            if (menuForUpdate == null || menuForUpdate.Id <= 0)
            {
                return result;
            }
            return menuForUpdate.ToModel();
        }



        public async Task<BaseResult> Delete(int menuId)
        {
            var result = new BaseResult();
            if (menuId <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Yêu cầu xóa menu không hợp lệ";
                return result;
            }
            var menuForUpdate = await _menuRepository.Query().FirstOrDefaultAsync(c => c.Id == menuId);
            if (menuForUpdate == null || menuForUpdate.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy menu yêu càu";
                return result;
            }

            var childs = await _menuRepository.Query().CountAsync(c => c.ParentId == menuId);
            if (childs > 0)
            {
                result.Result = Result.Failed;
                result.Message = "Menu có chứa menu con, vui lòng xóa menu con trước";
                return result;
            }
            try
            {

                //Cập nhật thông tin chung của thực thể
                menuForUpdate = menuForUpdate.UpdateCommonInt();
                var delResult = await _menuRepository.DeleteAsync(menuForUpdate);
                if (delResult <= 0)
                {
                    result.Result = Result.SystemError;
                    result.Message = "Hệ thống không thể thực hiện yêu cầu lúc này, vui lòng thử lại sau!";
                    return result;
                }

            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.ToString();
            }
            return result;
        }
        #endregion

        #region LOCK / UNLOCK
        public async Task<BaseResult> UpdateStatus(MenuStatusModel model, MenuStatus newStatus)
        {
            var result = new BaseResult();
            if (model.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Yêu cầu không hợp lệ";
                return result;
            }
            var menuForUpdate = await _menuRepository.Query().FirstOrDefaultAsync(c => c.Id == model.Id);
            if (menuForUpdate == null || menuForUpdate.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy menu yêu càu";
                return result;
            }
            try
            {
                menuForUpdate.Status = newStatus;
                //Cập nhật thông tin chung của thực thể
                menuForUpdate = menuForUpdate.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);
                var udpResult = await _menuRepository.UpdateAsync(menuForUpdate);
                if (udpResult <= 0)
                {
                    result.Result = Result.SystemError;
                    result.Message = "Hệ thống không thể thực hiện yêu cầu lúc này, vui lòng thử lại sau!";
                    return result;
                }

            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.ToString();
            }
            return result;
        }

        #endregion


        #region Utilities
        private async Task<MenuResult> Create(Menu menu)
        {
            var result = new MenuResult();
            try
            {
                menu.Status = MenuStatus.Active;
                if (!string.IsNullOrEmpty(menu.AllowRoles))
                {
                    menu.AllowRoles = $",{menu.AllowRoles},";
                }
                await _menuRepository.InsertAsync(menu);
                result.Id = menu.Id;

            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.ToString();
                result.Id = 0;
            }
            return result;
        }

        private async Task<MenuResult> Update(Menu menu)
        {
            var result = new MenuResult() { Result = Result.Success };
            var menuForUpdate = await _menuRepository.Query().FirstOrDefaultAsync(c => c.Id == menu.Id);
            if (menuForUpdate == null || menu.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy menu yêu cầu!";
                return result;
            }

            try
            {
                menuForUpdate.Label = menu.Label;
                menuForUpdate.Kind = menu.Kind;
                menuForUpdate.ParentId = menu.ParentId;
                menuForUpdate.IconClass = menu.IconClass;
                menuForUpdate.Url = menu.Url;
                menuForUpdate.AppType = menu.AppType;
                menuForUpdate.MenuLevel = menu.MenuLevel;
                menuForUpdate.DisplayOrder = menu.DisplayOrder;
                menuForUpdate.AllowRoles = menu.AllowRoles;
                if (!string.IsNullOrEmpty(menuForUpdate.AllowRoles))
                {
                    menuForUpdate.AllowRoles = $",{menuForUpdate.AllowRoles},";
                }


                await _menuRepository.UpdateAsync(menuForUpdate);
                result.Id = menuForUpdate.Id;
            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.ToString();
            }

            return result;
        }

        private List<MenuTreeItem> GetChildMenu(MenuTreeItem menuTreeItem, List<Menu> menus)
        {
            var result = new List<MenuTreeItem>();

            result.Add(menuTreeItem);
            var childs = menus.Where(c => c.ParentId == menuTreeItem.Id).OrderBy(m => m.DisplayOrder).Select(m => m.ToTreeItem()).ToList();

            if (childs.Any())
            {

                menuTreeItem.Childs = new List<MenuTreeItem>();
                foreach (var item in childs)
                {
                    menuTreeItem.Childs.AddRange(GetChildMenu(item, menus));
                }
            }
            return result;
        }

        private List<MenuTreeItem> GetChilds(Menu menu, List<Menu> menus, int level)
        {
            var result = new List<MenuTreeItem>();
            var mainItem = new MenuTreeItem() { Id = menu.Id, Label = menu.Label };
            if (level > 0)
            {
                string prefix = "";
                for (int i = 0; i < level; i++)
                {
                    prefix += "|---";
                }
                mainItem.Label = prefix + mainItem.Label;
            }
            result.Add(mainItem);
            var childs = menus.Where(c => c.ParentId == menu.Id).OrderBy(m => m.DisplayOrder);
            if (childs.Any())
            {
                level += 1;
                foreach (var item in childs)
                {
                    result.AddRange(GetChilds(item, menus, level));
                }
            }
            return result;


        }


        #endregion


    }
}
