using System;
using System.Collections.Generic;
using System.Linq;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Data;
using Nop.Core.Data.Extensions;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Security;
using Nop.Core.Domain.Stores;
using Nop.Core.Domain.TopicSideMenu;
using Nop.Data;
using Nop.Services.Catalog;
using Nop.Services.Events;
using Nop.Services.Localization;
using Nop.Services.Security;
using Nop.Services.Stores;
using Nop.Services.TopicSideMenuService;

namespace Nop.Services.TobpicSideMenuB2C
{
    /// <summary>
    /// Category service
    /// </summary>
    public partial class TopicSideMenuB2CService : ITopicSideMenuB2CService
    {
        #region Fields

        private readonly CatalogSettings _catalogSettings;
        private readonly CommonSettings _commonSettings;
        private readonly IAclService _aclService;
        private readonly ICacheManager _cacheManager;
        private readonly IDataProvider _dataProvider;
        private readonly IDbContext _dbContext;
        private readonly IEventPublisher _eventPublisher;
        private readonly ILocalizationService _localizationService;
        private readonly IRepository<AclRecord> _aclRepository;
        private readonly IRepository<Category> _categoryRepository;
        private readonly IRepository<Product> _productRepository;
        private readonly IRepository<ProductCategory> _productCategoryRepository;
        private readonly IRepository<StoreMapping> _storeMappingRepository;
        private readonly IStaticCacheManager _staticCacheManager;
        private readonly IStoreContext _storeContext;
        private readonly IStoreMappingService _storeMappingService;
        private readonly IWorkContext _workContext;
        private readonly IRepository<TopicSideMenu> _topicsideMenuRepository;
        private readonly IRepository<TopicSideMenuMapping> _topicsideMenuMappingRepository;

        #endregion

        #region Ctor

        public TopicSideMenuB2CService(CatalogSettings catalogSettings,
            CommonSettings commonSettings,
            IAclService aclService,
            ICacheManager cacheManager,
            IDataProvider dataProvider,
            IDbContext dbContext,
            IEventPublisher eventPublisher,
            ILocalizationService localizationService,
            IRepository<AclRecord> aclRepository,
            IRepository<Category> categoryRepository,
            IRepository<Product> productRepository,
            IRepository<ProductCategory> productCategoryRepository,
            IRepository<StoreMapping> storeMappingRepository,
            IStaticCacheManager staticCacheManager,
            IStoreContext storeContext,
            IStoreMappingService storeMappingService,
            IWorkContext workContext,
            IRepository<TopicSideMenu> topicsideMenuRepository,
            IRepository<TopicSideMenuMapping> topicsideMenuMappingRepository)
        {
            _catalogSettings = catalogSettings;
            _commonSettings = commonSettings;
            _aclService = aclService;
            _cacheManager = cacheManager;
            _dataProvider = dataProvider;
            _dbContext = dbContext;
            _eventPublisher = eventPublisher;
            _localizationService = localizationService;
            _aclRepository = aclRepository;
            _categoryRepository = categoryRepository;
            _productRepository = productRepository;
            _productCategoryRepository = productCategoryRepository;
            _storeMappingRepository = storeMappingRepository;
            _staticCacheManager = staticCacheManager;
            _storeContext = storeContext;
            _storeMappingService = storeMappingService;
            _workContext = workContext;
            _topicsideMenuRepository = topicsideMenuRepository;
            _topicsideMenuMappingRepository = topicsideMenuMappingRepository;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Delete category
        /// </summary>
        /// <param name="category">Category</param>
        public virtual void DeleteTopicSideMenu(TopicSideMenu TopicSideMenu)
        {
            if (TopicSideMenu == null)
                throw new ArgumentNullException(nameof(TopicSideMenu));

            if (TopicSideMenu is IEntityForCaching)
                throw new ArgumentException("Cacheable entities are not supported by Entity Framework");

            TopicSideMenu.Deleted = true;
            UpdateTopicSideMenu(TopicSideMenu);

            //event notification
            _eventPublisher.EntityDeleted(TopicSideMenu);
        }

        /// <summary>
        /// Gets all categories
        /// </summary>
        /// <param name="storeId">Store identifier; 0 if you want to get all records</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <param name="loadCacheableCopy">A value indicating whether to load a copy that could be cached (workaround until Entity Framework supports 2-level caching)</param>
        /// <returns>Categories</returns>
        public virtual IList<TopicSideMenu> GetAllTopicSideMenu(int storeId = 0, bool showHidden = false, bool loadCacheableCopy = true)
        {
            IList<TopicSideMenu> LoadTopicSideMenuFunc() => GetAllTopicSideMenu(string.Empty, storeId, showHidden: showHidden);

            IList<TopicSideMenu> TopicSideMenu;
            if (loadCacheableCopy)
            {
                //cacheable copy
                var key = string.Format(NopTopicSideMenuB2CDefaults.TopMenuB2CAllCacheKey,
                    storeId,
                    string.Join(",", _workContext.CurrentCustomer.GetCustomerRoleIds()),
                    showHidden);
                TopicSideMenu = _staticCacheManager.Get(key, () =>
                {
                    var result = new List<TopicSideMenu>();
                    foreach (var category in LoadTopicSideMenuFunc())
                        result.Add(new TopicSideMenuB2CForCaching(category));
                    return result;
                });
            }
            else
            {
                TopicSideMenu = LoadTopicSideMenuFunc();
            }

            return TopicSideMenu;
        }

        /// <summary>
        /// Gets all categories
        /// </summary>
        /// <param name="categoryName">Category name</param>
        /// <param name="storeId">Store identifier; 0 if you want to get all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>Categories</returns>
        public virtual IPagedList<TopicSideMenu> GetAllTopicSideMenu(string categoryName, int storeId = 0,
            int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false)
        {
            //don't use a stored procedure. Use LINQ
            var query = _topicsideMenuRepository.Table;
            if (!showHidden)
                query = query.Where(c => c.Published);
            if (!string.IsNullOrWhiteSpace(categoryName))
                query = query.Where(c => c.Name.Contains(categoryName));
            query = query.Where(c => !c.Deleted);
            query = query.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Id);

            var unsortedCategories = query.ToList();

            //sort categories
            var sortedCategories = SortTopicSideMenuForTree(unsortedCategories);

            //paging
            return new PagedList<TopicSideMenu>(sortedCategories, pageIndex, pageSize);
        }

        /// <summary>
        /// Gets a category
        /// </summary>
        /// <param name="categoryId">Category identifier</param>
        /// <returns>Category</returns>
        public virtual TopicSideMenu GetTopicSideMenuById(int TopicSideMenuId)
        {
            if (TopicSideMenuId == 0)
                return null;

            var key = string.Format(NopCatalogDefaults.CategoriesByIdCacheKey, TopicSideMenuId);
            return _cacheManager.Get(key, () => _topicsideMenuRepository.GetById(TopicSideMenuId));
        }

        /// <summary>
        /// Inserts category
        /// </summary>
        /// <param name="category">Category</param>
        public virtual void InsertTopicSideMenu(TopicSideMenu SideMneu)
        {
            if (SideMneu == null)
                throw new ArgumentNullException(nameof(SideMneu));

            if (SideMneu is IEntityForCaching)
                throw new ArgumentException("Cacheable entities are not supported by Entity Framework");

            _topicsideMenuRepository.Insert(SideMneu);

            //cache
            _cacheManager.RemoveByPrefix(NopCatalogDefaults.CategoriesPrefixCacheKey);
            _staticCacheManager.RemoveByPrefix(NopCatalogDefaults.CategoriesPrefixCacheKey);
            _cacheManager.RemoveByPrefix(NopCatalogDefaults.ProductCategoriesPrefixCacheKey);

            //event notification
            _eventPublisher.EntityInserted(SideMneu);
        }

        /// <summary>
        /// Updates the category
        /// </summary>
        /// <param name="category">Category</param>
        public virtual void UpdateTopicSideMenu(TopicSideMenu TopicSideMenu)
        {
            if (TopicSideMenu == null)
                throw new ArgumentNullException(nameof(TopicSideMenu));

            if (TopicSideMenu is IEntityForCaching)
                throw new ArgumentException("Cacheable entities are not supported by Entity Framework");


            _topicsideMenuRepository.Update(TopicSideMenu);

            ////cache
            //_cacheManager.RemoveByPrefix(NopCatalogDefaults.CategoriesPrefixCacheKey);
            //_staticCacheManager.RemoveByPrefix(NopCatalogDefaults.CategoriesPrefixCacheKey);
            //_cacheManager.RemoveByPrefix(NopCatalogDefaults.ProductCategoriesPrefixCacheKey);

            //event notification
            _eventPublisher.EntityUpdated(TopicSideMenu);
        }

        /// <summary>
        /// Sort categories for tree representation
        /// </summary>
        /// <param name="source">Source</param>
        /// <param name="parentId">Parent category identifier</param>
        /// <param name="ignoreCategoriesWithoutExistingParent">A value indicating whether categories without parent category in provided category list (source) should be ignored</param>
        /// <returns>Sorted categories</returns>
        public virtual IList<TopicSideMenu> SortTopicSideMenuForTree(IList<TopicSideMenu> source, int parentId = 0,
            bool ignoreCategoriesWithoutExistingParent = false)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            var result = new List<TopicSideMenu>();


            //find categories without parent in provided category source and insert them into result
            foreach (var cat in source)
                if (result.FirstOrDefault(x => x.Id == cat.Id) == null)
                    result.Add(cat);

            return result;
        }

        #endregion

        #region MyRegion

        /// <summary>
        /// Inserts category
        /// </summary>
        /// <param name="category">Category</param>
        public virtual void InsertTopicSideMenuMapping(TopicSideMenuMapping SideMneu)
        {
            if (SideMneu == null)
                throw new ArgumentNullException(nameof(SideMneu));

            if (SideMneu is IEntityForCaching)
                throw new ArgumentException("Cacheable entities are not supported by Entity Framework");

            _topicsideMenuMappingRepository.Insert(SideMneu);

            //event notification
            _eventPublisher.EntityInserted(SideMneu);
        }

        /// <summary>
        /// Updates the category
        /// </summary>
        /// <param name="category">Category</param>
        public virtual void UpdateTopicSideMenuMapping(TopicSideMenuMapping TopicSideMenu)
        {
            if (TopicSideMenu == null)
                throw new ArgumentNullException(nameof(TopicSideMenu));

            if (TopicSideMenu is IEntityForCaching)
                throw new ArgumentException("Cacheable entities are not supported by Entity Framework");


            _topicsideMenuMappingRepository.Update(TopicSideMenu);

            //event notification
            _eventPublisher.EntityUpdated(TopicSideMenu);
        }

        /// <summary>
        /// Gets a category
        /// </summary>
        /// <param name="categoryId">Category identifier</param>
        /// <returns>Category</returns>
        public virtual TopicSideMenuMapping GetTopicSideMenuMappingByTopicId(int TopicId)
        {
            if (TopicId == 0)
                return null;
            return _topicsideMenuMappingRepository.Table.Where(x => x.TopicId == TopicId).FirstOrDefault();
        }


        #endregion
    }
}