/*
 * Asset Share Commons
 *
 * Copyright (C) 2017 Adobe
 *
 * Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.adobe.aem.commons.assetshare.components.predicates;

import com.adobe.aem.commons.assetshare.util.PredicateUtil;
import com.adobe.cq.wcm.core.components.models.form.Field;
import com.adobe.cq.wcm.core.components.models.form.OptionItem;
import com.day.cq.wcm.commons.WCMUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.Required;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Named;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

public abstract class AbstractPredicate implements Predicate {
    private static final Logger log = LoggerFactory.getLogger(AbstractPredicate.class);

    public static final String REQUEST_ATTR_PREDICATE_GROUP_TRACKER = "asset-share-commons__predicate-group";
    public static final String REQUEST_ATTR_LEGACY_PREDICATE_GROUP_TRACKER = "DEPRECATED_DO_NOT_USE__asset-share-commons__legacy_predicate-group";

    private static final String REQUEST_ATTR_FORM_ID_TRACKER = "asset-share-commons__form-id";
    private static final String PN_GENERATE_PREDICATE_GROUP_ID = "generatePredicateGroupId";

    @Self
    @Required
    private SlingHttpServletRequest request;

    @ValueMapValue
    @Default(booleanValues = false)
    private boolean expanded;

    @ValueMapValue
    @Default(booleanValues = false)
    private boolean autoSearch;

    @ValueMapValue
    @Named("updateMethod")
    @Default(values = "")
    private String componentUpdateMethod;

    private int group = 1;

    private Field coreField;

    /**
     * Asset Share Predicate Methods
     **/

    public boolean isExpanded() {
        if (!expanded) {
            // Handling incoming request query params
            return StringUtils.isNotBlank(getInitialValue()) || !getInitialValues().isEmpty();
        }

        return expanded;
    }

    public boolean isAutoSearch() {
        return autoSearch;
    }

    public String getGroup() {
        Map<String, Integer> groupTracker = getGroupTracker(this.request);
        String path = this.request.getResource().getPath();

        if (groupTracker.get(path) != null) {
            group = groupTracker.get(path);
        } else {
            group = this.request.getResource().getPath().hashCode();
        }

        return group + "_group";
    }


    public String getInitialValue() {
        return null;
    }

    public ValueMap getInitialValues() {
        return ValueMap.EMPTY;
    }

    public String getId() {
        if (request.getResource() != null && !ResourceUtil.isNonExistingResource(request.getResource())) {
            return "cmp-" + getName() + "_" + String.valueOf(request.getResource().getPath().hashCode());
        } else {
            return "cmp-" + coreField.getId();
        }
    }

    public String getComponentUpdateMethod() {
        return componentUpdateMethod;
    }

    /**
     * Core Field Component Delegates
     **/

    public String getTitle() {
        return coreField.getTitle();
    }

    public String getValue() {
        return coreField.getValue();
    }

    public String getHelpMessage() {
        return coreField.getHelpMessage();
    }

    public String getFormId() {
        if (request.getAttribute(REQUEST_ATTR_FORM_ID_TRACKER) == null) {
            request.setAttribute(REQUEST_ATTR_FORM_ID_TRACKER, 1);
        }

        return REQUEST_ATTR_FORM_ID_TRACKER + "__" + String.valueOf(request.getAttribute(REQUEST_ATTR_FORM_ID_TRACKER));
    }

    /**
     * @return true if the request appears to be a request that has search parameters.
     */
    public boolean isParameterizedSearchRequest() {
        return PredicateUtil.isParameterizedSearchRequest(request);
    }

    /**
     * Initializer Methods.
     **/

    /**
     * Initializes the abstract predicate; This is used to:
     * - Initialize the predicate group number for the Model.
     * - Initialize the Core Components Field Sling Model which the Asset Share Commons Predicates Components delegate to.
     *
     * @param request the current SlingHttpServletRequest object
     * @param coreField the Core Components Field component (if the request can be adapted to one by the concrete implementing class).
     */
    protected final void initPredicate(final SlingHttpServletRequest request, final Field coreField) {
        this.coreField = coreField;
        initGroup(request);
    }

    /**
     * Initializes the predicate group number from the tracking request attribute, and increments for the next Sling Model calling this method.
     *
     * @param request the current SlingHttpServletRequest object.
     */
    @SuppressWarnings("AEM Rules:AEM-15")
    protected synchronized final void initGroup(final SlingHttpServletRequest request) {
        /* Track Predicate Groups across Request */

        /*if (!isGroupIdGeneratingComponent(request) || !isReady() || !generateGroupId(request)) {
            generateLegacyGroupId(request);
        }*/

        if (isGroupIdGeneratingComponent(request)) {
            generateGroupId(request);
        }
    }

    /**
     * @param request the Sling Http Request object.
     * @return true if the component is marked as generating a predicate group Id.
     */
    private boolean isGroupIdGeneratingComponent(SlingHttpServletRequest request) {
        final com.day.cq.wcm.api.components.Component component = WCMUtils.getComponent(request.getResource());
        return component != null && component.getProperties().get(PN_GENERATE_PREDICATE_GROUP_ID, false);
    }

    /**
     * Set the groupId and set the request attribute.
     *
     * @param request the Sling Http Request object.
     * @return true if a group id was generated.
     */
    private boolean generateGroupId(SlingHttpServletRequest request) {
        HashMap<String, Integer> groupTracker = getGroupTracker(request);

        if (groupTracker.containsKey(request.getResource().getPath())) {
            this.group = groupTracker.getOrDefault(request.getResource().getPath(), request.getResource().getPath().hashCode());
            // Processed
            return false;
        } else {
            group = groupTracker.values().stream().max(Integer::compare).orElse(-1) + 1;
            groupTracker.put(request.getResource().getPath(), group);
            request.setAttribute(REQUEST_ATTR_PREDICATE_GROUP_TRACKER, groupTracker);
            request.setAttribute("TEST", "I WWAS SET IN GENERATE GROUP ID");
            return true;
        }
    }


    private HashMap<String, Integer> getGroupTracker(SlingHttpServletRequest request) {
        HashMap<String, Integer> groupTracker = (HashMap) request.getAttribute(REQUEST_ATTR_PREDICATE_GROUP_TRACKER);
        if (groupTracker == null) {
            groupTracker = new HashMap<>();
        }

        return groupTracker;
    }

    public class AlphabeticalOptionItems implements Comparator<OptionItem> {
        public int compare(OptionItem a, OptionItem b)  {
            return a.getText().compareTo(b.getText());
        }
    }
}
