using Lada.Framework.Data.Domains;
using Lada.Framework.Data.Domains.Cms;
using Lada.Framework.Data.Repositories;
using Lada.Framework.DTO;
using Lada.Framework.DTO.SmartTable;
using Lada.Framework.Infrastructures.AppSettings;
using Lada.Framework.Infrastructures.Helpers;
using Ladaer.BackEnd.Infrastructures.AppSettings;
using Ladaer.BackEnd.Infrastructures.DTO;
using Ladaer.BackEnd.Infrastructures.DTO.Items.Cms;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Cms;
using Ladaer.BackEnd.Infrastructures.DTO.Results.Cms;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Drawing.Imaging;
using System.Linq;

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

        /// <summary>
        /// Tạo / cập nhật bài viết
        /// </summary>
        /// <param name="model">Thông tin bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        Task<PostResult> CreateOrUpdate(PostModel model);

        /// <summary>
        /// Lấy thông tin bài viết qua Id
        /// </summary>
        /// <param name="postId">Id của bài viết.</param>
        /// <returns>Thông tin bài viết.</returns>
        Task<PostModel> GetById(int postId);

        /// <summary>
        /// Xóa bài viết
        /// </summary>
        /// <param name="postId">Id của bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        Task<BaseResult> Delete(int postId);

        /// <summary>
        /// Cập nhật trạng thái bài viết
        /// </summary>
        /// <param name="model">Thông tin trạng thái bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        Task<BaseResult> UpdatePostStatus(PostStatusModel model);
    }


    /// <summary>
    /// Triển khai dịch vụ quản lý bài viết.
    /// </summary>
    public class PostService : IPostService
    {
        private readonly IRepository<Post> _postRepository;
        private readonly IRepository<Category> _categoryRepository;
        private readonly IRepository<Tag> _tagRepository;
        private readonly IRepository<PostTag> _postTagRepository;
        private readonly IRepository<PostCategory> _postCategoryRepository;
        private readonly IUrlRecordService _urlRecordService;
        private readonly IRepository<UrlRecord> _urlRecordRepository;
        private CmsSetting _cmsSettings;
        private readonly ITagService _tagService;
        private readonly IPostCategoryService _postCategoryService;

        /// <summary>
        /// Khởi tạo một thể hiện của <see cref="PostService"/>.
        /// </summary>
        /// <param name="postRepository">Đối tượng IRepository của Post.</param>
        /// <param name="urlRecordService">Dịch vụ quản lý UrlRecord.</param>
        /// <param name="cmsSettings">Cài đặt CMS.</param>
        /// <param name="categoryRepository">Đối tượng IRepository của Category.</param>
        /// <param name="tagRepository">Đối tượng IRepository của Tag.</param>
        /// <param name="urlRecordRepository">Đối tượng IRepository của UrlRecord.</param>
        public PostService(IRepository<Post> postRepository, IUrlRecordService urlRecordService,
            IOptions<CmsSetting> cmsSettings, IRepository<Category> categoryRepository, IRepository<Tag> tagRepository,
            IRepository<UrlRecord> urlRecordRepository, ITagService tagService, IPostCategoryService postCategoryService,
            IRepository<PostTag> postTagRepository, IRepository<PostCategory> postCategoryRepository)
        {
            _postRepository = postRepository;
            _urlRecordService = urlRecordService;
            _cmsSettings = cmsSettings.Value;
            _categoryRepository = categoryRepository;
            _tagRepository = tagRepository;
            _urlRecordRepository = urlRecordRepository;
            _tagService = tagService;
            _postCategoryService = postCategoryService;
            _postTagRepository = postTagRepository;
            _postCategoryRepository = postCategoryRepository;
        }

        #region SEARCH
        /// <summary>
        /// Tìm kiếm bài viết
        /// </summary>
        /// <param name="param">Thông số tìm kiếm.</param>
        /// <returns>Kết quả tìm kiếm bài viết.</returns>
        public async Task<SmartTableResult<PostItem>> Search(SmartTableParam param)
        {
            var query = _postRepository.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.Title.ToLower().Contains(keyword));
                }
                if (search.CreatedStart != null && search.CreatedEnd != null)
                {
                    DateTimeOffset start = search.CreatedStart;
                    DateTimeOffset end = search.CreatedEnd;
                    DateTimeOffset startParam = start.ToLocalTime().StartOfDay();
                    DateTimeOffset endParam = end.EndOfDay().ToLocalTime().EndOfDay();
                    query = query.Where(x => x.CreatedTime >= startParam && x.CreatedTime <= endParam);
                }
                if (search.CategoryId != null)
                {
                    int catId = search.CategoryId;
                    if (catId > 0)
                    {
                        query = query.Where(x => x.PostCategories != null && x.PostCategories.Any(pc => pc.CategoryId == catId));
                    }
                }
            }
            param.Sort = new Sort() { Predicate = "Id", Reverse = true };
            var gridData = await query.Include(p => p.UrlRecord).Include(p => p.PostCategories).ToSmartTableResultAsync(param, x => x.ToItem());
            return gridData;
        }
        #endregion

        #region CRUD

        #region CreateOrUpdate
        /// <summary>
        /// Tạo hoặc cập nhật bài viết
        /// </summary>
        /// <param name="model">Thông tin bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        public async Task<PostResult> CreateOrUpdate(PostModel model)
        {
            var post = model.ToEntity();
            // Cập nhật thông tin chung của thực thể
            //post = post.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);
            //if (model.PostCategoryModels.Any())
            //{
            //    post.Categories = await UpdatePostCategories(model.PostCategoryModels);
            //}
            //else
            //{
            //    post.Categories = new List<Category>();
            //}
            //if (model.Tags.Any())
            //{
            //    post.Tags = await UpdatePostTags(model.Tags, model.UpdatedBy, model.UpdatedByUserName);
            //}
            //else
            //{
            //    post.Tags = new List<Tag>();
            //}

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

        /// <summary>
        /// Cập nhật bài viết
        /// </summary>
        /// <param name="post">Thông tin bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        private async Task<PostResult> Update(PostModel model)
        {
            var result = new PostResult() { Result = Result.Success };
            var postForUpdate = _postRepository.Query().FirstOrDefault(c => c.Id == model.Id);
            var currentTime = DateTimeHelper.CurrentTime();
            if (postForUpdate == null || model.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy bài viết yêu cầu!";
                return result;
            }

            try
            {
                // Cập nhật thông tin của bài viết
                postForUpdate.Title = model.Title.Trim();
                postForUpdate.Description = model.Description.Trim();
                postForUpdate.ShortDescription = model.ShortDescription;
                postForUpdate.Thumbnail = model.Thumbnail;
                postForUpdate.Visibility = model.Visibility;
                postForUpdate.IsFeatured = model.IsFeatured;
                postForUpdate.TOC = model.TOC;
                if (model.ValidFrom.HasValue)
                {
                    postForUpdate.ValidFrom = model.ValidFrom;
                }
                // Cập nhật thông tin chung của thực thể
                postForUpdate = postForUpdate.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

                await _postRepository.UpdateAsync(postForUpdate);

                #region Categories

                await _postCategoryService.RemovePostCategoryByPostIdAsync(model.Id);

                var catIds = model.PostCategoryModels.Select(pc => pc.Id).ToList();
                var cats = await _categoryRepository.Query().Where(c => catIds.Contains(c.Id)).ToListAsync();
                var postCats = new List<PostCategory>();
                foreach (var cat in cats)
                {
                    postCats.Add(new PostCategory() { CategoryId = cat.Id, PostId = postForUpdate.Id, CreatedTime = currentTime, UpdatedTime = currentTime, CreatedBy = model.UpdatedBy, UpdatedBy = model.UpdatedBy, CreatedByUserName = model.UpdatedByUserName, UpdatedByUserName = model.UpdatedByUserName });
                }
                if (postCats.Any())
                {
                    await _postCategoryRepository.InsertAsync(postCats);
                }

                #endregion

                #region Tags
                await _tagService.RemovePostTagByPostIdAsync(postForUpdate.Id);
                var tagNames = model.Tags.Select(t => t.Name.ToLower()).Distinct().ToList();
                var existTags = await _tagRepository.Query().Where(t => tagNames.Contains(t.Name.ToLower())).ToListAsync();
                var addTagNames = new List<string>();
                var addedTags = new List<Tag>();
                var postTags = new List<PostTag>();
                foreach (var tagName in tagNames.Distinct(StringComparer.OrdinalIgnoreCase))
                {
                    var curTag = existTags.FirstOrDefault(t => t.Name.ToLower() == tagName);
                    if (curTag == null || curTag.Id <= 0)
                    {
                        curTag = await _tagService.CreateAsync(tagName, model.UpdatedBy, model.UpdatedByUserName);
                    }

                    postTags.Add(new PostTag { TagId = curTag.Id, PostId = postForUpdate.Id, CreatedTime = currentTime, UpdatedTime = currentTime, CreatedBy = model.UpdatedBy, UpdatedBy = model.UpdatedBy, CreatedByUserName = model.UpdatedByUserName, UpdatedByUserName = model.UpdatedByUserName });

                }
                if (postTags.Any())
                {
                    await _postTagRepository.InsertAsync(postTags);
                }
                #endregion


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

            return result;
        }

        /// <summary>
        /// Tạo bài viết
        /// </summary>
        /// <param name="post">Thông tin bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        private async Task<PostResult> Create(PostModel model)
        {
            var result = new PostResult();
            var currentTime = DateTimeHelper.CurrentTime();
            try
            {
                var slug = model.Title.NormalizeUrl();
                slug = await _urlRecordService.GenerateUrlRecord(slug);

                if (!model.ValidFrom.HasValue)
                {
                    model.ValidFrom = DateTimeOffset.Now;
                }

                #region Commons
                var post = model.ToEntity().UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

                post.UrlRecord = new UrlRecord()
                {
                    MetaTitle = post.Title.CutWordByLength(_cmsSettings.SeoSetting.MetaTitleLength),
                    MetaDescription = post.ShortDescription.CutWordByLength(_cmsSettings.SeoSetting.MetaDescriptionLength),
                    MetaKeywords = "",
                    Slug = slug,
                    CreatedBy = post.CreatedBy,
                    CreatedByUserName = post.CreatedByUserName,
                    UpdatedBy = post.UpdatedBy,
                    UpdatedByUserName = post.UpdatedByUserName,
                    CreatedTime = post.CreatedTime,
                    UpdatedTime = post.UpdatedTime,
                    EntityName = "Post",
                    EntityDescription = ""
                };
                if (string.IsNullOrEmpty(post.Password))
                {
                    post.Password = string.Empty;
                }
                await _postRepository.InsertAsync(post);
                #endregion


                #region Tags
                var tagNames = model.Tags.Select(t => t.Name.ToLower()).Distinct().ToList();
                var existTags = await _tagRepository.Query().Where(t => tagNames.Contains(t.Name.ToLower())).ToListAsync();
                var addTagNames = new List<string>();
                var addedTags = new List<Tag>();
                var postTags = new List<PostTag>();
                foreach (var tagName in tagNames.Distinct(StringComparer.OrdinalIgnoreCase))
                {
                    var curTag = existTags.FirstOrDefault(t => t.Name.ToLower() == tagName);
                    if (curTag == null || curTag.Id <= 0)
                    {
                        curTag = await _tagService.CreateAsync(tagName, model.UpdatedBy, model.UpdatedByUserName);
                    }

                    postTags.Add(new PostTag { TagId = curTag.Id, PostId = post.Id, CreatedTime = currentTime, UpdatedTime = currentTime, CreatedBy = model.UpdatedBy, UpdatedBy = model.UpdatedBy, CreatedByUserName = model.UpdatedByUserName, UpdatedByUserName = model.UpdatedByUserName });

                }
                if (postTags.Any())
                {
                    await _postTagRepository.InsertAsync(postTags);
                }
                #endregion

                #region Category
                var catIds = model.PostCategoryModels.Select(pc => pc.Id).ToList();
                var cats = await _categoryRepository.Query().Where(c => catIds.Contains(c.Id)).ToListAsync();

                var postCats = new List<PostCategory>();
                foreach (var cat in cats)
                {
                    postCats.Add(new PostCategory() { CategoryId = cat.Id, PostId = post.Id, CreatedTime = currentTime, UpdatedTime = currentTime, CreatedBy = model.UpdatedBy, UpdatedBy = model.UpdatedBy, CreatedByUserName = model.UpdatedByUserName, UpdatedByUserName = model.UpdatedByUserName });
                }
                if (postCats.Any())
                {
                    await _postCategoryRepository.InsertAsync(postCats);
                }
                #endregion

                post.Faqs = new List<Faq>();

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

        /// <summary>
        /// Xóa bài viết
        /// </summary>
        /// <param name="postId">Id của bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        public async Task<BaseResult> Delete(int postId)
        {
            var result = new BaseResult();
            if (postId <= 0) return result;
            var post = await _postRepository.Query().FirstOrDefaultAsync(c => c.Id == postId);

            if (post != null && post.Id > 0)
            {
                var deleteResult = await _postRepository.DeleteAsync(post);
                if (deleteResult <= 0)
                {
                    result.Result = Result.SystemError;
                    result.Message = "Không thể xóa được";
                }

                var urlRecord = await _urlRecordRepository.Query().FirstOrDefaultAsync(u => u.PostId == postId);
                if (urlRecord != null && urlRecord.Id > 0)
                {
                    var deleteUrlResult = await _urlRecordRepository.DeleteAsync(urlRecord);
                    if (deleteUrlResult <= 0)
                    {
                        result.Result = Result.SystemError;
                        result.Message = "Không thể xóa urlrecord";
                    }
                }
            }
            else
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy bài viết yêu cầu";
            }
            return result;
        }

        /// <summary>
        /// Lấy thông tin bài viết qua Id
        /// </summary>
        /// <param name="postId">Id của bài viết.</param>
        /// <returns>Thông tin bài viết.</returns>
        public async Task<PostModel> GetById(int postId)
        {
            var model = new PostModel();
            if (postId <= 0) return model;
            var post = await _postRepository.Query()
                .Include(p => p.PostCategories!).ThenInclude(pc => pc.Category!)
                .Include(p => p.PostTags!).ThenInclude(pc => pc.Tag!)
                .Include(p => p.UrlRecord)
                .FirstOrDefaultAsync(c => c.Id == postId);
            if (post != null && post.Id > 0)
            {
                model = post.ToModel();
            }
            return model;
        }

        /// <summary>
        /// Cập nhật trạng thái bài viết
        /// </summary>
        /// <param name="model">Thông tin trạng thái bài viết.</param>
        /// <returns>Kết quả của thao tác.</returns>
        public async Task<BaseResult> UpdatePostStatus(PostStatusModel model)
        {
            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 postForUpdate = await _postRepository.Query().FirstOrDefaultAsync(c => c.Id == model.Id);
            if (postForUpdate == null || postForUpdate.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy tin tuyển dụng yêu cầu";
                return result;
            }
            if (model.Status == postForUpdate.Status)
            {
                result.Result = Result.Failed;
                result.Message = "Trạng thái tin đăng đang là: " + model.Status.GetDescription();
                return result;
            }

            try
            {
                postForUpdate.Status = model.Status;
                postForUpdate = postForUpdate.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);
                var udpResult = await _postRepository.UpdateAsync(postForUpdate);
                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

        /// <summary>
        /// Cập nhật danh mục bài viết
        /// </summary>
        /// <param name="postCategories">Danh sách danh mục bài viết.</param>
        /// <returns>Danh sách danh mục đã cập nhật.</returns>
        private async Task<List<Category>> UpdatePostCategories(List<PostCategoryModel> postCategories)
        {
            var result = new List<Category>();
            var categoryIds = postCategories.Select(pc => pc.Id).ToList();
            result = _categoryRepository.Query().Where(c => categoryIds.Contains(c.Id)).ToList();
            return result;
        }

        /// <summary>
        /// Cập nhật thẻ bài viết
        /// </summary>
        /// <param name="postTags">Danh sách thẻ bài viết.</param>
        /// <param name="createdBy">Người tạo.</param>
        /// <param name="createdByUserName">Tên người tạo.</param>
        /// <returns>Danh sách thẻ đã cập nhật.</returns>
        private async Task<List<Tag>> UpdatePostTags(List<PostTagModel> postTags, int createdBy, string createdByUserName)
        {
            var result = new List<Tag>();
            var tagNames = postTags.Select(t => t.Name).ToList();
            var existTags = _tagRepository.Query().Where(t => tagNames.Contains(t.Name)).ToList();
            var existTagsName = existTags.Select(t => t.Name).ToList();
            var newTagNames = tagNames.Except(existTagsName).ToList();
            var currentTime = DateTimeHelper.CurrentTime();
            if (newTagNames.Any())
            {
                var newTags = new List<Tag>();
                foreach (var newtagName in newTagNames)
                {
                    var slug = newtagName.NormalizeUrl();
                    slug = await _urlRecordService.GenerateUrlRecord(slug, UrlEntityType.Tag);
                    var tag = new Tag()
                    {
                        Name = newtagName,
                        Description = "",
                        UrlRecord = new UrlRecord()
                        {
                            MetaTitle = newtagName,
                            MetaDescription = newtagName,
                            MetaKeywords = newtagName + "," + newtagName.Unsigned(),
                            Slug = slug,
                            CreatedBy = createdBy,
                            CreatedByUserName = createdByUserName,
                            UpdatedBy = createdBy,
                            UpdatedByUserName = createdByUserName,
                            CreatedTime = currentTime,
                            UpdatedTime = currentTime,
                            EntityName = "Tag",
                            EntityDescription = ""
                        },
                        CreatedBy = createdBy,
                        CreatedByUserName = createdByUserName,
                        UpdatedBy = createdBy,
                        UpdatedByUserName = createdByUserName,
                        CreatedTime = currentTime,
                        UpdatedTime = currentTime,
                    };
                    newTags.Add(tag);
                }
                await _tagRepository.InsertAsync(newTags);
                result = _tagRepository.Query().Where(t => tagNames.Contains(t.Name)).ToList();
            }
            else
            {
                result = existTags;
            }
            return result;
        }
    }
}
