package com.worldline.direct.b2bcheckoutaddon.controllers.pages.steps;

import com.worldline.direct.b2bcheckoutaddon.constants.WorldlineCheckoutConstants;
import com.worldline.direct.b2bcheckoutaddon.controllers.WorldlineWebConstants;
import com.worldline.direct.b2bcheckoutaddon.forms.WorldlineAddressForm;
import com.worldline.direct.b2bcheckoutaddon.utils.WorldlineAddressDataUtil;
import de.hybris.platform.acceleratorstorefrontcommons.annotations.PreValidateCheckoutStep;
import de.hybris.platform.acceleratorstorefrontcommons.annotations.PreValidateQuoteCheckoutStep;
import de.hybris.platform.acceleratorstorefrontcommons.annotations.RequireHardLogIn;
import de.hybris.platform.acceleratorstorefrontcommons.checkout.steps.CheckoutStep;
import de.hybris.platform.acceleratorstorefrontcommons.checkout.steps.validation.ValidationResults;
import de.hybris.platform.acceleratorstorefrontcommons.constants.WebConstants;
import de.hybris.platform.acceleratorstorefrontcommons.controllers.pages.checkout.steps.AbstractCheckoutStepController;
import de.hybris.platform.acceleratorstorefrontcommons.controllers.util.GlobalMessages;
import de.hybris.platform.acceleratorstorefrontcommons.forms.AddressForm;
import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException;
import de.hybris.platform.cms2.model.pages.ContentPageModel;
import de.hybris.platform.commercefacades.address.data.AddressVerificationResult;
import de.hybris.platform.commercefacades.order.data.CartData;
import de.hybris.platform.commercefacades.user.data.AddressData;
import de.hybris.platform.commercefacades.user.data.CountryData;
import de.hybris.platform.commerceservices.address.AddressVerificationDecision;
import de.hybris.platform.util.Config;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.annotation.Resource;
import java.util.Set;

@Controller
@RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.root)
public class WorldlineDeliveryAddressCheckoutStepController extends AbstractCheckoutStepController {

   private static final String DELIVERY_ADDRESS = "delivery-address";
   private static final String SHOW_SAVE_TO_ADDRESS_BOOK_ATTR = "showSaveToAddressBook";

   @Resource(name = "worldlineDefaultAddressDataUtil")
   private WorldlineAddressDataUtil addressDataUtil;

   @Override
   @RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.add, method = RequestMethod.GET)
   @RequireHardLogIn
   @PreValidateQuoteCheckoutStep
   @PreValidateCheckoutStep(checkoutStep = DELIVERY_ADDRESS)
   public String enterStep(final Model model, final RedirectAttributes redirectAttributes) throws CMSItemNotFoundException
   {
      getCheckoutFacade().setDeliveryAddressIfAvailable();
      final CartData cartData = getCheckoutFacade().getCheckoutCart();

      populateCommonModelAttributes(model, cartData, new WorldlineAddressForm());

      return WorldlineCheckoutConstants.Views.Pages.MultiStepCheckout.worldlineDeliveryAddressPage;
   }

   @RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.add, method = RequestMethod.POST)
   @RequireHardLogIn
   public String add(final WorldlineAddressForm addressForm, final BindingResult bindingResult, final Model model,
         final RedirectAttributes redirectModel) throws CMSItemNotFoundException
   {
      final CartData cartData = getCheckoutFacade().getCheckoutCart();

      getAddressValidator().validate(addressForm, bindingResult);
      populateCommonModelAttributes(model, cartData, addressForm);

      if (bindingResult.hasErrors())
      {
         GlobalMessages.addErrorMessage(model, "address.error.formentry.invalid");
         return WorldlineCheckoutConstants.Views.Pages.MultiStepCheckout.worldlineDeliveryAddressPage;
      }

      final AddressData newAddress = addressDataUtil.convertToAddressData(addressForm);

      processAddressVisibilityAndDefault(addressForm, newAddress);

      // Verify the address data.
      final AddressVerificationResult<AddressVerificationDecision> verificationResult = getAddressVerificationFacade()
            .verifyAddressData(newAddress);
      final boolean addressRequiresReview = getAddressVerificationResultHandler().handleResult(verificationResult, newAddress,
            model, redirectModel, bindingResult, getAddressVerificationFacade().isCustomerAllowedToIgnoreAddressSuggestions(),
            "checkout.multi.address.updated");

      if (addressRequiresReview)
      {
         return WorldlineCheckoutConstants.Views.Pages.MultiStepCheckout.worldlineDeliveryAddressPage;
      }

      getUserFacade().addAddress(newAddress);

      final AddressData previousSelectedAddress = getCheckoutFacade().getCheckoutCart().getDeliveryAddress();
      // Set the new address as the selected checkout delivery address
      getCheckoutFacade().setDeliveryAddress(newAddress);
      if (previousSelectedAddress != null && !previousSelectedAddress.isVisibleInAddressBook())
      { // temporary address should be removed
         getUserFacade().removeAddress(previousSelectedAddress);
      }

      // Set the new address as the selected checkout delivery address
      getCheckoutFacade().setDeliveryAddress(newAddress);

      return getCheckoutStep().nextStep();
   }

   protected void processAddressVisibilityAndDefault(final AddressForm addressForm, final AddressData newAddress)
   {
      if (addressForm.getSaveInAddressBook() != null)
      {
         newAddress.setVisibleInAddressBook(addressForm.getSaveInAddressBook().booleanValue());
         if (addressForm.getSaveInAddressBook().booleanValue() && CollectionUtils.isEmpty(getUserFacade().getAddressBook()))
         {
            newAddress.setDefaultAddress(true);
         }
      }
      else if (getCheckoutCustomerStrategy().isAnonymousCheckout())
      {
         newAddress.setDefaultAddress(true);
         newAddress.setVisibleInAddressBook(true);
      }
   }

   @RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.edit, method = RequestMethod.GET)
   @RequireHardLogIn
   public String editAddressForm(@RequestParam("editAddressCode") final String editAddressCode, final Model model,
         final RedirectAttributes redirectAttributes) throws CMSItemNotFoundException
   {
      final ValidationResults validationResults = getCheckoutStep().validate(redirectAttributes);
      if (getCheckoutStep().checkIfValidationErrors(validationResults))
      {
         return getCheckoutStep().onValidation(validationResults);
      }

      AddressData addressData = null;
      if (StringUtils.isNotEmpty(editAddressCode))
      {
         addressData = getCheckoutFacade().getDeliveryAddressForCode(editAddressCode);
      }

      final WorldlineAddressForm addressForm = new WorldlineAddressForm();
      final boolean hasAddressData = addressData != null;
      if (hasAddressData)
      {
         addressDataUtil.convert(addressData, addressForm);
      }

      final CartData cartData = getCheckoutFacade().getCheckoutCart();
      populateCommonModelAttributes(model, cartData, addressForm);

      if (addressData != null)
      {
         model.addAttribute(SHOW_SAVE_TO_ADDRESS_BOOK_ATTR, Boolean.valueOf(!addressData.isVisibleInAddressBook()));
      }

      return WorldlineCheckoutConstants.Views.Pages.MultiStepCheckout.worldlineDeliveryAddressPage;
   }

   @RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.edit, method = RequestMethod.POST)
   @RequireHardLogIn
   public String edit(final WorldlineAddressForm addressForm, final BindingResult bindingResult, final Model model,
         final RedirectAttributes redirectModel) throws CMSItemNotFoundException
   {
      getAddressValidator().validate(addressForm, bindingResult);

      final CartData cartData = getCheckoutFacade().getCheckoutCart();
      populateCommonModelAttributes(model, cartData, addressForm);

      if (bindingResult.hasErrors())
      {
         GlobalMessages.addErrorMessage(model, "address.error.formentry.invalid");
         return WorldlineCheckoutConstants.Views.Pages.MultiStepCheckout.worldlineDeliveryAddressPage;
      }

      final AddressData newAddress = addressDataUtil.convertToAddressData(addressForm);

      processAddressVisibility(addressForm, newAddress);

      newAddress.setDefaultAddress(CollectionUtils.isEmpty(getUserFacade().getAddressBook())
            || getUserFacade().getAddressBook().size() == 1 || Boolean.TRUE.equals(addressForm.getDefaultAddress()));

      // Verify the address data.
      final AddressVerificationResult<AddressVerificationDecision> verificationResult = getAddressVerificationFacade()
            .verifyAddressData(newAddress);
      final boolean addressRequiresReview = getAddressVerificationResultHandler().handleResult(verificationResult, newAddress,
            model, redirectModel, bindingResult, getAddressVerificationFacade().isCustomerAllowedToIgnoreAddressSuggestions(),
            "checkout.multi.address.updated");

      if (addressRequiresReview)
      {
         if (StringUtils.isNotEmpty(addressForm.getAddressId()))
         {
            final AddressData addressData = getCheckoutFacade().getDeliveryAddressForCode(addressForm.getAddressId());
            if (addressData != null)
            {
               model.addAttribute(SHOW_SAVE_TO_ADDRESS_BOOK_ATTR, Boolean.valueOf(!addressData.isVisibleInAddressBook()));
               model.addAttribute("edit", Boolean.TRUE);
            }
         }

         return WorldlineCheckoutConstants.Views.Pages.MultiStepCheckout.worldlineDeliveryAddressPage;
      }

      getUserFacade().editAddress(newAddress);
      getCheckoutFacade().setDeliveryModeIfAvailable();
      getCheckoutFacade().setDeliveryAddress(newAddress);

      return getCheckoutStep().nextStep();
   }

   protected void processAddressVisibility(final WorldlineAddressForm addressForm, final AddressData newAddress)
   {

      if (addressForm.getSaveInAddressBook() == null)
      {
         newAddress.setVisibleInAddressBook(true);
      }
      else
      {
         newAddress.setVisibleInAddressBook(Boolean.TRUE.equals(addressForm.getSaveInAddressBook()));
      }
   }

   @RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.remove, method = RequestMethod.POST )
   @RequireHardLogIn
   public String removeAddress(@RequestParam("addressCode") final String addressCode, final RedirectAttributes redirectModel,
         final Model model) throws CMSItemNotFoundException
   {
      if (getCheckoutFacade().isRemoveAddressEnabledForCart())
      {
         final AddressData addressData = new AddressData();
         addressData.setId(addressCode);
         getUserFacade().removeAddress(addressData);
         GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.CONF_MESSAGES_HOLDER,
               "account.confirmation.address.removed");
      }
      final ContentPageModel multiCheckoutSummaryPage = getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL);
      storeCmsPageInModel(model, multiCheckoutSummaryPage);
      setUpMetaDataForContentPage(model, multiCheckoutSummaryPage);
      model.addAttribute("addressForm", new AddressForm());

      return getCheckoutStep().currentStep();
   }

   @RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.select, method = RequestMethod.POST)
   @RequireHardLogIn
   public String doSelectSuggestedAddress(final AddressForm addressForm, final RedirectAttributes redirectModel)
   {
      final Set<String> resolveCountryRegions = org.springframework.util.StringUtils
            .commaDelimitedListToSet(Config.getParameter("resolve.country.regions"));

      final AddressData selectedAddress = addressDataUtil.convertToAddressData(addressForm);
      final CountryData countryData = selectedAddress.getCountry();

      if (!resolveCountryRegions.contains(countryData.getIsocode()))
      {
         selectedAddress.setRegion(null);
      }

      if (addressForm.getSaveInAddressBook() != null)
      {
         selectedAddress.setVisibleInAddressBook(addressForm.getSaveInAddressBook().booleanValue());
      }

      if (Boolean.TRUE.equals(addressForm.getEditAddress()))
      {
         getUserFacade().editAddress(selectedAddress);
      }
      else
      {
         getUserFacade().addAddress(selectedAddress);
      }

      final AddressData previousSelectedAddress = getCheckoutFacade().getCheckoutCart().getDeliveryAddress();
      // Set the new address as the selected checkout delivery address
      getCheckoutFacade().setDeliveryAddress(selectedAddress);
      if (previousSelectedAddress != null && !previousSelectedAddress.isVisibleInAddressBook())
      { // temporary address should be removed
         getUserFacade().removeAddress(previousSelectedAddress);
      }

      GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.CONF_MESSAGES_HOLDER, "checkout.multi.address.added");

      return getCheckoutStep().nextStep();
   }


   /**
    * This method gets called when the "Use this Address" button is clicked. It sets the selected delivery address on
    * the checkout facade - if it has changed, and reloads the page highlighting the selected delivery address.
    *
    * @param selectedAddressCode
    *           - the id of the delivery address.
    *
    * @return - a URL to the page to load.
    */
   @RequestMapping(value = WorldlineWebConstants.URL.Checkout.DeliveryAddress.select, method = RequestMethod.GET)
   @RequireHardLogIn
   public String doSelectDeliveryAddress(@RequestParam("selectedAddressCode") final String selectedAddressCode,
         final RedirectAttributes redirectAttributes)
   {
      final ValidationResults validationResults = getCheckoutStep().validate(redirectAttributes);
      if (getCheckoutStep().checkIfValidationErrors(validationResults))
      {
         return getCheckoutStep().onValidation(validationResults);
      }
      if (StringUtils.isNotBlank(selectedAddressCode))
      {
         final AddressData selectedAddressData = getCheckoutFacade().getDeliveryAddressForCode(selectedAddressCode);
         final boolean hasSelectedAddressData = selectedAddressData != null;
         if (hasSelectedAddressData)
         {
            setDeliveryAddress(selectedAddressData);
         }
      }
      return getCheckoutStep().nextStep();
   }

   protected void setDeliveryAddress(final AddressData selectedAddressData)
   {
      final AddressData cartCheckoutDeliveryAddress = getCheckoutFacade().getCheckoutCart().getDeliveryAddress();
      if (isAddressIdChanged(cartCheckoutDeliveryAddress, selectedAddressData))
      {
         getCheckoutFacade().setDeliveryAddress(selectedAddressData);
         if (cartCheckoutDeliveryAddress != null && !cartCheckoutDeliveryAddress.isVisibleInAddressBook())
         { // temporary address should be removed
            getUserFacade().removeAddress(cartCheckoutDeliveryAddress);
         }
      }
   }

   @RequestMapping(value = "/back", method = RequestMethod.GET)
   @RequireHardLogIn
   @Override
   public String back(final RedirectAttributes redirectAttributes)
   {
      return getCheckoutStep().previousStep();
   }

   @RequestMapping(value = "/next", method = RequestMethod.GET)
   @RequireHardLogIn
   @Override
   public String next(final RedirectAttributes redirectAttributes)
   {
      return getCheckoutStep().nextStep();
   }

   protected String getBreadcrumbKey()
   {
      return "checkout.multi." + getCheckoutStep().getProgressBarId() + ".breadcrumb";
   }

   protected CheckoutStep getCheckoutStep()
   {
      return getCheckoutStep(DELIVERY_ADDRESS);
   }

   protected void populateCommonModelAttributes(final Model model, final CartData cartData, final WorldlineAddressForm addressForm)
         throws CMSItemNotFoundException
   {
      model.addAttribute("cartData", cartData);
      model.addAttribute("addressForm", addressForm);
      model.addAttribute("deliveryAddresses", getDeliveryAddresses(cartData.getDeliveryAddress()));
      model.addAttribute("noAddress", Boolean.valueOf(getCheckoutFlowFacade().hasNoDeliveryAddress()));
      model.addAttribute("addressFormEnabled", Boolean.valueOf(getCheckoutFacade().isNewAddressEnabledForCart()));
      model.addAttribute("removeAddressEnabled", Boolean.valueOf(getCheckoutFacade().isRemoveAddressEnabledForCart()));
      model.addAttribute(SHOW_SAVE_TO_ADDRESS_BOOK_ATTR, Boolean.TRUE);
      model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs(getBreadcrumbKey()));
      model.addAttribute("metaRobots", "noindex,nofollow");
      if (StringUtils.isNotBlank(addressForm.getCountryIso()))
      {
         model.addAttribute("regions", getI18NFacade().getRegionsForCountryIso(addressForm.getCountryIso()));
         model.addAttribute("country", addressForm.getCountryIso());
      }
      prepareDataForPage(model);
      final ContentPageModel multiCheckoutSummaryPage = getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL);
      storeCmsPageInModel(model, multiCheckoutSummaryPage);
      setUpMetaDataForContentPage(model, multiCheckoutSummaryPage);
      setCheckoutStepLinksForModel(model, getCheckoutStep());
   }
}
