/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * 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 CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.datastax.oss.driver.internal.mapper.processor.entity;

import com.datastax.oss.driver.api.mapper.annotations.Entity;
import com.datastax.oss.driver.api.mapper.annotations.NamingStrategy;
import com.datastax.oss.driver.api.mapper.entity.naming.NameConverter;
import com.datastax.oss.driver.api.mapper.entity.naming.NamingConvention;
import com.datastax.oss.driver.internal.mapper.processor.MapperProcessorTest;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import javax.lang.model.element.Modifier;
import org.junit.Test;

public class EntityNamingStrategyTest extends MapperProcessorTest {

  private static final ClassName CUSTOM_CONVERTER_CLASS_NAME =
      ClassName.get("test", "CustomConverter");

  private static final TypeSpec CUSTOM_CONVERTER_CLASS =
      TypeSpec.classBuilder(CUSTOM_CONVERTER_CLASS_NAME)
          .addSuperinterface(NameConverter.class)
          .addMethod(
              MethodSpec.methodBuilder("toCassandraName")
                  .addModifiers(Modifier.PUBLIC)
                  .returns(String.class)
                  .addParameter(String.class, "javaName")
                  .addStatement("return null;") // doesn't matter, converter won't be invoked
                  .build())
          .build();

  // Common code for the entity class, to avoid repeating it in every test
  private TypeSpec.Builder entityTemplate() {
    return TypeSpec.classBuilder("TestEntity")
        .addModifiers(Modifier.PUBLIC)
        .addAnnotation(Entity.class)
        .addField(TypeName.INT, "i", Modifier.PRIVATE)
        .addMethod(
            MethodSpec.methodBuilder("setI")
                .addParameter(TypeName.INT, "i")
                .addModifiers(Modifier.PUBLIC)
                .build())
        .addMethod(
            MethodSpec.methodBuilder("getI")
                .returns(TypeName.INT)
                .addModifiers(Modifier.PUBLIC)
                .addStatement("return 0")
                .build());
  }

  @Test
  public void should_fail_if_both_convention_and_converter_specified() {
    should_fail_with_expected_error(
        "Invalid annotation configuration: "
            + "NamingStrategy must have either a 'convention' or 'customConverterClass' argument, "
            + "but not both",
        "test",
        CUSTOM_CONVERTER_CLASS,
        entityTemplate()
            .addAnnotation(
                AnnotationSpec.builder(NamingStrategy.class)
                    .addMember("convention", "$T.CASE_INSENSITIVE", NamingConvention.class)
                    .addMember("customConverterClass", "$T.class", CUSTOM_CONVERTER_CLASS_NAME)
                    .build())
            .build());
  }

  @Test
  public void should_fail_if_neither_convention_nor_converter_specified() {
    should_fail_with_expected_error(
        "Invalid annotation configuration: "
            + "NamingStrategy must have either a 'convention' or 'customConverterClass' argument",
        "test",
        CUSTOM_CONVERTER_CLASS,
        entityTemplate().addAnnotation(NamingStrategy.class).build());
  }

  @Test
  public void should_warn_if_multiple_conventions_specified() {
    should_succeed_with_expected_warning(
        "Too many naming conventions: "
            + "NamingStrategy must have at most one 'convention' argument "
            + "(will use the first one: CASE_INSENSITIVE)",
        "test",
        CUSTOM_CONVERTER_CLASS,
        entityTemplate()
            .addAnnotation(
                AnnotationSpec.builder(NamingStrategy.class)
                    .addMember(
                        "convention",
                        "{ $1T.CASE_INSENSITIVE, $1T.EXACT_CASE }",
                        NamingConvention.class)
                    .build())
            .build());
  }

  @Test
  public void should_warn_if_multiple_converters_specified() {
    should_succeed_with_expected_warning(
        "Too many custom converters: "
            + "NamingStrategy must have at most one 'customConverterClass' argument "
            + "(will use the first one: test.CustomConverter)",
        "test",
        CUSTOM_CONVERTER_CLASS,
        entityTemplate()
            .addAnnotation(
                AnnotationSpec.builder(NamingStrategy.class)
                    .addMember(
                        "customConverterClass",
                        "{ $1T.class, $1T.class }",
                        CUSTOM_CONVERTER_CLASS_NAME)
                    .build())
            .build());
  }
}
