using AyCode.Core.Extensions;
using FruitBank.Common.Interfaces;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Tax;
using Nop.Plugin.Misc.FruitBankPlugin.Helpers;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Affiliates;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Configuration;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Discounts;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Media;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Security;
using Nop.Services.Seo;
using Nop.Services.Shipping;
using Nop.Services.Stores;
using Nop.Services.Tax;
using Nop.Services.Vendors;
using Nop.Web.Areas.Admin.Factories;
using Nop.Web.Areas.Admin.Models.Orders;
using System.Collections;
using System.Reflection;
using System.Runtime.Intrinsics.Arm;
using AyCode.Core.Helpers;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase;
using FruitBank.Common.Dtos;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order;
using Mango.Nop.Core.Extensions;

namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
{
    public class CustomOrderModelFactory : MgOrderModelFactory<OrderListModelExtended, OrderModelExtended>
    {
        private readonly FruitBankDbContext _ctx;
        private readonly IOrderMeasurementService _orderMeasurementService;

        #region Ctor
        public CustomOrderModelFactory(
            FruitBankDbContext ctx,
            IOrderMeasurementService orderMeasurementService,
            AddressSettings addressSettings,
        CatalogSettings catalogSettings,
        CurrencySettings currencySettings,
        IActionContextAccessor actionContextAccessor,
        IAddressModelFactory addressModelFactory,
        IAddressService addressService,
        IAffiliateService affiliateService,
        IBaseAdminModelFactory baseAdminModelFactory,
        ICountryService countryService,
        ICurrencyService currencyService,
        ICustomerService customerService,
        IDateTimeHelper dateTimeHelper,
        IDiscountService discountService,
        IDownloadService downloadService,
        IEncryptionService encryptionService,
        IGiftCardService giftCardService,
        ILocalizationService localizationService,
        IMeasureService measureService,
        IOrderProcessingService orderProcessingService,
        IOrderReportService orderReportService,
        IOrderService orderService,
        IPaymentPluginManager paymentPluginManager,
        IPaymentService paymentService,
        IPictureService pictureService,
        IPriceCalculationService priceCalculationService,
        IPriceFormatter priceFormatter,
        IProductAttributeService productAttributeService,
        IProductService productService,
        IReturnRequestService returnRequestService,
        IRewardPointService rewardPointService,
        ISettingService settingService,
        IShipmentService shipmentService,
        IShippingService shippingService,
        IStateProvinceService stateProvinceService,
        IStoreService storeService,
        ITaxService taxService,
        IUrlHelperFactory urlHelperFactory,
        IVendorService vendorService,
        IWorkContext workContext,
        MeasureSettings measureSettings,
        NopHttpClient nopHttpClient,
        OrderSettings orderSettings,
        ShippingSettings shippingSettings,
        IUrlRecordService urlRecordService,
        TaxSettings taxSettings,
        IGenericAttributeService genericAttributeService
        ) : base(addressSettings,
            catalogSettings,
            currencySettings,
            actionContextAccessor,
            addressModelFactory,
            addressService,
            affiliateService,
            baseAdminModelFactory,
            countryService,
            currencyService,
            customerService,
            dateTimeHelper,
            discountService,
            downloadService,
            encryptionService,
            giftCardService,
            localizationService,
            measureService,
            orderProcessingService,
            orderReportService,
            orderService,
            paymentPluginManager,
            paymentService,
            pictureService,
            priceCalculationService,
            priceFormatter,
            productAttributeService,
            productService,
            returnRequestService,
            rewardPointService,
            settingService,
            shipmentService,
            shippingService,
            stateProvinceService,
            storeService,
            taxService,
            urlHelperFactory,
            vendorService,
            workContext,
            measureSettings,
            nopHttpClient,
            orderSettings,
            shippingSettings,
            urlRecordService,
            taxSettings,
            genericAttributeService
            )
        {
            _ctx = ctx;
            _orderMeasurementService = orderMeasurementService;
        }
        #endregion Ctor
        public override async Task<OrderSearchModelExtended> PrepareOrderSearchModelAsync(OrderSearchModelExtended searchModel)
        {
            var baseModel = await base.PrepareOrderSearchModelAsync(searchModel);
            return baseModel;
        }

        public override async Task<OrderListModel> PrepareOrderListModelAsync(OrderSearchModel searchModel)
            => await base.PrepareOrderListModelAsync(searchModel);

        public async Task<OrderListModelExtended> PrepareOrderListModelExtendedAsync(OrderSearchModelExtended searchModel)
        {
            Dictionary<int, OrderDto> orderDtosById = null;

            var orderListModelExtended = await base.PrepareOrderListModelExtendedAsync(searchModel, async (orderListModel, orderModelExtended) =>
            {
                orderDtosById ??= await _ctx.OrderDtos.GetAllByIds(orderListModel.Data.Select(x => x.Id)).ToDictionaryAsync(k => k.Id, v => v);
                var orderDto = orderDtosById[orderModelExtended.Id];

                PrepareOrderModelExtended(orderModelExtended, orderDto);
            });
            
            return orderListModelExtended;
        }

        private static void PrepareOrderModelExtended(OrderModelExtended orderModelExtended, OrderDto orderDto)
        {
            orderModelExtended.IsMeasured = orderDto.IsMeasured;
            orderModelExtended.MeasuringStatus = orderDto.MeasuringStatus;
            orderModelExtended.IsMeasurable = orderDto.IsMeasurable;
            orderModelExtended.DateOfReceipt = orderDto.DateOfReceipt;
            orderModelExtended.OrderTotal = !orderDto.IsComplete && orderDto.IsMeasurable ? "kalkuláció alatt..." : orderModelExtended.OrderTotal;
            orderModelExtended.IsAllOrderItemAvgWeightValid = orderDto.IsAllOrderItemAvgWeightValid;


            //var fullName = $"{orderDto.Customer.FirstName}_{orderDto.Customer.LastName}".Trim();
            orderModelExtended.CustomerCompany = $"{orderDto.Customer.Company} {orderDto.Customer.FirstName}_{orderDto.Customer.LastName}";
            var genericAttributes = orderDto.GenericAttributes;
            if(genericAttributes != null)
            {
                var innvoiceTechIdAttribute = genericAttributes.FirstOrDefault(ga => ga.Key == "InnVoiceOrderTechId");
                if (innvoiceTechIdAttribute != null)
                {
                    orderModelExtended.InnvoiceTechId = innvoiceTechIdAttribute.Value;
                }
            }
        }

        public override async Task<OrderModel> PrepareOrderModelAsync(OrderModel model, Order order, bool excludeProperties = false)
        {
            return await base.PrepareOrderModelAsync(model, order, excludeProperties);
        }

        public async Task<OrderModelExtended> PrepareOrderModelExtendedAsync(OrderModel model, Order order, bool excludeProperties = false)
        {
            var orderModel = await base.PrepareOrderModelAsync(model, order, excludeProperties);
            var orderModelExtended = new OrderModelExtended();

            PropertyHelper.CopyPublicValueTypeProperties(orderModel, orderModelExtended);

            var orderDto = await _ctx.OrderDtos.GetByIdAsync(order.Id, true);
            PrepareOrderModelExtended(orderModelExtended, orderDto);

            var orderItemDtosById = await _ctx.OrderItemDtos.GetAllByIds(orderModelExtended.Items.Select(oi => oi.Id)).ToDictionaryAsync(k => k.Id, v => v);
            foreach (var orderItemModel in orderModelExtended.Items)
            {
                var orderItemModelExtended = new OrderItemModelExtended();
                PropertyHelper.CopyPublicValueTypeProperties(orderItemModel, orderItemModelExtended);

                var orderItemDto = orderItemDtosById[orderItemModelExtended.Id];

                orderItemModelExtended.IsMeasured = orderItemDto.IsMeasured;
                orderItemModelExtended.IsMeasurable = orderItemDto.IsMeasurable;
                orderItemModelExtended.MeasuringStatus = orderItemDto.MeasuringStatus;
                orderItemModelExtended.NetWeight = (decimal)orderItemDto.NetWeight;
                orderItemModelExtended.ProductStockQuantity = orderItemDto.ProductDto!.StockQuantity;
                orderItemModelExtended.ProductIncomingQuantity = orderItemDto.ProductDto.IncomingQuantity;
                orderItemModelExtended.ProductAvailableQuantity = orderItemDto.ProductDto.AvailableQuantity;
                orderItemModelExtended.ProductAverageWeight = orderItemDto.ProductDto.AverageWeight;
                orderItemModelExtended.AverageWeight = orderItemDto.AverageWeight;
                orderItemModelExtended.AverageWeightIsValid = orderItemDto.AverageWeightIsValid;
                orderItemModelExtended.AverageWeightDifference = orderItemDto.AverageWeightDifference;

                orderItemModelExtended.SubTotalInclTax = orderItemDto.IsMeasurable && !orderItemDto.IsAudited ? "kalkuláció alatt..." : orderItemModelExtended.SubTotalInclTax;

                orderModelExtended.ItemExtendeds ??= new List<OrderItemModelExtended>();
                orderModelExtended.ItemExtendeds.Add(orderItemModelExtended);

                //var a = orderItemModelExtended;
                //var gf= $"{a.ProductStockQuantity} {(a.ProductIncomingQuantity > 0 ? a.ProductIncomingQuantity.ToString() : string.Empty)}";
            }

            //orderModelExtended.Items.Clear();
            return orderModelExtended;
        }

        // example async custom logic
        private async Task<bool> ShouldMarkAsNeedsMeasurementAsync(OrderModel order)
        {
            // TODO: your logic (e.g. check if order has products that need measuring)
            if (order == null)
                return false;

            var fullOrder = await _orderService.GetOrderByIdAsync(order.Id);
            if (fullOrder != null)
            {
                return await _orderMeasurementService.IsPendingMeasurementAsync(fullOrder);
            }
            return await Task.FromResult(false);
        }

        private async Task<DateTime?> GetPickupDateTimeAsync(OrderModel order)
        {           
            DateTime? dateTime = DateTime.MinValue;
            var fullOrder = await _orderService.GetOrderByIdAsync(order.Id);
            if (fullOrder != null)
            {
                dateTime = await GenericAttributeService.GetAttributeAsync<DateTime>(fullOrder, nameof(IOrderDto.DateOfReceipt));
                if(dateTime == DateTime.MinValue || !dateTime.HasValue)
                {
                    dateTime = null;
                }
            }
            return dateTime;
        }
    }
}

