
using Lada.Framework.Data.Domains;
using Lada.Framework.Data.Domains.Categories;
using Lada.Framework.Data.Domains.Cms;
using Lada.Framework.Data.Enums;
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.Items.Cms;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Categories;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Cms;
using Ladaer.BackEnd.Infrastructures.DTO.Results.Categories;
using Ladaer.BackEnd.Infrastructures.DTO.Results.Cms;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;

namespace Ladaer.BackEnd.Infrastructures.Services.Cms
{
    /// <summary>
    /// Giao diện cho dịch vụ quản lý danh mục.
    /// </summary>
    public interface ICategoryService
    {
        /// <summary>
        /// Tìm kiếm danh mục.
        /// </summary>
        /// <param name="param">Thông số tìm kiếm.</param>
        /// <returns>Kết quả tìm kiếm danh mục.</returns>
        Task<SmartTableResult<CategoryItem>> Search(SmartTableParam param);

        /// <summary>
        /// Lấy cây danh mục.
        /// </summary>
        /// <param name="keyword">Từ khóa tìm kiếm.</param>
        /// <returns>Danh sách cây danh mục.</returns>
        Task<List<CategoryTreeItem>> GetTree(string keyword, ContentType contentType = ContentType.Post);

        /// <summary>
        /// Tạo hoặc cập nhật danh mục.
        /// </summary>
        /// <param name="model">Thông tin danh mục.</param>
        /// <returns>Kết quả của thao tác.</returns>
        Task<CategoryResult> CreateOrUpdate(CategoryModel model);

        /// <summary>
        /// Lấy thông tin danh mục qua Id.
        /// </summary>
        /// <param name="CategoryId">Id của danh mục.</param>
        /// <returns>Thông tin danh mục.</returns>
        Task<CategoryModel> GetById(int CategoryId);

        /// <summary>
        /// Xóa danh mục.
        /// </summary>
        /// <param name="CategoryId">Id của danh mục.</param>
        /// <returns>Kết quả của thao tác.</returns>
        Task<BaseResult> Delete(int CategoryId);
    }


    /// <summary>
    /// Triển khai dịch vụ quản lý danh mục.
    /// </summary>
    public class CategoryService : ICategoryService
    {
        private readonly IRepository<Category> _categoryRepository;
        private readonly IUrlRecordService _urlRecordService;

        /// <summary>
        /// Khởi tạo một thể hiện của <see cref="CategoryService"/>.
        /// </summary>
        /// <param name="categoryRepository">Đối tượng IRepository của Category.</param>
        /// <param name="urlRecordService">Dịch vụ quản lý UrlRecord.</param>
        public CategoryService(IRepository<Category> categoryRepository, IUrlRecordService urlRecordService)
        {
            _categoryRepository = categoryRepository;
            _urlRecordService = urlRecordService;
        }

        /// <summary>
        /// Tạo hoặc cập nhật danh mục.
        /// </summary>
        /// <param name="model">Thông tin danh mục.</param>
        /// <returns>Kết quả của thao tác.</returns>
        public async Task<CategoryResult> CreateOrUpdate(CategoryModel model)
        {
            var category = model.ToEntity();

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

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

        #region CreateOrUpdate
        /// <summary>
        /// Tạo danh mục.
        /// </summary>
        /// <param name="category">Thông tin danh mục.</param>
        /// <returns>Kết quả của thao tác.</returns>
        private async Task<CategoryResult> Create(Category category)
        {
            var result = new CategoryResult();
            try
            {
                var slug = category.Name.NormalizeUrl();
                slug = await _urlRecordService.GenerateUrlRecord(slug);
                category.UrlRecord = new UrlRecord()
                {
                    MetaTitle = category.Name,
                    MetaDescription = category.Description,
                    MetaKeywords = "",
                    Slug = slug,
                    CreatedBy = category.CreatedBy,
                    CreatedByUserName = category.CreatedByUserName,
                    UpdatedBy = category.UpdatedBy,
                    UpdatedByUserName = category.UpdatedByUserName,
                    CreatedTime = category.CreatedTime,
                    UpdatedTime = category.UpdatedTime,
                    EntityName = "Category",
                    EntityDescription = ""
                };

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

        /// <summary>
        /// Cập nhật danh mục.
        /// </summary>
        /// <param name="category">Thông tin danh mục.</param>
        /// <returns>Kết quả của thao tác.</returns>
        private async Task<CategoryResult> Update(Category category)
        {
            var result = new CategoryResult() { Result = Result.Success };
            var categoryForUpdate = await _categoryRepository.Query().Include(c => c.UrlRecord).FirstOrDefaultAsync(c => c.Id == category.Id);
            if (categoryForUpdate == null || category.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy category yêu cầu!";
                return result;
            }

            try
            {
                var urlRecordId = (categoryForUpdate.UrlRecord != null && categoryForUpdate.UrlRecord.Id > 0) ? categoryForUpdate.UrlRecord.Id : 0;
                var slug = category.Name.NormalizeUrl();
                
                if (urlRecordId > 0)
                {
                    slug = await _urlRecordService.GenerateUrlRecord(slug, urlRecordId);

                    if (categoryForUpdate.UrlRecord.Slug != slug)
                    {
                        categoryForUpdate.UrlRecord.Slug = slug;
                    }
                }
                else
                {
                    slug = await _urlRecordService.GenerateUrlRecord(slug);
                    //Nếu không có thì tạo
                    categoryForUpdate.UrlRecord = new UrlRecord()
                    {
                        MetaTitle = category.Name,
                        MetaDescription = category.Description,
                        MetaKeywords = "",
                        Slug = slug,
                        CreatedBy = category.CreatedBy,
                        CreatedByUserName = category.CreatedByUserName,
                        UpdatedBy = category.UpdatedBy,
                        UpdatedByUserName = category.UpdatedByUserName,
                        CreatedTime = category.CreatedTime,
                        UpdatedTime = category.UpdatedTime,
                        EntityName = "Category",
                        EntityDescription = ""
                    };
                }

                categoryForUpdate.Name = category.Name;
                categoryForUpdate.DisplayOrder = category.DisplayOrder;
                categoryForUpdate.Description = category.Description;
                categoryForUpdate.ParentId = category.ParentId;
                categoryForUpdate.IsFeatured = category.IsFeatured;
                categoryForUpdate.ContentType = category.ContentType;
                await _categoryRepository.UpdateAsync(categoryForUpdate);
                result.Id = categoryForUpdate.Id;
            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.ToString();
            }

            return result;
        }
        #endregion

        /// <summary>
        /// Xóa danh mục.
        /// </summary>
        /// <param name="categoryId">Id của danh mục.</param>
        /// <returns>Kết quả của thao tác.</returns>
        public async Task<BaseResult> Delete(int categoryId)
        {
            var result = new BaseResult();
            if (categoryId <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Yêu cầu xóa danh mục không hợp lệ";
                return result;
            }
            var categoryForUpdate = await _categoryRepository.Query().FirstOrDefaultAsync(c => c.Id == categoryId);
            if (categoryForUpdate == null || categoryForUpdate.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy danh mục yêu cầu";
                return result;
            }

            var childs = await _categoryRepository.Query().CountAsync(c => c.ParentId == categoryId);
            if (childs > 0)
            {
                result.Result = Result.Failed;
                result.Message = "Danh mục có chứa danh mục con, vui lòng xóa danh mục con trước";
                return result;
            }
            try
            {
                // Cập nhật thông tin chung của thực thể
                categoryForUpdate = categoryForUpdate.UpdateCommonInt();
                var delResult = await _categoryRepository.DeleteAsync(categoryForUpdate);
                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;
        }

        /// <summary>
        /// Lấy thông tin danh mục qua Id.
        /// </summary>
        /// <param name="categoryId">Id của danh mục.</param>
        /// <returns>Thông tin danh mục.</returns>
        public async Task<CategoryModel> GetById(int categoryId)
        {
            var result = new CategoryModel();
            if (categoryId <= 0)
            {
                return result;
            }
            var categoryForUpdate = await _categoryRepository.Query().FirstOrDefaultAsync(c => c.Id == categoryId);
            if (categoryForUpdate == null || categoryForUpdate.Id <= 0)
            {
                return result;
            }
            return categoryForUpdate.ToModel();
        }

        /// <summary>
        /// Tìm kiếm danh mục.
        /// </summary>
        /// <param name="param">Thông số tìm kiếm.</param>
        /// <returns>Kết quả tìm kiếm danh mục.</returns>
        public async Task<SmartTableResult<CategoryItem>> Search(SmartTableParam param)
        {
            var query = _categoryRepository.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.Name.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.ParentId != null)
                {
                    int parentId = search.ParentId;
                    query = query.Where(x => x.ParentId == parentId);
                }

                if (search.ContentType != null)
                {
                    ContentType contentType = search.ContentType;
                    query = query.Where(x => x.ContentType == contentType);
                }
            }
            param.Sort = new Sort() { Predicate = "Id", Reverse = true };
            var gridData = await query.Include(c => c.UrlRecord).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 = _categoryRepository.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.Name;
                        }
                        else item.ParentName = "";
                    }
                }
                #endregion
            }
            return gridData;
        }

        /// <summary>
        /// Lấy cây danh mục.
        /// </summary>
        /// <param name="keyword">Từ khóa tìm kiếm.</param>
        /// <returns>Danh sách cây danh mục.</returns>
        public async Task<List<CategoryTreeItem>> GetTree(string keyword, ContentType contentType = ContentType.Post)
        {
            var result = new List<CategoryTreeItem>();
            var categoryList = new List<Category>();
            var query = _categoryRepository.Query().Where(c => c.ContentType == contentType);
            if (!string.IsNullOrEmpty(keyword))
            {
                keyword = keyword.Trim();
                categoryList = await query.Where(t => t.Name.Contains(keyword)).ToListAsync();
            }
            else
            {
                categoryList = await query.ToListAsync();
            }

            var rootItems = categoryList.Where(c => c.ParentId == 0).OrderBy(c => c.DisplayOrder).ToList();
            foreach (var item in rootItems)
            {
                result.AddRange(GetChilds(item, categoryList, 0));
            }
            return result;
        }

        /// <summary>
        /// Lấy danh sách con của danh mục.
        /// </summary>
        /// <param name="category">Danh mục.</param>
        /// <param name="categorys">Danh sách danh mục.</param>
        /// <param name="level">Cấp độ.</param>
        /// <returns>Danh sách con của danh mục.</returns>
        private List<CategoryTreeItem> GetChilds(Category category, List<Category> categorys, int level)
        {
            var result = new List<CategoryTreeItem>();
            var mainItem = new CategoryTreeItem() { Id = category.Id, Name = category.Name };
            if (level > 0)
            {
                string prefix = "";
                for (int i = 0; i < level; i++)
                {
                    prefix += "|---";
                }
                mainItem.RawName = mainItem.Name;
                mainItem.Name = prefix + mainItem.Name;
            }
            else
            {
                mainItem.RawName = mainItem.Name;
            }
            result.Add(mainItem);
            var childs = categorys.Where(c => c.ParentId == category.Id).OrderBy(m => m.DisplayOrder);
            if (childs.Any())
            {
                level += 1;
                foreach (var item in childs)
                {
                    result.AddRange(GetChilds(item, categorys, level));
                }
            }
            return result;
        }


    }
}
