using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Nop.Core.Domain.Directory;
using Nop.Plugin.Shipping.FixedByWeightByTotal.Domain;
using Nop.Plugin.Shipping.FixedByWeightByTotal.Models;
using Nop.Plugin.Shipping.FixedByWeightByTotal.Services;
using Nop.Services.Configuration;
using Nop.Services.Directory;
using Nop.Services.Localization;
using Nop.Services.Security;
using Nop.Services.Shipping;
using Nop.Services.Stores;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Models.Extensions;
using Nop.Web.Framework.Mvc;
using Nop.Web.Framework.Mvc.Filters;

namespace Nop.Plugin.Shipping.FixedByWeightByTotal.Controllers;

[AuthorizeAdmin]
[Area(AreaNames.ADMIN)]
[AutoValidateAntiforgeryToken]
public class FixedByWeightByTotalController : BasePluginController
{
    #region Fields

    protected readonly CurrencySettings _currencySettings;
    protected readonly FixedByWeightByTotalSettings _fixedByWeightByTotalSettings;
    protected readonly ICountryService _countryService;
    protected readonly ICurrencyService _currencyService;
    protected readonly ILocalizationService _localizationService;
    protected readonly IMeasureService _measureService;
    protected readonly ISettingService _settingService;
    protected readonly IShippingByWeightByTotalService _shippingByWeightService;
    protected readonly IShippingMethodsService _shippingMethodsService;
    protected readonly IStateProvinceService _stateProvinceService;
    protected readonly IStoreService _storeService;
    protected readonly IWarehouseService _warehouseService;
    protected readonly MeasureSettings _measureSettings;

    #endregion

    #region Ctor

    public FixedByWeightByTotalController(CurrencySettings currencySettings,
        FixedByWeightByTotalSettings fixedByWeightByTotalSettings,
        ICountryService countryService,
        ICurrencyService currencyService,
        ILocalizationService localizationService,
        IMeasureService measureService,
        ISettingService settingService,
        IShippingByWeightByTotalService shippingByWeightService,
        IShippingMethodsService shippingMethodsService,
        IStateProvinceService stateProvinceService,
        IStoreService storeService,
        IWarehouseService warehouseService,
        MeasureSettings measureSettings)
    {
        _currencySettings = currencySettings;
        _fixedByWeightByTotalSettings = fixedByWeightByTotalSettings;
        _countryService = countryService;
        _currencyService = currencyService;
        _localizationService = localizationService;
        _measureService = measureService;
        _settingService = settingService;
        _shippingByWeightService = shippingByWeightService;
        _stateProvinceService = stateProvinceService;
        _shippingMethodsService = shippingMethodsService;
        _storeService = storeService;
        _warehouseService = warehouseService;
        _measureSettings = measureSettings;
    }

    #endregion

    #region Methods

    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> Configure()
    {
        var model = new ConfigurationModel
        {
            LimitMethodsToCreated = _fixedByWeightByTotalSettings.LimitMethodsToCreated,
            ShippingByWeightByTotalEnabled = _fixedByWeightByTotalSettings.ShippingByWeightByTotalEnabled
        };

        //stores
        model.AvailableStores.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var store in await _storeService.GetAllStoresAsync())
            model.AvailableStores.Add(new SelectListItem { Text = store.Name, Value = store.Id.ToString() });
        //warehouses
        model.AvailableWarehouses.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var warehouses in await _warehouseService.GetAllWarehousesAsync())
            model.AvailableWarehouses.Add(new SelectListItem { Text = warehouses.Name, Value = warehouses.Id.ToString() });
        //shipping methods
        model.AvailableShippingMethods.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var sm in await _shippingMethodsService.GetAllShippingMethodsAsync())
            model.AvailableShippingMethods.Add(new SelectListItem { Text = sm.Name, Value = sm.Id.ToString() });
        //countries
        model.AvailableCountries.Add(new SelectListItem { Text = "*", Value = "0" });
        var countries = await _countryService.GetAllCountriesAsync();
        foreach (var c in countries)
            model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString() });
        //states
        model.AvailableStates.Add(new SelectListItem { Text = "*", Value = "0" });

        model.SetGridPageSize();

        return View("~/Plugins/Shipping.FixedByWeightByTotal/Views/Configure.cshtml", model);
    }

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> Configure(ConfigurationModel model)
    {
        //save settings
        _fixedByWeightByTotalSettings.LimitMethodsToCreated = model.LimitMethodsToCreated;
        await _settingService.SaveSettingAsync(_fixedByWeightByTotalSettings);

        return Json(new { Result = true });
    }

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> SaveMode(bool value)
    {
        //save settings
        _fixedByWeightByTotalSettings.ShippingByWeightByTotalEnabled = value;
        await _settingService.SaveSettingAsync(_fixedByWeightByTotalSettings);

        return Json(new { Result = true });
    }

    #region Fixed rate

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> FixedShippingRateList(ConfigurationModel searchModel)
    {
        var shippingMethods = (await _shippingMethodsService.GetAllShippingMethodsAsync()).ToPagedList(searchModel);

        var gridModel = await new FixedRateListModel().PrepareToGridAsync(searchModel, shippingMethods, () =>
        {
            return shippingMethods.SelectAwait(async shippingMethod => new FixedRateModel
            {
                ShippingMethodId = shippingMethod.Id,
                ShippingMethodName = shippingMethod.Name,

                Rate = await _settingService
                    .GetSettingByKeyAsync<decimal>(string.Format(FixedByWeightByTotalDefaults.FIXED_RATE_SETTINGS_KEY, shippingMethod.Id)),
                TransitDays = await _settingService
                    .GetSettingByKeyAsync<int?>(string.Format(FixedByWeightByTotalDefaults.TRANSIT_DAYS_SETTINGS_KEY, shippingMethod.Id))
            });
        });

        return Json(gridModel);
    }

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> UpdateFixedShippingRate(FixedRateModel model)
    {
        await _settingService.SetSettingAsync(string.Format(FixedByWeightByTotalDefaults.FIXED_RATE_SETTINGS_KEY, model.ShippingMethodId), model.Rate, 0, false);
        await _settingService.SetSettingAsync(string.Format(FixedByWeightByTotalDefaults.TRANSIT_DAYS_SETTINGS_KEY, model.ShippingMethodId), model.TransitDays, 0, false);

        await _settingService.ClearCacheAsync();

        return new NullJsonResult();
    }

    #endregion

    #region Rate by weight

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> RateByWeightByTotalList(ConfigurationModel searchModel, ConfigurationModel filter)
    {
        //var records = _shippingByWeightService.GetAll(command.Page - 1, command.PageSize);
        var records = await _shippingByWeightService.FindRecordsAsync(
            pageIndex: searchModel.Page - 1,
            pageSize: searchModel.PageSize,
            storeId: filter.SearchStoreId,
            warehouseId: filter.SearchWarehouseId,
            countryId: filter.SearchCountryId,
            stateProvinceId: filter.SearchStateProvinceId,
            zip: filter.SearchZip,
            shippingMethodId: filter.SearchShippingMethodId,
            weight: null,
            orderSubtotal: null
        );

        var gridModel = await new ShippingByWeightByTotalListModel().PrepareToGridAsync(searchModel, records, () =>
        {
            return records.SelectAwait(async record =>
            {
                var model = new ShippingByWeightByTotalModel
                {
                    Id = record.Id,
                    StoreId = record.StoreId,
                    StoreName = (await _storeService.GetStoreByIdAsync(record.StoreId))?.Name ?? "*",
                    WarehouseId = record.WarehouseId,
                    WarehouseName = (await _warehouseService.GetWarehouseByIdAsync(record.WarehouseId))?.Name ?? "*",
                    ShippingMethodId = record.ShippingMethodId,
                    ShippingMethodName = (await _shippingMethodsService.GetShippingMethodByIdAsync(record.ShippingMethodId))?.Name ?? "Unavailable",
                    CountryId = record.CountryId,
                    CountryName = (await _countryService.GetCountryByIdAsync(record.CountryId))?.Name ?? "*",
                    StateProvinceId = record.StateProvinceId,
                    StateProvinceName = (await _stateProvinceService.GetStateProvinceByIdAsync(record.StateProvinceId))?.Name ?? "*",
                    WeightFrom = record.WeightFrom,
                    WeightTo = record.WeightTo,
                    OrderSubtotalFrom = record.OrderSubtotalFrom,
                    OrderSubtotalTo = record.OrderSubtotalTo,
                    AdditionalFixedCost = record.AdditionalFixedCost,
                    PercentageRateOfSubtotal = record.PercentageRateOfSubtotal,
                    RatePerWeightUnit = record.RatePerWeightUnit,
                    LowerWeightLimit = record.LowerWeightLimit,
                    Zip = !string.IsNullOrEmpty(record.Zip) ? record.Zip : "*"
                };

                var htmlSb = new StringBuilder("<div>");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.WeightFrom"),
                    model.WeightFrom);
                htmlSb.Append("<br />");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.WeightTo"),
                    model.WeightTo);
                htmlSb.Append("<br />");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.OrderSubtotalFrom"),
                    model.OrderSubtotalFrom);
                htmlSb.Append("<br />");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.OrderSubtotalTo"),
                    model.OrderSubtotalTo);
                htmlSb.Append("<br />");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.AdditionalFixedCost"),
                    model.AdditionalFixedCost);
                htmlSb.Append("<br />");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.RatePerWeightUnit"),
                    model.RatePerWeightUnit);
                htmlSb.Append("<br />");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.LowerWeightLimit"),
                    model.LowerWeightLimit);
                htmlSb.Append("<br />");
                htmlSb.AppendFormat("{0}: {1}",
                    await _localizationService.GetResourceAsync("Plugins.Shipping.FixedByWeightByTotal.Fields.PercentageRateOfSubtotal"),
                    model.PercentageRateOfSubtotal);

                htmlSb.Append("</div>");
                model.DataHtml = htmlSb.ToString();

                return model;
            });
        });

        return Json(gridModel);
    }

    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> AddRateByWeightByTotalPopup()
    {
        var model = new ShippingByWeightByTotalModel
        {
            PrimaryStoreCurrencyCode = (await _currencyService.GetCurrencyByIdAsync(_currencySettings.PrimaryStoreCurrencyId))?.CurrencyCode,
            BaseWeightIn = (await _measureService.GetMeasureWeightByIdAsync(_measureSettings.BaseWeightId))?.Name,
            WeightTo = 1000000,
            OrderSubtotalTo = 1000000
        };

        var shippingMethods = await _shippingMethodsService.GetAllShippingMethodsAsync();
        if (!shippingMethods.Any())
            return Content("No shipping methods can be loaded");

        //stores
        model.AvailableStores.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var store in await _storeService.GetAllStoresAsync())
            model.AvailableStores.Add(new SelectListItem { Text = store.Name, Value = store.Id.ToString() });
        //warehouses
        model.AvailableWarehouses.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var warehouses in await _warehouseService.GetAllWarehousesAsync())
            model.AvailableWarehouses.Add(new SelectListItem { Text = warehouses.Name, Value = warehouses.Id.ToString() });
        //shipping methods
        foreach (var sm in shippingMethods)
            model.AvailableShippingMethods.Add(new SelectListItem { Text = sm.Name, Value = sm.Id.ToString() });
        //countries
        model.AvailableCountries.Add(new SelectListItem { Text = "*", Value = "0" });
        var countries = await _countryService.GetAllCountriesAsync(showHidden: true);
        foreach (var c in countries)
            model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString() });
        //states
        model.AvailableStates.Add(new SelectListItem { Text = "*", Value = "0" });

        return View("~/Plugins/Shipping.FixedByWeightByTotal/Views/AddRateByWeightByTotalPopup.cshtml", model);
    }

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> AddRateByWeightByTotalPopup(ShippingByWeightByTotalModel model)
    {
        await _shippingByWeightService.InsertShippingByWeightRecordAsync(new ShippingByWeightByTotalRecord
        {
            StoreId = model.StoreId,
            WarehouseId = model.WarehouseId,
            CountryId = model.CountryId,
            StateProvinceId = model.StateProvinceId,
            Zip = model.Zip == "*" ? null : model.Zip,
            ShippingMethodId = model.ShippingMethodId,
            WeightFrom = model.WeightFrom,
            WeightTo = model.WeightTo,
            OrderSubtotalFrom = model.OrderSubtotalFrom,
            OrderSubtotalTo = model.OrderSubtotalTo,
            AdditionalFixedCost = model.AdditionalFixedCost,
            RatePerWeightUnit = model.RatePerWeightUnit,
            PercentageRateOfSubtotal = model.PercentageRateOfSubtotal,
            LowerWeightLimit = model.LowerWeightLimit,
            TransitDays = model.TransitDays
        });

        ViewBag.RefreshPage = true;

        return View("~/Plugins/Shipping.FixedByWeightByTotal/Views/AddRateByWeightByTotalPopup.cshtml", model);
    }

    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> EditRateByWeightByTotalPopup(int id)
    {
        var sbw = await _shippingByWeightService.GetByIdAsync(id);
        if (sbw == null)
            //no record found with the specified id
            return RedirectToAction("Configure");

        var model = new ShippingByWeightByTotalModel
        {
            Id = sbw.Id,
            StoreId = sbw.StoreId,
            WarehouseId = sbw.WarehouseId,
            CountryId = sbw.CountryId,
            StateProvinceId = sbw.StateProvinceId,
            Zip = sbw.Zip,
            ShippingMethodId = sbw.ShippingMethodId,
            WeightFrom = sbw.WeightFrom,
            WeightTo = sbw.WeightTo,
            OrderSubtotalFrom = sbw.OrderSubtotalFrom,
            OrderSubtotalTo = sbw.OrderSubtotalTo,
            AdditionalFixedCost = sbw.AdditionalFixedCost,
            PercentageRateOfSubtotal = sbw.PercentageRateOfSubtotal,
            RatePerWeightUnit = sbw.RatePerWeightUnit,
            LowerWeightLimit = sbw.LowerWeightLimit,
            PrimaryStoreCurrencyCode = (await _currencyService.GetCurrencyByIdAsync(_currencySettings.PrimaryStoreCurrencyId))?.CurrencyCode,
            BaseWeightIn = (await _measureService.GetMeasureWeightByIdAsync(_measureSettings.BaseWeightId))?.Name,
            TransitDays = sbw.TransitDays
        };

        var shippingMethods = await _shippingMethodsService.GetAllShippingMethodsAsync();
        if (!shippingMethods.Any())
            return Content("No shipping methods can be loaded");

        var selectedStore = await _storeService.GetStoreByIdAsync(sbw.StoreId);
        var selectedWarehouse = await _warehouseService.GetWarehouseByIdAsync(sbw.WarehouseId);
        var selectedShippingMethod = await _shippingMethodsService.GetShippingMethodByIdAsync(sbw.ShippingMethodId);
        var selectedCountry = await _countryService.GetCountryByIdAsync(sbw.CountryId);
        var selectedState = await _stateProvinceService.GetStateProvinceByIdAsync(sbw.StateProvinceId);
        //stores
        model.AvailableStores.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var store in await _storeService.GetAllStoresAsync())
            model.AvailableStores.Add(new SelectListItem { Text = store.Name, Value = store.Id.ToString(), Selected = (selectedStore != null && store.Id == selectedStore.Id) });
        //warehouses
        model.AvailableWarehouses.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var warehouse in await _warehouseService.GetAllWarehousesAsync())
            model.AvailableWarehouses.Add(new SelectListItem { Text = warehouse.Name, Value = warehouse.Id.ToString(), Selected = (selectedWarehouse != null && warehouse.Id == selectedWarehouse.Id) });
        //shipping methods
        foreach (var sm in shippingMethods)
            model.AvailableShippingMethods.Add(new SelectListItem { Text = sm.Name, Value = sm.Id.ToString(), Selected = (selectedShippingMethod != null && sm.Id == selectedShippingMethod.Id) });
        //countries
        model.AvailableCountries.Add(new SelectListItem { Text = "*", Value = "0" });
        var countries = await _countryService.GetAllCountriesAsync(showHidden: true);
        foreach (var c in countries)
            model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString(), Selected = (selectedCountry != null && c.Id == selectedCountry.Id) });
        //states
        var states = selectedCountry != null ? (await _stateProvinceService.GetStateProvincesByCountryIdAsync(selectedCountry.Id, showHidden: true)).ToList() : new List<StateProvince>();
        model.AvailableStates.Add(new SelectListItem { Text = "*", Value = "0" });
        foreach (var s in states)
            model.AvailableStates.Add(new SelectListItem { Text = s.Name, Value = s.Id.ToString(), Selected = (selectedState != null && s.Id == selectedState.Id) });

        return View("~/Plugins/Shipping.FixedByWeightByTotal/Views/EditRateByWeightByTotalPopup.cshtml", model);
    }

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> EditRateByWeightByTotalPopup(ShippingByWeightByTotalModel model)
    {
        var sbw = await _shippingByWeightService.GetByIdAsync(model.Id);
        if (sbw == null)
            //no record found with the specified id
            return RedirectToAction("Configure");

        sbw.StoreId = model.StoreId;
        sbw.WarehouseId = model.WarehouseId;
        sbw.CountryId = model.CountryId;
        sbw.StateProvinceId = model.StateProvinceId;
        sbw.Zip = model.Zip == "*" ? null : model.Zip;
        sbw.ShippingMethodId = model.ShippingMethodId;
        sbw.WeightFrom = model.WeightFrom;
        sbw.WeightTo = model.WeightTo;
        sbw.OrderSubtotalFrom = model.OrderSubtotalFrom;
        sbw.OrderSubtotalTo = model.OrderSubtotalTo;
        sbw.AdditionalFixedCost = model.AdditionalFixedCost;
        sbw.RatePerWeightUnit = model.RatePerWeightUnit;
        sbw.PercentageRateOfSubtotal = model.PercentageRateOfSubtotal;
        sbw.LowerWeightLimit = model.LowerWeightLimit;
        sbw.TransitDays = model.TransitDays;

        await _shippingByWeightService.UpdateShippingByWeightRecordAsync(sbw);

        ViewBag.RefreshPage = true;

        return View("~/Plugins/Shipping.FixedByWeightByTotal/Views/EditRateByWeightByTotalPopup.cshtml", model);
    }

    [HttpPost]
    [CheckPermission(StandardPermission.Configuration.MANAGE_SHIPPING_SETTINGS)]
    public async Task<IActionResult> DeleteRateByWeightByTotal(int id)
    {
        var sbw = await _shippingByWeightService.GetByIdAsync(id);
        if (sbw != null)
            await _shippingByWeightService.DeleteShippingByWeightRecordAsync(sbw);

        return new NullJsonResult();
    }

    #endregion

    #endregion
}