/*
 * Copyright (C) 2015-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package jdocs.stream.javadsl.cookbook;

import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.stream.Attributes;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import akka.testkit.DebugFilter;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.ConfigFactory;
import jdocs.stream.SilenceSystemOut;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import scala.runtime.AbstractFunction0;

import java.util.Arrays;

public class RecipeLoggingElements extends RecipeTest {
  static ActorSystem system;

  @BeforeClass
  public static void setup() {
    system =
        ActorSystem.create(
            "RecipeLoggingElements",
            ConfigFactory.parseString(
                "akka.loglevel=DEBUG\nakka.loggers = [akka.testkit.TestEventListener]"));
  }

  @AfterClass
  public static void tearDown() {
    TestKit.shutdownActorSystem(system);
    system = null;
  }

  @Test
  public void workWithPrintln() throws Exception {
    new TestKit(system) {
      final SilenceSystemOut.System System = SilenceSystemOut.get(getTestActor());

      {
        final Source<String, NotUsed> mySource = Source.from(Arrays.asList("1", "2", "3"));

        // #println-debug
        mySource.map(
            elem -> {
              System.out.println(elem);
              return elem;
            });
        // #println-debug
      }
    };
  }

  @Test
  public void workWithLog() throws Exception {
    new TestKit(system) {
      private <T> T analyse(T i) {
        return i;
      }

      {
        final Source<String, NotUsed> mySource = Source.from(Arrays.asList("1", "2", "3"));

        // #log-custom
        // customise log levels
        mySource
            .log("before-map")
            .withAttributes(
                Attributes.createLogLevels(
                    Logging.WarningLevel(), // onElement
                    Logging.InfoLevel(), // onFinish
                    Logging.DebugLevel() // onFailure
                    ))
            .map(i -> analyse(i));

        // or provide custom logging adapter
        final LoggingAdapter adapter = Logging.getLogger(system, "customLogger");
        mySource.log("custom", adapter);
        // #log-custom

        new DebugFilter("customLogger", "[custom] Element: ", false, false, 3)
            .intercept(
                new AbstractFunction0<Object>() {
                  public Void apply() {
                    mySource.log("custom", adapter).runWith(Sink.ignore(), system);
                    return null;
                  }
                },
                system);
      }
    };
  }

  @Test
  public void errorLog() throws Exception {
    new TestKit(system) {
      {
        // #log-error
        Source.from(Arrays.asList(-1, 0, 1))
            .map(x -> 1 / x) // throwing ArithmeticException: / by zero
            .log("error logging")
            .runWith(Sink.ignore(), system);
        // #log-error
      }
    };
  }
}
