/*
 * Copyright 2019 The Error Prone Authors.
 *
 * 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.errorprone.bugpatterns;

import com.google.errorprone.BugCheckerRefactoringTestHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Unit tests for {@link UnnecessaryOptionalGet}. */
@RunWith(JUnit4.class)
public final class UnnecessaryOptionalGetTest {
  private final BugCheckerRefactoringTestHelper refactoringTestHelper =
      BugCheckerRefactoringTestHelper.newInstance(UnnecessaryOptionalGet.class, getClass());

  @Test
  public void genericOptionalVars_sameVarGet_replacesWithLambdaArg() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.ifPresent(x -> System.out.println(op.get()));
                op.map(x -> Long.parseLong(op.get()));
                op.filter(x -> op.get().isEmpty());
                op.flatMap(x -> Optional.of(op.get()));
                op.flatMap(x -> Optional.of(op.orElseThrow()));
              }
            }
            """)
        .addOutputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.ifPresent(x -> System.out.println(x));
                op.map(x -> Long.parseLong(x));
                op.filter(x -> x.isEmpty());
                op.flatMap(x -> Optional.of(x));
                op.flatMap(x -> Optional.of(x));
              }
            }
            """)
        .doTest();
  }

  @Test
  public void guava_sameVarGet_replacesWithLambdaArg() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import com.google.common.base.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.transform(x -> Long.parseLong(op.get()));
              }
            }
            """)
        .addOutputLines(
            "Test.java",
            """
            import com.google.common.base.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.transform(x -> Long.parseLong(x));
              }
            }
            """)
        .doTest();
  }

  @Test
  public void genericOptionalVars_orElseVariations_replacesWithLambdaArg() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.ifPresent(x -> System.out.println(op.orElse("other")));
                op.ifPresent(x -> System.out.println(op.orElseGet(() -> "other")));
                op.ifPresent(x -> System.out.println(op.orElseThrow(RuntimeException::new)));
              }
            }
            """)
        .addOutputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.ifPresent(x -> System.out.println(x));
                op.ifPresent(x -> System.out.println(x));
                op.ifPresent(x -> System.out.println(x));
              }
            }
            """)
        .doTest();
  }

  @Test
  public void guava_orVariations_replacesWithLambdaArg() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import com.google.common.base.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.transform(x -> Long.parseLong(op.or("other")));
                op.transform(x -> Long.parseLong(op.or(() -> "other")));
              }
            }
            """)
        .addOutputLines(
            "Test.java",
            """
            import com.google.common.base.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.transform(x -> Long.parseLong(x));
                op.transform(x -> Long.parseLong(x));
              }
            }
            """)
        .doTest();
  }

  @Test
  public void genericOptionalVars_sameVarGet_lamdaBlocks_replacesWithLamdaArg() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.ifPresent(
                    x -> {
                      System.out.println(op.get());
                    });
                op.map(
                    x -> {
                      return Long.parseLong(op.get());
                    });
                op.filter(
                    x -> {
                      return op.get().isEmpty();
                    });
                op.flatMap(
                    x -> {
                      return Optional.of(op.get());
                    });
              }
            }
            """)
        .addOutputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.ifPresent(
                    x -> {
                      System.out.println(x);
                    });
                op.map(
                    x -> {
                      return Long.parseLong(x);
                    });
                op.filter(
                    x -> {
                      return x.isEmpty();
                    });
                op.flatMap(
                    x -> {
                      return Optional.of(x);
                    });
              }
            }
            """)
        .doTest();
  }

  @Test
  public void genericOptionalVars_differentOptionalVarGet_doesNothing() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op1 = Optional.of("hello");
                Optional<String> op2 = Optional.of("hello");
                op1.ifPresent(x -> System.out.println(op2.get()));
              }
            }
            """)
        .expectUnchanged()
        .doTest();
  }

  @Test
  public void genericOptionalVars_differentMethodGet_doesNothing() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                myOpFunc1().ifPresent(x -> System.out.println(myOpFunc2().get()));
              }

              private Optional<String> myOpFunc1() {
                return Optional.of("hello");
              }

              private Optional<String> myOpFunc2() {
                return Optional.of("hello");
              }
            }
            """)
        .expectUnchanged()
        .doTest();
  }

  @Test
  public void genericOptionalMethods_sameMethodInvocation_replacesWithLamdaArg() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                myOpFunc().ifPresent(x -> System.out.println(myOpFunc().get()));
              }

              private Optional<String> myOpFunc() {
                return Optional.of("hello");
              }
            }
            """)
        .expectUnchanged()
        .doTest();
  }

  @Test
  public void primitiveOptionals() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.OptionalDouble;
            import java.util.OptionalInt;
            import java.util.OptionalLong;

            public class Test {
              private void home() {
                OptionalDouble opDouble = OptionalDouble.of(1.0);
                OptionalInt opInt = OptionalInt.of(1);
                OptionalLong opLong = OptionalLong.of(1L);
                opDouble.ifPresent(x -> System.out.println(opDouble.getAsDouble()));
                opInt.ifPresent(x -> System.out.println(opInt.getAsInt()));
                opLong.ifPresent(x -> System.out.println(opLong.getAsLong()));
              }
            }
            """)
        .addOutputLines(
            "Test.java",
            """
            import java.util.OptionalDouble;
            import java.util.OptionalInt;
            import java.util.OptionalLong;

            public class Test {
              private void home() {
                OptionalDouble opDouble = OptionalDouble.of(1.0);
                OptionalInt opInt = OptionalInt.of(1);
                OptionalLong opLong = OptionalLong.of(1L);
                opDouble.ifPresent(x -> System.out.println(x));
                opInt.ifPresent(x -> System.out.println(x));
                opLong.ifPresent(x -> System.out.println(x));
              }
            }
            """)
        .doTest();
  }

  @Test
  public void differentReceivers() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              abstract static class T {
                abstract Optional<String> getValue();
              }

              static void test(T actual, T expected) {
                actual
                    .getValue()
                    .ifPresent(
                        actualValue -> {
                          String expectedValue = expected.getValue().get();
                          actualValue.equals(expectedValue);
                        });
              }
            }
            """)
        .expectUnchanged()
        .doTest();
  }

  @Test
  public void orElseThrow() {
    refactoringTestHelper
        .addInputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.flatMap(x -> Optional.of(op.orElseThrow()));
              }
            }
            """)
        .addOutputLines(
            "Test.java",
            """
            import java.util.Optional;

            public class Test {
              private void home() {
                Optional<String> op = Optional.of("hello");
                op.flatMap(x -> Optional.of(x));
              }
            }
            """)
        .doTest();
  }
}
