/*
 ************************************************************************************
 * Copyright (C) 2020 Openbravo S.L.U.
 * Licensed under the Openbravo Commercial License version 1.0
 * You may obtain a copy of the License at http://www.openbravo.com/legal/obcl.html
 * or in the legal folder of this module distribution.
 ************************************************************************************
 */

package com.openbravo.norauto.mdm.connector.mappings.javamapper;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.criterion.Restrictions;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.provider.OBProvider;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.security.OrganizationStructureProvider;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.financial.multitaxcategory.Product_TaxCategory;
import org.openbravo.france.ecotax.PhiecoTaxcategory;
import org.openbravo.model.ad.system.Language;
import org.openbravo.model.common.enterprise.Organization;
import org.openbravo.model.common.plm.Characteristic;
import org.openbravo.model.common.plm.CharacteristicValue;
import org.openbravo.model.common.plm.Product;
import org.openbravo.model.common.plm.ProductCategory;
import org.openbravo.model.common.plm.ProductCharacteristic;
import org.openbravo.model.common.plm.ProductCharacteristicValue;
import org.openbravo.model.common.plm.ProductStatus;
import org.openbravo.model.common.plm.ProductTrl;
import org.openbravo.model.common.uom.UOM;
import org.openbravo.model.financialmgmt.tax.TaxCategory;
import org.openbravo.model.pricing.pricelist.PriceList;
import org.openbravo.model.pricing.pricelist.PriceListVersion;
import org.openbravo.model.pricing.pricelist.ProductPrice;
import org.openbravo.multiupc.MultiUPC;
import org.openbravo.retail.config.OBRETCOProductList;
import org.openbravo.retail.config.OBRETCOProlProduct;
import org.openbravo.service.external.integration.SynchronizableBusinessObject;
import org.openbravo.service.external.integration.mapping.EntityMappingId;
import org.openbravo.service.external.integration.mapping.ImportJavaPropertyMappingHandler;

import com.openbravo.norauto.mdm.connector.mappings.utility.MDMIntegrationUtil;
import com.openbravo.norauto.mdm.connector.mappings.utility.MDMIntegrationsConstants;

/**
 * ImportProductJavaPropertyMappingHandler for the Product entity mapping
 */
@EntityMappingId(MDMIntegrationsConstants.PRODUCT_ENTITY_MAPPING_ID)
public class ImportProductJavaPropertyMappingHandler
    extends ImportJavaPropertyMappingHandler<Product> {

  private static final Logger log = LogManager.getLogger();

  @Override
  public Map<Integer, String> getPropertySorting() {
    Map<Integer, String> properties = new HashMap<>();
    properties.put(11, "upcList");
    properties.put(21, "category");
    properties.put(31, "characteristics");
    properties.put(41, "uom");
    properties.put(51, "translations");
    properties.put(61, "organizations");
    properties.put(71, "organization");
    properties.put(81, "deee");
    return properties;

  }

  @SuppressWarnings("unchecked")
  @Override
  public void setPropertyInImportedObject(Product product, String mappingName,
      SynchronizableBusinessObject sbo) {
    Map<String, Object> properties = sbo.getProperties();
    if (!properties.containsKey(mappingName)) {
      log.warn("Property: {} is not included in SynchronizableBusinessObject {}", mappingName,
          properties.keySet());
      return;
    }
    try {
      Object itemValue = properties.get(mappingName);
      switch (mappingName) {
        case "upcList":
          List<SynchronizableBusinessObject> upcList = (List<SynchronizableBusinessObject>) itemValue;
          String organizationSearchKey = (String) properties.get("organization");
          Organization organization = MDMIntegrationUtil.getOrganization(organizationSearchKey);
          setMultiUPC(product, upcList, organization);
          break;
        case "category":
          setProductCategory(product, (String) itemValue);
          break;
        case "characteristics":
          List<SynchronizableBusinessObject> characteristicsList = (List<SynchronizableBusinessObject>) itemValue;
          setCharacteristics(product, characteristicsList);
          break;
        case "uom":
          setUOM(product, (String) itemValue);
          break;
        case "translations":
          List<SynchronizableBusinessObject> translations = (List<SynchronizableBusinessObject>) itemValue;
          setTranslations(product, translations);
          break;
        case "organizations":
          List<SynchronizableBusinessObject> organizations = (List<SynchronizableBusinessObject>) itemValue;
          setOrganizations(product, organizations);
          break;
        case "organization":
          setOrganization(product, (String) itemValue);
          break;
        case "deee":
          setDeeeTax(product, (String) itemValue);
          break;
        default:
          log.warn("Java property not defined in the mapping: {}", mappingName);
      }
    } catch (Exception e) {
      log.error("Error while importing the property " + mappingName, e);
      throw new OBException(e);
    }
  }

  private void setDeeeTax(Product product, String deeTax) {
    if (StringUtils.isBlank(deeTax)) {
      return;
    }
    BigDecimal deeeQty = new BigDecimal(deeTax);
    if (deeeQty.compareTo(BigDecimal.ZERO) > 0) {
      product.setPhiecoDeee(getDefaultDeeCategory());
      product.setPhiecoDeeeQty(deeeQty);
    }
  }

  private PhiecoTaxcategory getDefaultDeeCategory() {
    OBCriteria<PhiecoTaxcategory> criteria = OBDal.getInstance()
        .createCriteria(PhiecoTaxcategory.class);
    criteria.add(Restrictions.eq(PhiecoTaxcategory.PROPERTY_SEARCHKEY, "UNIT"));
    criteria.setFilterOnReadableOrganization(false);
    criteria.setMaxResults(1);
    return (PhiecoTaxcategory) criteria.uniqueResult();
  }

  private void setOrganization(Product product, String organizationSearchKey) {
    Organization organization = MDMIntegrationUtil.getOrganization(organizationSearchKey);
    if (organization == null) {
      throw new OBException("Organization not found with searchKey " + organizationSearchKey);
    }
    product.setOrganization(organization);
  }

  private void setMultiUPC(Product product, List<SynchronizableBusinessObject> upcs,
      Organization organization) {
    String upcItem = null;
    if (product.isNewOBObject()) {
      String upcHead = (String) upcs.get(0).getProperties().get("value");
      updateOrDeleteUPCOccurrencesByProductOrg(product, upcHead, organization);
      product.setUPCEAN(upcHead);
      upcs.remove(0);
      for (SynchronizableBusinessObject upc : upcs) {
        upcItem = (String) upc.getProperties().get("value");
        updateOrDeleteUPCOccurrencesByProductOrg(product, upcItem, organization);
        addMultiUPC(product, upcItem, organization);
      }
    } else {
      for (SynchronizableBusinessObject upc : upcs) {
        upcItem = (String) upc.getProperties().get("value");
        updateOrDeleteUPCOccurrencesByProductOrg(product, upcItem, organization);
        if (!existUPC(product, upcItem)) {
          if (StringUtils.isBlank(product.getUPCEAN())) {
            product.setUPCEAN(upcItem);
          } else {
            addMultiUPC(product, upcItem, organization);
          }
        }
      }
    }
  }

  private void updateOrDeleteUPCOccurrencesByProductOrg(Product product, String upcItem,
      Organization organization) {
    int updated = updateUPCOccurrencesByProductOrg(product, upcItem, organization);
    int deleted = deleteMultiUPCOccurrencesByProductOrg(product, upcItem);
    log.info("Updated {} occurrences of the UPC/EAN value {} in Product window", updated, upcItem);
    log.info("Deleted {} occurrences of the UPC/EAN value {} in Multi UPC window", deleted,
        upcItem);
  }

  private int updateUPCOccurrencesByProductOrg(Product product, String upcItem,
      Organization organization) {
  //@formatter:off
    String hql =
            "update Product " +
            "set uPCEAN = null " +
            " where id <> :productId " +
            "   and organization.id = :orgId " + 
            "   and uPCEAN = :upc ";
    //@formatter:on

    return OBDal.getInstance()
        .getSession()
        .createQuery(hql)
        .setParameter("productId", product.getId())
        .setParameter("orgId", organization.getId())
        .setParameter("upc", upcItem)
        .executeUpdate();
  }

  private int deleteMultiUPCOccurrencesByProductOrg(Product product, String upcItem) {
  //@formatter:off
    String hql =
            "delete from obmupc_prod_multiupc " +
            " where product.id <> :productId " +
            "   and upc = :upc ";
    //@formatter:on

    return OBDal.getInstance()
        .getSession()
        .createQuery(hql)
        .setParameter("productId", product.getId())
        .setParameter("upc", upcItem)
        .executeUpdate();
  }

  private void setProductCategory(Product product, String itemValue) {
    ProductCategory productCategory = MDMIntegrationUtil.getProductCategoryBySearchKey(itemValue);
    if (productCategory == null) {
      throw new OBException(
          String.format("Unable to find Product Category [%s] for product with SK [%s]", itemValue,
              product.getSearchKey()));
    }
    product.setProductCategory(productCategory);
  }

  private void setCharacteristics(Product product,
      List<SynchronizableBusinessObject> characteristics) {
    for (SynchronizableBusinessObject ch : characteristics) {
      String chName = (String) ch.getProperties().get("name");
      String chCode = (String) ch.getProperties().get("characteristic");
      if (!StringUtils.isEmpty(chName) && !StringUtils.isEmpty(chCode)) {
        Characteristic characteristicItem = getCharacteristicByName(chName);
        if (characteristicItem != null) {
          CharacteristicValue characteristicValueItem = getOrCreateCharacteristicValueIfNotExists(
              characteristicItem, chCode);
          if (characteristicValueItem != null) {
            addProductCharacteristicValue(product, characteristicItem, characteristicValueItem);
          } else {
            throw new OBException(
                String.format("Unable to find Characteristic value [%s] for characteristic  [%s]",
                    chCode, chName));
          }
        } else {
          throw new OBException(String.format("Unable to find Characteristic [%s]", chName));
        }
      }
    }
  }

  private void setUOM(Product product, String itemValue) {
    UOM uom = MDMIntegrationUtil.getUOMByEdiCode(itemValue);
    if (uom == null) {
      throw new OBException(String.format("Unable to find UOM [%s] for product with SK [%s]",
          itemValue, product.getSearchKey()));
    }
    product.setUOM(uom);
  }

  private void setTranslations(Product product, List<SynchronizableBusinessObject> translations) {
    for (SynchronizableBusinessObject translation : translations) {
      addTranslation(product, translation.getProperties());
    }
  }

  private void setOrganizations(Product product, List<SynchronizableBusinessObject> organizations) {
    Map<String, String> assortmentsList = new HashMap<String, String>();
    for (SynchronizableBusinessObject organization : organizations) {
      Organization orgItem = MDMIntegrationUtil
          .getOrganization((String) organization.getProperties().get("code"));
      if (orgItem != null) {
        addOrganizationInfo(product, organization, orgItem);
        addAssortment(product, organization, orgItem, assortmentsList);
      } else {
        throw new OBException(String.format("Unable to find Organization with SK [%s]",
            (String) organization.getProperties().get("code")));
      }
    }
  }

  private void addAssortment(Product product, SynchronizableBusinessObject organizationSBO,
      Organization organization, Map<String, String> assortmentsList) {
    // isSold and isActive
    String isSold = (String) organizationSBO.getProperties().get("isSold");
    String isActive = (String) organizationSBO.getProperties().get("isActive");

    OBRETCOProductList assortment = organization.getObretcoProductlist();
    if (assortment != null && !assortmentsList.containsKey(assortment.getId())) {
      assortmentsList.put(assortment.getId(), assortment.getName());
      addProductToAssortment(assortment, product, isSold, isActive);
    }
  }

  private void addOrganizationInfo(Product product, SynchronizableBusinessObject organization,
      Organization orgItem) {
    String taxCode = (String) organization.getProperties().get("taxCode");
    String priceSt = (String) organization.getProperties().get("price");
    String isActive = (String) organization.getProperties().get("isActive");
    final Organization org = OBDal.getInstance().get(Organization.class, orgItem.getId());
    // taxCode
    if (!StringUtils.isEmpty(taxCode)) {
      addProductTaxCategory(product, org, taxCode);
    }
    // price
    if (!StringUtils.isEmpty(priceSt)) {
      BigDecimal price = new BigDecimal((String) organization.getProperties().get("price"));
      addPrice(product, org, price, isActive);
    }
  }

  private void addMultiUPC(Product product, String upc, Organization organization) {
    MultiUPC dataMultiUpc = OBProvider.getInstance().get(MultiUPC.class);
    dataMultiUpc.setOrganization(organization);
    dataMultiUpc.setProduct(product);
    dataMultiUpc.setUpc(upc);
    product.getObmupcProdMultiupcList().add(dataMultiUpc);
  }

  private void addProductTaxCategory(Product product, Organization org, String taxCategoryCode) {

    OrganizationStructureProvider osp = OBContext.getOBContext().getOrganizationStructureProvider();
    List<String> parentOrgs = osp.getParentList(org.getId(), true);

    for (String orgId : parentOrgs) {

      TaxCategory taxCategory = MDMIntegrationUtil.getTaxCategory(orgId, taxCategoryCode);

      if (taxCategory == null) {
        continue;
      }

      Product_TaxCategory dataProductTaxCategory = null;
      if (product.isNewOBObject()) {
        Optional<String> alreadyCreated = product.getOBFMTCProductTaxCategoryList()
            .stream()
            .map(e -> e.getOrganization().getId())
            .filter(e -> e.equals(org.getId()))
            .findAny();
        if (alreadyCreated.isPresent()) {
          continue;
        }
        dataProductTaxCategory = OBProvider.getInstance().get(Product_TaxCategory.class);
      } else {
        dataProductTaxCategory = getProductTaxCategoryByOrg(product, org);
        if (dataProductTaxCategory == null) {
          dataProductTaxCategory = OBProvider.getInstance().get(Product_TaxCategory.class);
        }
      }
      dataProductTaxCategory.setNorcusTvaCode(taxCategoryCode);
      dataProductTaxCategory.setProduct(product);
      dataProductTaxCategory.setOrganization(org);
      dataProductTaxCategory.setTaxCategory(taxCategory);
      product.getOBFMTCProductTaxCategoryList().add(dataProductTaxCategory);
    }

  }

  private void addProductCharacteristicValue(Product product, Characteristic ch,
      CharacteristicValue chValue) {
    createOrUpdateProductCharacteristic(product, ch);
    createOrUpdateProductCharacteristicValue(product, ch, chValue);
  }

  private void createOrUpdateProductCharacteristic(Product product, Characteristic ch) {
    ProductCharacteristic productCharacteristic = null;
    if (product.isNewOBObject()) {
      productCharacteristic = OBProvider.getInstance().get(ProductCharacteristic.class);
    } else {
      productCharacteristic = getProductCharacteristic(product, ch);
      if (productCharacteristic == null) {
        productCharacteristic = OBProvider.getInstance().get(ProductCharacteristic.class);
      }
    }
    if (productCharacteristic.isNewOBObject()) {
      productCharacteristic.setProduct(product);
      productCharacteristic.setCharacteristic(ch);
      productCharacteristic
          .setSequenceNumber((product.getProductCharacteristicList().size() + 1) * 10L);
      product.getProductCharacteristicList().add(productCharacteristic);
    }
  }

  public static ProductCharacteristic getProductCharacteristic(Product product, Characteristic ch) {
    OBCriteria<ProductCharacteristic> crit = OBDal.getInstance()
        .createCriteria(ProductCharacteristic.class);
    crit.add(Restrictions.eq(ProductCharacteristic.PROPERTY_PRODUCT, product));
    crit.add(Restrictions.eq(ProductCharacteristic.PROPERTY_CHARACTERISTIC, ch));
    return (ProductCharacteristic) crit.uniqueResult();
  }

  private void createOrUpdateProductCharacteristicValue(Product product, Characteristic ch,
      CharacteristicValue chValue) {
    ProductCharacteristicValue dataProductCharacteristicValue = null;
    if (product.isNewOBObject()) {
      dataProductCharacteristicValue = OBProvider.getInstance()
          .get(ProductCharacteristicValue.class);
    } else {
      dataProductCharacteristicValue = getProductCharacteristicValue(product, ch);
      if (dataProductCharacteristicValue == null) {
        dataProductCharacteristicValue = OBProvider.getInstance()
            .get(ProductCharacteristicValue.class);
      }
    }
    dataProductCharacteristicValue.setProduct(product);
    dataProductCharacteristicValue.setCharacteristic(ch);
    dataProductCharacteristicValue.setCharacteristicValue(chValue);
    product.getProductCharacteristicValueList().add(dataProductCharacteristicValue);
  }

  private void addTranslation(Product product, Map<String, Object> translationInfo) {
    Language language = MDMIntegrationUtil.getLanguage((String) translationInfo.get("language"));
    ProductTrl dataTranslation;
    if (product.isNewOBObject()) {
      dataTranslation = OBProvider.getInstance().get(ProductTrl.class);
    } else {
      dataTranslation = getProductTrlByLanguage(product, language);
      if (dataTranslation == null) {
        dataTranslation = OBProvider.getInstance().get(ProductTrl.class);
      }
    }
    dataTranslation.setProduct(product);
    dataTranslation.setLanguage(language);
    dataTranslation.setName((String) translationInfo.get("translation"));
    product.getProductTrlList().add(dataTranslation);
  }

  private void addProductToAssortment(final OBRETCOProductList assortment, final Product product,
      String isSold, String isActive) {
    OBRETCOProlProduct dataProductAssortment = null;

    if (product.isNewOBObject()) {
      dataProductAssortment = OBProvider.getInstance().get(OBRETCOProlProduct.class);
    } else {
      dataProductAssortment = getProductAssortment(product, assortment);
      if (dataProductAssortment == null) {
        dataProductAssortment = OBProvider.getInstance().get(OBRETCOProlProduct.class);
      }
    }
    dataProductAssortment.setProduct(product);
    dataProductAssortment.setObretcoProductlist(assortment);
    if (isSold == null || isSold.equals("Y")) {
      dataProductAssortment.setProductStatus(null);
    } else {
      dataProductAssortment.setProductStatus(getObsoleteStatus());
    }
    if (isActive != null) {
      dataProductAssortment.setActive(MDMIntegrationUtil.getBooleanValue(isActive));
    }
    assortment.getOBRETCOProlProductList().add(dataProductAssortment);
  }

  private void addPrice(final Product product, final Organization org, final BigDecimal price,
      String isActive) {
    PriceList priceList = getPriceListByOrg(org);
    if (priceList != null) {
      PriceListVersion priceListVersion = null;
      if (priceList.getPricingPriceListVersionList().size() > 1) {
        throw new OBException(String.format(
            "There is more than one price list version for the price list of organization [%s]",
            org.getName()));
      } else if (priceList.getPricingPriceListVersionList().size() == 1) {
        priceListVersion = priceList.getPricingPriceListVersionList().get(0);
        ProductPrice dataPrice = null;
        if (product.isNewOBObject()) {
          dataPrice = OBProvider.getInstance().get(ProductPrice.class);
        } else {
          dataPrice = getProductPriceByPriceList(product, priceListVersion);
          if (dataPrice == null) {
            dataPrice = OBProvider.getInstance().get(ProductPrice.class);
          }
        }
        dataPrice.setProduct(product);
        dataPrice.setOrganization(org);
        dataPrice.setPriceListVersion(priceListVersion);
        dataPrice.setListPrice(price);
        dataPrice.setPriceLimit(price);
        dataPrice.setStandardPrice(price);
        dataPrice
            .setActive((isActive == null) ? true : MDMIntegrationUtil.getBooleanValue(isActive));
        product.getPricingProductPriceList().add(dataPrice);
      } else {
        log.warn("Price List Version not defined for the price list " + priceList.getName());
      }
    } else {
      log.warn("Price List not defined for the organization " + org.getName());
    }
  }

  public static boolean existUPC(Product product, String upc) {
    OBCriteria<MultiUPC> crit = OBDal.getInstance().createCriteria(MultiUPC.class);
    crit.add(Restrictions.eq(MultiUPC.PROPERTY_PRODUCT, product));
    crit.add(Restrictions.eq(MultiUPC.PROPERTY_UPC, upc));
    MultiUPC dataMultiUpc = (MultiUPC) crit.uniqueResult();
    return (dataMultiUpc != null) || (StringUtils.equals(product.getUPCEAN(), upc));
  }

  public static ProductCharacteristicValue getProductCharacteristicValue(Product product,
      final Characteristic ch) {
    OBCriteria<ProductCharacteristicValue> crit = OBDal.getInstance()
        .createCriteria(ProductCharacteristicValue.class);
    crit.add(Restrictions.eq(ProductCharacteristicValue.PROPERTY_PRODUCT, product));
    crit.add(Restrictions.eq(ProductCharacteristicValue.PROPERTY_CHARACTERISTIC, ch));
    return (ProductCharacteristicValue) crit.uniqueResult();
  }

  public static ProductTrl getProductTrlByLanguage(Product product, Language language) {
    OBCriteria<ProductTrl> crit = OBDal.getInstance().createCriteria(ProductTrl.class);
    crit.add(Restrictions.eq(ProductTrl.PROPERTY_PRODUCT, product));
    crit.add(Restrictions.eq(ProductTrl.PROPERTY_LANGUAGE, language));
    return (ProductTrl) crit.uniqueResult();
  }

  private Product_TaxCategory getProductTaxCategoryByOrg(final Product product,
      final Organization org) {
    OBCriteria<Product_TaxCategory> crit = OBDal.getInstance()
        .createCriteria(Product_TaxCategory.class);
    crit.add(Restrictions.eq(Product_TaxCategory.PROPERTY_PRODUCT, product));
    crit.add(Restrictions.eq(Product_TaxCategory.PROPERTY_ORGANIZATION, org));
    return (Product_TaxCategory) crit.uniqueResult();
  }

  private OBRETCOProlProduct getProductAssortment(final Product product,
      final OBRETCOProductList assortment) {
    OBCriteria<OBRETCOProlProduct> criteria = OBDal.getInstance()
        .createCriteria(OBRETCOProlProduct.class);
    criteria.add(Restrictions.eq(OBRETCOProlProduct.PROPERTY_OBRETCOPRODUCTLIST, assortment));
    criteria.add(Restrictions.eq(OBRETCOProlProduct.PROPERTY_PRODUCT, product));
    criteria.setFilterOnActive(false);
    return (OBRETCOProlProduct) criteria.uniqueResult();
  }

  private ProductStatus getObsoleteStatus() {
    OBCriteria<ProductStatus> criteria = OBDal.getInstance().createCriteria(ProductStatus.class);
    criteria.add(Restrictions.eq(ProductStatus.PROPERTY_RESTRICTDISTRIBORDERISSUE, true));
    return (ProductStatus) criteria.uniqueResult();
  }

  public static PriceList getPriceListByOrg(Organization org) {
    OBCriteria<PriceList> criteria = OBDal.getInstance().createCriteria(PriceList.class);
    criteria.add(Restrictions.eq(PriceList.PROPERTY_ORGANIZATION, org));
    return (PriceList) criteria.uniqueResult();
  }

  private ProductPrice getProductPriceByPriceList(final Product product,
      final PriceListVersion plv) {
    OBCriteria<ProductPrice> crit = OBDal.getInstance().createCriteria(ProductPrice.class);
    crit.add(Restrictions.eq(ProductPrice.PROPERTY_PRODUCT, product));
    crit.add(Restrictions.eq(ProductPrice.PROPERTY_PRICELISTVERSION, plv));
    crit.setFilterOnActive(false);
    return (ProductPrice) crit.uniqueResult();
  }

  private Characteristic getCharacteristicByName(final String characteristicName) {
    final OBCriteria<Characteristic> criteria = OBDal.getInstance()
        .createCriteria(Characteristic.class);
    criteria.add(Restrictions.eq(Characteristic.PROPERTY_NAME, characteristicName));
    criteria.setMaxResults(1);
    return (Characteristic) criteria.uniqueResult();
  }

  private CharacteristicValue getOrCreateCharacteristicValueIfNotExists(
      final Characteristic characteristic, final String characteristicCode) {

    OBCriteria<CharacteristicValue> criteria = OBDal.getInstance()
        .createCriteria(CharacteristicValue.class);
    criteria.add(Restrictions.eq(CharacteristicValue.PROPERTY_CHARACTERISTIC, characteristic));
    criteria.add(Restrictions.eq(CharacteristicValue.PROPERTY_CODE, characteristicCode));
    criteria.setMaxResults(1);
    CharacteristicValue characteristicValue = (CharacteristicValue) criteria.uniqueResult();

    if (characteristicValue == null) {
      String chName = characteristic.getName();
      if (chName.equals(MDMIntegrationsConstants.PRODUCT_TIRE_WIDTH_CHARACTERISTIC_NAME)
          || chName.equals(MDMIntegrationsConstants.PRODUCT_HEIGHT_WIDTH_CHARACTERISTIC_NAME)
          || chName.equals(MDMIntegrationsConstants.PRODUCT_DIAMETER_WIDTH_CHARACTERISTIC_NAME)) {
        characteristicValue = createCharacteristicValue(characteristic, characteristicCode);
      }
    }
    return characteristicValue;
  }

  private CharacteristicValue createCharacteristicValue(final Characteristic characteristic,
      final String characteristicValueCode) {
    final CharacteristicValue chValue = OBProvider.getInstance().get(CharacteristicValue.class);
    chValue.setNewOBObject(true);
    chValue.setName(characteristicValueCode);
    chValue.setCode(characteristicValueCode);
    chValue.setCharacteristic(characteristic);
    OBDal.getInstance().save(chValue);
    characteristic.getCharacteristicValueList().add(chValue);
    return chValue;
  }
}
