package com.google.android.apps.common.testing.accessibility.framework.suggestions;

import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult;
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType;
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult;
import com.google.android.apps.common.testing.accessibility.framework.Parameters;
import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchy;
import com.google.common.collect.ImmutableList;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Base class which creates and sorts a list of {@link FixSuggestion} for a given {@link
 * AccessibilityHierarchyCheckResult}.
 *
 * @param <T> the type of {@link AccessibilityHierarchyCheck}. This {@link FixSuggestionsProvider}
 *     only provides fix suggestions to a {@link AccessibilityHierarchyCheckResult} generated by
 *     this specific type of {@link AccessibilityHierarchyCheck}.
 */
abstract class BaseFixSuggestionsProvider<T extends AccessibilityHierarchyCheck>
    implements FixSuggestionsProvider {

  @Override
  public final ImmutableList<FixSuggestion> provideFixSuggestions(
      AccessibilityHierarchyCheckResult checkResult,
      AccessibilityHierarchy hierarchy,
      @Nullable Parameters parameters) {
    if ((checkResult.getType() == AccessibilityCheckResultType.NOT_RUN)
        || (checkResult.getType() == AccessibilityCheckResultType.SUPPRESSED)) {
      return ImmutableList.of();
    }

    // Only provides fix suggestions to a {@link AccessibilityHierarchyCheckResult} generated by a
    // specific type of {@link AccessibilityHierarchyCheck}
    if (!Objects.equals(checkResult.getSourceCheckClass(), getTargetCheckClass())) {
      return ImmutableList.of();
    }

    ImmutableList.Builder<FixSuggestion> fixSuggestionBuilder = new ImmutableList.Builder<>();
    for (FixSuggestionProducer<? extends FixSuggestion> suggestionProducer :
        getFixSuggestionProducers()) {
      FixSuggestion suggestion =
          suggestionProducer.produceFixSuggestion(checkResult, hierarchy, parameters);
      if (suggestion != null) {
        fixSuggestionBuilder.add(suggestion);
      }
    }
    return sortFixSuggestions(fixSuggestionBuilder.build(), checkResult, hierarchy, parameters);
  }

  /** Returns an immutable list of {@link FixSuggestionProducer} which produce fix suggestions. */
  protected abstract ImmutableList<FixSuggestionProducer<? extends FixSuggestion>>
      getFixSuggestionProducers();

  /**
   * Sorts the given list of {@link FixSuggestion} according to which fix suggestion works better
   * for the given {@link AccessibilityCheckResult}.
   *
   * <p>Returns the given fix suggestions without sorting by default. Override this method to
   * provide dynamic sorting for the given fix suggestions.
   *
   * @param checkResult the {@link AccessibilityHierarchyCheckResult} which needs fix suggestions
   * @param hierarchy The hierarchy which contains the culprit {@link ViewHierarchyElement}
   * @param parameters Optional input data or preferences
   * @return suggested fixes (possibly none) sorted so that the better suggestions appear first
   */
  protected ImmutableList<FixSuggestion> sortFixSuggestions(
      ImmutableList<FixSuggestion> fixSuggestions,
      AccessibilityHierarchyCheckResult checkResult,
      AccessibilityHierarchy hierarchy,
      @Nullable Parameters parameters) {
    return fixSuggestions;
  }

  /**
   * Returns the {@link Class} of the specific type of {@link AccessibilityHierarchyCheck}.
   *
   * <p>This {@link FixSuggestionsProvider} only provides fix suggestions to a {@link
   * AccessibilityHierarchyCheckResult} generated by the specific type of {@link
   * AccessibilityHierarchyCheck}.
   */
  protected abstract Class<T> getTargetCheckClass();
}
