/*******************************************************************************
 *
 *    Copyright 2019 Adobe. All rights reserved.
 *    This file is licensed to you under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License. You may obtain a copy
 *    of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software distributed under
 *    the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
 *    OF ANY KIND, either express or implied. See the License for the specific language
 *    governing permissions and limitations under the License.
 *
 ******************************************************************************/

package com.adobe.cq.commerce.core.components.internal.models.v1.relatedproducts;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.cq.commerce.core.components.client.MagentoGraphqlClient;
import com.adobe.cq.commerce.core.components.internal.models.v1.common.PriceImpl;
import com.adobe.cq.commerce.core.components.internal.models.v1.common.ProductListItemImpl;
import com.adobe.cq.commerce.core.components.internal.models.v1.relatedproducts.RelatedProductsRetriever.RelationType;
import com.adobe.cq.commerce.core.components.models.common.Price;
import com.adobe.cq.commerce.core.components.models.common.ProductListItem;
import com.adobe.cq.commerce.core.components.models.productcarousel.ProductCarousel;
import com.adobe.cq.commerce.core.components.models.retriever.AbstractProductsRetriever;
import com.adobe.cq.commerce.core.components.services.UrlProvider;
import com.adobe.cq.commerce.core.components.services.UrlProvider.ProductIdentifierType;
import com.adobe.cq.commerce.core.components.utils.SiteNavigation;
import com.adobe.cq.commerce.magento.graphql.ProductInterface;
import com.day.cq.wcm.api.Page;

@Model(adaptables = SlingHttpServletRequest.class, adapters = ProductCarousel.class, resourceType = RelatedProductsImpl.RESOURCE_TYPE)
public class RelatedProductsImpl implements ProductCarousel {

    protected static final String RESOURCE_TYPE = "core/cif/components/commerce/relatedproducts/v1/relatedproducts";
    private static final Logger LOGGER = LoggerFactory.getLogger(RelatedProductsImpl.class);

    protected static final String PN_PRODUCT = "product";
    protected static final String PN_RELATION_TYPE = "relationType";

    @Self
    private SlingHttpServletRequest request;

    @Inject
    private Resource resource;

    @Inject
    private Page currentPage;

    @Inject
    private UrlProvider urlProvider;

    @ScriptVariable
    private ValueMap properties;

    private Page productPage;
    private MagentoGraphqlClient magentoGraphqlClient;
    private AbstractProductsRetriever productsRetriever;
    private Locale locale;

    @PostConstruct
    private void initModel() {
        if (!isConfigured()) {
            return;
        }

        magentoGraphqlClient = MagentoGraphqlClient.create(resource);
        productPage = SiteNavigation.getProductPage(currentPage);
        if (productPage == null) {
            productPage = currentPage;
        }

        locale = productPage.getLanguage(false);

        if (magentoGraphqlClient == null) {
            LOGGER.error("Cannot get a GraphqlClient using the resource at {}", resource.getPath());
        } else {
            configureProductsRetriever();
        }
    }

    @Override
    public boolean isConfigured() {
        return properties.get(PN_PRODUCT, String.class) != null || request.getRequestPathInfo().getSelectorString() != null;
    }

    private void configureProductsRetriever() {
        String relationType = properties.get(PN_RELATION_TYPE, String.class);
        String product = properties.get(PN_PRODUCT, String.class);
        String skuOrSlug;
        ProductIdentifierType productIdentifierType;

        if (product != null) {
            skuOrSlug = product; // The picker is configured to return the SKU
            productIdentifierType = ProductIdentifierType.SKU;
        } else {
            Pair<ProductIdentifierType, String> identifier = urlProvider.getProductIdentifier(request);
            skuOrSlug = identifier.getRight();
            productIdentifierType = identifier.getLeft();
        }

        RelationType rel = relationType != null ? RelationType.valueOf(relationType) : RelationType.RELATED_PRODUCTS;
        productsRetriever = new RelatedProductsRetriever(magentoGraphqlClient, rel, productIdentifierType);
        productsRetriever.setIdentifiers(Collections.singletonList(skuOrSlug));
    }

    @Override
    public List<ProductListItem> getProducts() {
        if (productsRetriever == null || !isConfigured()) {
            return Collections.emptyList();
        }

        List<ProductInterface> products = productsRetriever.fetchProducts();
        if (products.isEmpty()) {
            return Collections.emptyList();
        }

        List<ProductListItem> carouselProductList = new ArrayList<>();
        for (ProductInterface product : products) {
            try {
                Price price = new PriceImpl(product.getPriceRange(), locale);
                carouselProductList.add(new ProductListItemImpl(
                    product.getSku(),
                    product.getUrlKey(),
                    product.getName(),
                    price,
                    product.getThumbnail().getUrl(),
                    productPage,
                    null,
                    request,
                    urlProvider));
            } catch (Exception e) {
                LOGGER.error("Failed to instantiate product " + (product != null ? product.getSku() : null), e);
            }
        }
        return carouselProductList;
    }

    @Override
    public AbstractProductsRetriever getProductsRetriever() {
        return productsRetriever;
    }

}