using AyCode.Core.Extensions;
using AyCode.Core.Helpers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Newtonsoft.Json;
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.Areas.Admin.Models.Order;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
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.Common;
using Nop.Web.Areas.Admin.Models.Orders;
using Nop.Web.Framework.Extensions;
using Nop.Web.Framework.Models.Extensions;

namespace Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase;

public class MgOrderModelFactory<TOrderListModelExt, TOrderModelExt> : OrderModelFactory 
    where TOrderListModelExt:OrderListModelExtended where TOrderModelExt: OrderModelExtended
{
    protected IGenericAttributeService GenericAttributeService;

    #region Ctor
    public MgOrderModelFactory(
        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 = genericAttributeService;
    }
    #endregion Cotr

    public virtual async Task<OrderSearchModelExtended> PrepareOrderSearchModelAsync(OrderSearchModelExtended searchModel)
    {
        ArgumentNullException.ThrowIfNull(searchModel);

        searchModel.IsLoggedInAsVendor = await _workContext.GetCurrentVendorAsync() != null;
        searchModel.BillingPhoneEnabled = _addressSettings.PhoneEnabled;

        var licenseCheckModel = new LicenseCheckModel();
        try
        {
            var result = await _nopHttpClient.GetLicenseCheckDetailsAsync();
            if (!string.IsNullOrEmpty(result))
            {
                licenseCheckModel = JsonConvert.DeserializeObject<LicenseCheckModel>(result);
                if (licenseCheckModel.DisplayWarning == false && licenseCheckModel.BlockPages == false)
                    await _settingService.SetSettingAsync($"{nameof(AdminAreaSettings)}.{nameof(AdminAreaSettings.CheckLicense)}", false);
            }
        }
        catch { }
        searchModel.LicenseCheckModel = licenseCheckModel;

        //prepare available order, payment and shipping statuses
        await _baseAdminModelFactory.PrepareOrderStatusesAsync(searchModel.AvailableOrderStatuses);
        if (searchModel.AvailableOrderStatuses.Any())
        {
            if (searchModel.OrderStatusIds?.Any() ?? false)
            {
                var ids = searchModel.OrderStatusIds.Select(id => id.ToString());
                var statusItems = searchModel.AvailableOrderStatuses.Where(statusItem => ids.Contains(statusItem.Value)).ToList();
                foreach (var statusItem in statusItems)
                {
                    statusItem.Selected = true;
                }
            }
            else
                searchModel.AvailableOrderStatuses.FirstOrDefault().Selected = true;
        }

        await _baseAdminModelFactory.PreparePaymentStatusesAsync(searchModel.AvailablePaymentStatuses);
        if (searchModel.AvailablePaymentStatuses.Any())
        {
            if (searchModel.PaymentStatusIds?.Any() ?? false)
            {
                var ids = searchModel.PaymentStatusIds.Select(id => id.ToString());
                var statusItems = searchModel.AvailablePaymentStatuses.Where(statusItem => ids.Contains(statusItem.Value)).ToList();
                foreach (var statusItem in statusItems)
                {
                    statusItem.Selected = true;
                }
            }
            else
                searchModel.AvailablePaymentStatuses.FirstOrDefault().Selected = true;
        }

        await _baseAdminModelFactory.PrepareShippingStatusesAsync(searchModel.AvailableShippingStatuses);
        if (searchModel.AvailableShippingStatuses.Any())
        {
            if (searchModel.ShippingStatusIds?.Any() ?? false)
            {
                var ids = searchModel.ShippingStatusIds.Select(id => id.ToString());
                var statusItems = searchModel.AvailableShippingStatuses.Where(statusItem => ids.Contains(statusItem.Value)).ToList();
                foreach (var statusItem in statusItems)
                {
                    statusItem.Selected = true;
                }
            }
            else
                searchModel.AvailableShippingStatuses.FirstOrDefault().Selected = true;
        }

        //prepare available stores
        await _baseAdminModelFactory.PrepareStoresAsync(searchModel.AvailableStores);

        //prepare available vendors
        await _baseAdminModelFactory.PrepareVendorsAsync(searchModel.AvailableVendors);

        //prepare available warehouses
        await _baseAdminModelFactory.PrepareWarehousesAsync(searchModel.AvailableWarehouses);
        

        //prepare available payment methods
        searchModel.AvailablePaymentMethods = (await _paymentPluginManager.LoadAllPluginsAsync()).Select(method =>
            new SelectListItem { Text = method.PluginDescriptor.FriendlyName, Value = method.PluginDescriptor.SystemName }).ToList();
        searchModel.AvailablePaymentMethods.Insert(0, new SelectListItem { Text = await _localizationService.GetResourceAsync("Admin.Common.All"), Value = string.Empty });

        //prepare available billing countries
        searchModel.AvailableCountries = (await _countryService.GetAllCountriesForBillingAsync(showHidden: true))
            .Select(country => new SelectListItem { Text = country.Name, Value = country.Id.ToString() }).ToList();
        searchModel.AvailableCountries.Insert(0, new SelectListItem { Text = await _localizationService.GetResourceAsync("Admin.Common.All"), Value = "0" });

        //searchModel.AvailableCompanies = (await _customerService.GetAllCustomersAsync())
        //    .Select(customer => new SelectListItem { Text = customer.Company, Value = customer.Id.ToString() }).ToList();
        //searchModel.AvailableCompanies.Insert(0, new SelectListItem { Text = await _localizationService.GetResourceAsync("Admin.Common.All"), Value = "0" });

        //prepare grid
        searchModel.SetGridPageSize();

        searchModel.HideStoresList = _catalogSettings.IgnoreStoreLimitations || searchModel.AvailableStores.SelectionIsNotPossible();

        return searchModel;
    }

    public override async Task<OrderListModel> PrepareOrderListModelAsync(OrderSearchModel searchModel)
    { 
       var preFiltered = await base.PrepareOrderListModelAsync(searchModel);
        return preFiltered;
    }

    public async Task<OrderListModel> PrepareOrderListModelAsync(OrderSearchModel searchModel, int customerId)
    {
        ArgumentNullException.ThrowIfNull(searchModel);

        //get parameters to filter orders
        var orderStatusIds = (searchModel.OrderStatusIds?.Contains(0) ?? true) ? null : searchModel.OrderStatusIds.ToList();
        var paymentStatusIds = (searchModel.PaymentStatusIds?.Contains(0) ?? true) ? null : searchModel.PaymentStatusIds.ToList();
        var shippingStatusIds = (searchModel.ShippingStatusIds?.Contains(0) ?? true) ? null : searchModel.ShippingStatusIds.ToList();
        var currentVendor = await _workContext.GetCurrentVendorAsync();
        if (currentVendor != null)
            searchModel.VendorId = currentVendor.Id;
        var startDateValue = !searchModel.StartDate.HasValue ? null
            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(searchModel.StartDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync());
        var endDateValue = !searchModel.EndDate.HasValue ? null
            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(searchModel.EndDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync()).AddDays(1);
        var product = await _productService.GetProductByIdAsync(searchModel.ProductId);
        var filterByProductId = product != null && (currentVendor == null || product.VendorId == currentVendor.Id)
            ? searchModel.ProductId : 0;

        //get orders
        var orders = await _orderService.SearchOrdersAsync(storeId: searchModel.StoreId,
            vendorId: searchModel.VendorId,
            customerId: customerId,
            productId: filterByProductId,
            warehouseId: searchModel.WarehouseId,
            paymentMethodSystemName: searchModel.PaymentMethodSystemName,
            createdFromUtc: startDateValue,
            createdToUtc: endDateValue,
            osIds: orderStatusIds,
            psIds: paymentStatusIds,
            ssIds: shippingStatusIds,
            billingPhone: searchModel.BillingPhone,
            billingEmail: searchModel.BillingEmail,
            billingLastName: searchModel.BillingLastName,
            billingCountryId: searchModel.BillingCountryId,
            orderNotes: searchModel.OrderNotes,
            pageIndex: searchModel.Page - 1, pageSize: searchModel.PageSize);

        //prepare list model
        var model = await new OrderListModel().PrepareToGridAsync(searchModel, orders, () =>
        {
            //fill in model values from the entity
            return orders.SelectAwait(async order =>
            {
                var billingAddress = await _addressService.GetAddressByIdAsync(order.BillingAddressId);

                //fill in model values from the entity
                var orderModel = new OrderModel
                {
                    Id = order.Id,
                    OrderStatusId = order.OrderStatusId,
                    PaymentStatusId = order.PaymentStatusId,
                    ShippingStatusId = order.ShippingStatusId,
                    CustomerEmail = billingAddress.Email,
                    CustomerFullName = $"{billingAddress.FirstName} {billingAddress.LastName}",
                    CustomerId = order.CustomerId,
                    CustomOrderNumber = order.CustomOrderNumber
                };

                //convert dates to the user time
                orderModel.CreatedOn = await _dateTimeHelper.ConvertToUserTimeAsync(order.CreatedOnUtc, DateTimeKind.Utc);

                //fill in additional values (not existing in the entity)
                orderModel.StoreName = (await _storeService.GetStoreByIdAsync(order.StoreId))?.Name ?? "Deleted";
                orderModel.OrderStatus = await _localizationService.GetLocalizedEnumAsync(order.OrderStatus);
                orderModel.PaymentStatus = await _localizationService.GetLocalizedEnumAsync(order.PaymentStatus);
                orderModel.ShippingStatus = await _localizationService.GetLocalizedEnumAsync(order.ShippingStatus);
                orderModel.OrderTotal = await _priceFormatter.FormatPriceAsync(order.OrderTotal, true, false);

                return orderModel;
            });
        });

        return model;
    }
    public virtual async Task<TOrderListModelExt> PrepareOrderListModelExtendedAsync(OrderSearchModelExtended searchModel, Func<OrderListModel, TOrderModelExt, Task> dataItemCopiedCallback)
    {
        var customerCompany = searchModel.BillingCompany;
        var customer = int.TryParse(customerCompany, out var customerId) && customerId > 0
            ? await _customerService.GetCustomerByIdAsync(customerId)
            : null;
        //var customer = customers.FirstOrDefault(c => c.Company != null && c.Company.Equals(customerCompany, StringComparison.InvariantCultureIgnoreCase));
        //var customer = customers.FirstOrDefault(c => c.Company != null && c.Company.Equals(customerCompany, StringComparison.InvariantCultureIgnoreCase));
        OrderListModel prefiltered;
        if (customer != null)
        {

            prefiltered = await PrepareOrderListModelAsync(searchModel, customer.Id);
        }
        else
        {
            prefiltered = await PrepareOrderListModelAsync(searchModel);

        }

        var extendedRows = new List<TOrderModelExt>(prefiltered.RecordsFiltered);

        foreach (var orderModel in prefiltered.Data.ToList())
        {
            var orderModelExtended = Activator.CreateInstance<TOrderModelExt>();

            PropertyHelper.CopyPublicValueTypeProperties(orderModel, orderModelExtended);
            extendedRows.Add(orderModelExtended);

            if (dataItemCopiedCallback == null) continue;
            await dataItemCopiedCallback.Invoke(prefiltered, orderModelExtended);
        }

        prefiltered.Data = null;
        var totalRecords = prefiltered.RecordsTotal;
        var filteredRecords = prefiltered.RecordsFiltered;

        Console.WriteLine($"Total Records before filtering: {totalRecords}");
        Console.WriteLine($"Filtered Records before filtering: {filteredRecords}");

        //var orderListModelExtended = orderListModel.ToJson().JsonTo<TOrderListModelExt>();
        //if (searchModel.BillingCompany != null)
        //{
        //    //extendedRows = extendedRows.Where(x => x.CustomerCompany != null && x.CustomerCompany.IndexOf(searchModel.BillingCompany, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList();
        //    extendedRows = extendedRows.Where(x => x.CustomerId == Convert.ToInt32(searchModel.BillingCompany)).ToList();
        //}


        var orderListModelExtended = prefiltered.CloneTo<TOrderListModelExt>();
        orderListModelExtended.Data = extendedRows;
        orderListModelExtended.RecordsTotal = extendedRows.Count;
        //orderListModelExtended.RecordsFiltered = extendedRows.Count;


        //orderListModelExtended.RecordsTotal = totalRecords;
        orderListModelExtended.RecordsFiltered = filteredRecords;
        return orderListModelExtended;
    }
}