/*******************************************************************************
 *
 *    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.graphql.resource;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.caconfig.ConfigurationBuilder;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.cq.commerce.graphql.magento.GraphqlDataService;
import com.adobe.cq.commerce.graphql.magento.GraphqlDataServiceConfiguration;
import com.adobe.cq.commerce.virtual.catalog.data.CatalogDataResourceProviderFactory;
import com.adobe.cq.commerce.virtual.catalog.data.CatalogIdentifier;
import com.day.cq.commons.inherit.ComponentInheritanceValueMap;
import com.day.cq.commons.inherit.HierarchyNodeInheritanceValueMap;
import com.day.cq.commons.inherit.InheritanceValueMap;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;

import static com.adobe.cq.commerce.graphql.resource.Constants.MAGENTO_GRAPHQL_PROVIDER;

/**
 * Factory class for CIF integration data resource provider.
 */
@Component(
    service = { CatalogDataResourceProviderFactory.class, CatalogIdentifier.class },
    immediate = true,
    property = {
        CatalogDataResourceProviderFactory.PROPERTY_FACTORY_SERVICE_ID + "=" + MAGENTO_GRAPHQL_PROVIDER
    })
public class GraphqlResourceProviderFactory implements CatalogDataResourceProviderFactory<Object>, CatalogIdentifier {

    private static final Logger LOGGER = LoggerFactory.getLogger(GraphqlResourceProviderFactory.class);

    private static final String CONFIGURATION_NAME = "cloudconfigs/commerce";

    protected Map<String, GraphqlDataService> clients = new ConcurrentHashMap<>();

    @Reference(
        service = GraphqlDataService.class,
        bind = "bindGraphqlDataService",
        unbind = "unbindGraphqlDataService",
        cardinality = ReferenceCardinality.AT_LEAST_ONE,
        policy = ReferencePolicy.DYNAMIC)
    protected void bindGraphqlDataService(GraphqlDataService client, Map<?, ?> properties) {
        String identifier = client.getIdentifier();
        LOGGER.info("Registering GraphqlDataService '{}'", identifier);
        clients.put(identifier, client);
    }

    protected void unbindGraphqlDataService(GraphqlDataService client, Map<?, ?> properties) {
        String identifier = client.getIdentifier();
        LOGGER.info("De-registering GraphqlDataService '{}'", identifier);
        clients.remove(identifier);
    }

    @Override
    public GraphqlResourceProvider createResourceProvider(Resource root) {
        LOGGER.debug("Creating resource provider for resource at path {}", root.getPath());

        ConfigurationBuilder configurationBuilder = root.adaptTo(ConfigurationBuilder.class);
        ValueMap properties = configurationBuilder.name(CONFIGURATION_NAME)
            .asValueMap();

        Map<String, String> collectedProperties = new HashMap<>();
        if (properties.size() == 0) {
            collectedProperties = readFallbackConfiguration(root);
        } else {
            for (String key : properties.keySet()) {
                collectedProperties.put(key, properties.get(key, ""));
            }
        }

        String catalogIdentifier = collectedProperties.get(GraphqlDataServiceConfiguration.CQ_CATALOG_IDENTIFIER);
        if (StringUtils.isEmpty(catalogIdentifier)) {
            LOGGER.warn("Could not find cq:catalogIdentifier property for given resource at " + root.getPath());
            return null;
        }

        // Check Magento root category id
        String rootCategoryId = collectedProperties.get(Constants.MAGENTO_ROOT_CATEGORY_ID_PROPERTY);
        try {
            Integer.valueOf(rootCategoryId);
        } catch (NumberFormatException x) {
            LOGGER.warn("Invalid {} {} at {}", Constants.MAGENTO_ROOT_CATEGORY_ID_PROPERTY, rootCategoryId, root.getPath());
            return null;
        }

        GraphqlDataService client = clients.get(catalogIdentifier);
        if (client == null) {
            LOGGER.warn("No MagentoGraphqlClient instance available for catalog identifier " + catalogIdentifier);
            return null;
        }

        GraphqlResourceProvider resourceProvider = new GraphqlResourceProvider(root.getPath(), client, collectedProperties);
        return resourceProvider;
    }

    @Override
    public Collection<String> getAllCatalogIdentifiers() {
        return clients.keySet();
    }

    @Override
    public String getCommerceProviderName() {
        return MAGENTO_GRAPHQL_PROVIDER;
    }

    private Map<String, String> readFallbackConfiguration(Resource res) {
        InheritanceValueMap ivm;
        Page page = res.getResourceResolver()
            .adaptTo(PageManager.class)
            .getContainingPage(res);
        if (page != null) {
            ivm = new HierarchyNodeInheritanceValueMap(page.getContentResource());
        } else {
            ivm = new ComponentInheritanceValueMap(res);
        }

        Map<String, String> properties = new HashMap<String, String>();
        properties.put(GraphqlDataServiceConfiguration.CQ_CATALOG_IDENTIFIER, ivm.getInherited(
            GraphqlDataServiceConfiguration.CQ_CATALOG_IDENTIFIER, String.class));
        properties.put(Constants.MAGENTO_ROOT_CATEGORY_ID_PROPERTY, ivm.getInherited(Constants.MAGENTO_ROOT_CATEGORY_ID_PROPERTY,
            String.class));
        properties.put(Constants.MAGENTO_STORE_PROPERTY, ivm.getInherited("cq:" + Constants.MAGENTO_STORE_PROPERTY, String.class));

        return properties;
    }
}