using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
using BO.AppServer.Business.Exceptions;
using BO.AppServer.Business.Mapper;
using BO.AppServer.Business.Services.Base;
using BO.AppServer.Data.Entity;
using BO.AppServer.Metadata.Configuration;
using BO.AppServer.Metadata.Dto;
using BO.AppServer.Metadata.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Quadarax.Foundation.Core.Data;
using Quadarax.Foundation.Core.Data.Interface;
using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;

namespace BO.AppServer.Business.Services
{
    public class CatalogueService : UserContextService
    {



        #region *** Private fields ***

        private readonly MimeTypeRepo _repoMimeType;
        private readonly BillingPlanRepo _repoBillingPlan;
        #endregion

        #region *** Constructors ***
        public CatalogueService(
            MimeTypeRepo repoMimeType,
            BillingPlanRepo repoBillingPlan,
            UserRepo repoUser, 
            UserManager<IdentityUser> userManager, 
            IOptions<Configuration> config,
            IPrincipal currentPrincipal, ILoggerFactory logger) : base(config, repoUser, userManager, currentPrincipal, logger)
        {
            _repoMimeType = repoMimeType ?? throw new ArgumentNullException(nameof(repoMimeType));
            _repoBillingPlan = repoBillingPlan ?? throw new ArgumentNullException(nameof(repoBillingPlan));
        }
        #endregion

        #region *** Public operations ***
        #region **** MIME Types ****
        public async Task<ResultsValueDto<MimeTypeRDto>> GetMimeTypesAsync(MimeTypeScopeEnums scope, PagingDto paging)
        {
            List<MimeType> mimeTypes = null;
            switch (scope)
            {
                case MimeTypeScopeEnums.All:
                    mimeTypes = _repoMimeType.Query(paging).ToList();
                    break;
            }

            return new ResultsValueDto<MimeTypeRDto>(mimeTypes.MapList<MimeType, MimeTypeRDto>());
        }

        public async Task<ResultValueDto<MimeTypeRDto>> CreateMimeTypeAsync(MimeTypeCDto mimeType, bool isSilent = false)
        {

            // Validation
            var exists = _repoMimeType.Query(new Paging())
                .Any(x => x.IsEnabled && x.Mimetype1 == mimeType.Mimetype1.ToLower());
            if (exists)
            {
                var exc = new EntityAlreadyExistsException(typeof(MimeType), mimeType.Mimetype1);
                Log.LogWarning(exc.Message);
                if (!isSilent)
                    throw exc;
            }

            // Create entity
            var newMimeType = _repoMimeType.New();
            mimeType.CopyToDto(newMimeType);
            newMimeType.Mimetype1 = newMimeType.Mimetype1.ToLower();
            newMimeType.Created = DateTime.Now;
            newMimeType.CreatedByUserId = GetCurrentUserId();
            newMimeType.IsEnabled = true;
            _repoMimeType.Set(newMimeType);
            _repoMimeType.Commit();

            return new ResultValueDto<MimeTypeRDto>(newMimeType.Map<MimeType, MimeTypeRDto>());
        }

        public async Task<ResultValueDto<MimeTypeRDto>> UpdateMimeTypeAsync(MimeTypeUDto mimeType)
        {
            // Validation
            var updMimeType = _repoMimeType.Get(mimeType.Id);
            if (updMimeType == null)
            {
                var exc = new EntityNotExistsException(typeof(MimeType), mimeType.Mimetype1, mimeType.Id);
                Log.LogWarning(exc.Message);
                throw exc;
            }

            // Update entity
            mimeType.CopyToDto(updMimeType);
            updMimeType.Mimetype1 = updMimeType.Mimetype1.ToLower();
            updMimeType.Modified = DateTime.Now;
            updMimeType.ModifiedByUser = GetCurrentUser();
            _repoMimeType.Set(updMimeType);
            _repoMimeType.Commit();

            return new ResultValueDto<MimeTypeRDto>(updMimeType.Map<MimeType, MimeTypeRDto>());
        }
        #endregion
        #region **** Billing Plan ****
        public async Task<ResultsValueDto<BillingPlanRDto>> GetBillingPlansAsync(BillingPlanScopeEnums scope, PagingDto paging)
        {
            List<BillingPlan> billingPlans = null;
            switch (scope)
            {
                case BillingPlanScopeEnums.All:
                    billingPlans = _repoBillingPlan.Query(paging).ToList();
                    break;
            }

            return new ResultsValueDto<BillingPlanRDto>(billingPlans.MapList<BillingPlan, BillingPlanRDto>());
        }

        public async Task<ResultValueDto<BillingPlanRDto>> CreateBillingPlanAsync(BillingPlanCDto billingPlan, bool isSilent = false)
        {
            // Validation
            var exists = _repoBillingPlan.Query(new Paging())
                .Any(x => x.IsEnabled && x.Code == billingPlan.Code.ToLower());
            if (exists)
            {
                var exc = new EntityAlreadyExistsException(typeof(BillingPlan), billingPlan.Code);
                Log.LogWarning(exc.Message);
                if (!isSilent)
                    throw exc;
            }

            if (billingPlan.Code.Length > 10)
            {
                var exc = new DataSizeValidationException(typeof(BillingPlan), nameof(BillingPlanCDto.Code), 10);
                Log.LogWarning(exc.Message);
                if (!isSilent)
                    throw exc;
            }
            if (billingPlan.PstprocessProfile.Length > 5)
            {
                var exc = new DataSizeValidationException(typeof(BillingPlan), nameof(BillingPlanCDto.PstprocessProfile), 5);
                Log.LogWarning(exc.Message);
                if (!isSilent)
                    throw exc;
            }


            // Create entity
            var newBillingPlan = _repoBillingPlan.New();
            billingPlan.Code = billingPlan.Code.ToUpper();
            billingPlan.CopyToDto(newBillingPlan);
            newBillingPlan.Created = DateTime.Now;
            newBillingPlan.CreatedByUser = GetCurrentUser();
            newBillingPlan.IsEnabled = true;
            _repoBillingPlan.Set(newBillingPlan);
            _repoBillingPlan.Commit();

            return new ResultValueDto<BillingPlanRDto>(newBillingPlan.Map<BillingPlan, BillingPlanRDto>());
        }

        public async Task<ResultValueDto<BillingPlanRDto>> UpdateBillingPlanAsync(BillingPlanUDto billingPlan)
        {
            // Validation
            var updBillingPlan = _repoBillingPlan.Get(billingPlan.Id);
            if (updBillingPlan == null)
            {
                var exc = new EntityNotExistsException(typeof(BillingPlan), billingPlan.Code, billingPlan.Id);
                Log.LogWarning(exc.Message);
                throw exc;
            }

            // Update entity
            billingPlan.CopyToDto(updBillingPlan);
            updBillingPlan.Modified = DateTime.Now;
            updBillingPlan.ModifiedByUser = GetCurrentUser();
            _repoMimeType.Commit();

            return new ResultValueDto<BillingPlanRDto>(updBillingPlan.Map<BillingPlan, BillingPlanRDto>());
        }
        #endregion
        #endregion

        #region *** Private operations ***
        #endregion


        
    }
}
