/*
 * Copyright 2018 Google Inc.
 *
 * 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.google.template.soy.passes;

import com.google.template.soy.base.internal.IdGenerator;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.logging.ValidatedLoggingConfig;
import com.google.template.soy.shared.SoyGeneralOptions;
import com.google.template.soy.soytree.AliasDeclaration;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.types.SoyProtoEnumType;
import com.google.template.soy.types.SoyProtoType;
import com.google.template.soy.types.SoyType;
import com.google.template.soy.types.SoyTypeRegistry;

/**
 * Checks that aliases don't conflict with things that can be aliased (or their namespace prefixes).
 */
final class ValidateAliasesPass implements CompilerFilePass {
  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_GLOBAL =
      SoyErrorKind.of("Alias ''{0}'' conflicts with a global of the same name.");
  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_GLOBAL_PREFIX =
      SoyErrorKind.of("Alias ''{0}'' conflicts with namespace for global ''{1}''.");

  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_TYPE_NAME =
      SoyErrorKind.of("Alias ''{0}'' conflicts with a type of the same name.");
  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_TYPE_PREFIX =
      SoyErrorKind.of("Alias ''{0}'' conflicts with namespace for type ''{1}''.");

  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_VE =
      SoyErrorKind.of("Alias ''{0}'' conflicts with a VE of the same name.");

  private final ErrorReporter errorReporter;
  private final SoyGeneralOptions options;
  private final ValidatedLoggingConfig loggingConfig;

  ValidateAliasesPass(
      ErrorReporter errorReporter,
      SoyGeneralOptions options,
      ValidatedLoggingConfig loggingConfig) {
    this.errorReporter = errorReporter;
    this.options = options;
    this.loggingConfig = loggingConfig;
  }

  @Override
  public void run(SoyFileNode file, IdGenerator nodeIdGen) {
    SoyTypeRegistry registry = file.getSoyTypeRegistry();
    for (AliasDeclaration alias : file.getAliasDeclarations()) {
      if (options.getCompileTimeGlobals().containsKey(alias.alias().identifier())) {
        errorReporter.report(alias.alias().location(), ALIAS_CONFLICTS_WITH_GLOBAL, alias.alias());
      }
      SoyType type = registry.getType(alias.alias().identifier());
      // When running with a dummy type provider that parses all types as unknown, ignore that.
      if (type != null && type.getKind() != SoyType.Kind.UNKNOWN) {
        // Temporarily, while we migrate all proto aliases to imports, ignore aliases that are
        // identical to proto imports. They don't conflict and Tricorder will remove the aliases.
        if (!alias.namespace().identifier().equals(getFqProtoName(type))) {
          errorReporter.report(
              alias.alias().location(), ALIAS_CONFLICTS_WITH_TYPE_NAME, alias.alias());
        }
      }
      String conflictingNamespacedType =
          registry.findTypeWithMatchingNamespace(alias.alias().identifier());
      if (conflictingNamespacedType != null) {
        errorReporter.report(
            alias.alias().location(),
            ALIAS_CONFLICTS_WITH_TYPE_PREFIX,
            alias.alias(),
            conflictingNamespacedType);
      }
      String prefix = alias.alias().identifier() + ".";
      for (String global : options.getCompileTimeGlobals().keySet()) {
        if (global.startsWith(prefix)) {
          errorReporter.report(
              alias.alias().location(), ALIAS_CONFLICTS_WITH_GLOBAL_PREFIX, alias.alias(), global);
        }
      }
      if (loggingConfig.getElement(alias.alias().identifier()) != null) {
        errorReporter.report(alias.alias().location(), ALIAS_CONFLICTS_WITH_VE, alias.alias());
      }
    }
  }

  private static String getFqProtoName(SoyType type) {
    if (type instanceof SoyProtoType) {
      return ((SoyProtoType) type).getDescriptor().getFullName();
    } else if (type instanceof SoyProtoEnumType) {
      return ((SoyProtoEnumType) type).getDescriptor().getFullName();
    } else {
      return null;
    }
  }
}

@@ -1020,16 +1020,86 @@
 leNode;%0A
+import com.google.template.soy.soytree.TemplatesPerFile.TemplateName;%0A
 import c
@@ -1141,24 +1141,24 @@
 toEnumType;%0A
-
 import com.g
@@ -1295,16 +1295,38 @@
 egistry;
+%0Aimport java.util.Map;
 %0A%0A/**%0A *
@@ -2238,24 +2238,185 @@
 e name.%22);%0A%0A
+  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_AN_IMPORT_SYMBOL =%0A      SoyErrorKind.of(%22Alias ''%7B0%7D'' conflicts with an imported template symbol.%22);%0A%0A
   private fi
@@ -3181,24 +3181,25 @@
 ));%0A      %7D%0A
+%0A
       SoyTyp
@@ -3802,24 +3802,24 @@
 s.alias());%0A
-
         %7D%0A  
@@ -3815,32 +3815,800 @@
        %7D%0A      %7D
+%0A%0A      Map%3CString, TemplateName%3E tmplImports =%0A          file.getTemplateRegistry().getSymbolsToTemplateNamesMap();%0A      // Temporarily, while we migrate all template aliases to imports, ignore aliases that are%0A      // identical to template imports. They don't conflict and Tricorder will remove the aliases.%0A      // If two different paths are aliased to the same symbol, though, we should throw an error.%0A      if (tmplImports.containsKey(alias.alias().identifier())%0A          && !alias%0A              .namespace()%0A              .identifier()%0A              .equals(tmplImports.get(alias.alias().identifier()).fullyQualifiedName())) %7B%0A        errorReporter.report(%0A            alias.alias().location(), ALIAS_CONFLICTS_WITH_AN_IMPORT_SYMBOL, alias.alias());%0A      %7D
 %0A      String co

Make it an error for aliases to conflict with template imports, if they point to different things []