/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * license agreements; and to You under the Apache License, version 2.0:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * This file is part of the Apache Pekko project, which was derived from Akka.
 */

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

package jdocs.org.apache.pekko.typed;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import org.apache.pekko.actor.typed.*;
import org.apache.pekko.actor.typed.javadsl.AbstractBehavior;
import org.apache.pekko.actor.typed.javadsl.ActorContext;
import org.apache.pekko.actor.typed.javadsl.Behaviors;
import org.apache.pekko.actor.typed.javadsl.Receive;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// #logMessages
import org.slf4j.event.Level;

// #logMessages

// #test-logging
import org.apache.pekko.actor.testkit.typed.javadsl.LoggingTestKit;

// #test-logging

public interface LoggingDocExamples {

  // #context-log
  public class MyLoggingBehavior extends AbstractBehavior<String> {

    public static Behavior<String> create() {
      return Behaviors.setup(MyLoggingBehavior::new);
    }

    private MyLoggingBehavior(ActorContext<String> context) {
      super(context);
    }

    @Override
    public Receive<String> createReceive() {
      return newReceiveBuilder().onMessage(String.class, this::onReceive).build();
    }

    private Behavior<String> onReceive(String message) {
      getContext().getLog().info("Received message: {}", message);
      return this;
    }
  }
  // #context-log

  // #logger-name
  public class BackendManager extends AbstractBehavior<String> {

    public static Behavior<String> create() {
      return Behaviors.setup(
          context -> {
            context.setLoggerName(BackendManager.class);
            context.getLog().info("Starting up");
            return new BackendManager(context);
          });
    }

    private BackendManager(ActorContext<String> context) {
      super(context);
    }

    @Override
    public Receive<String> createReceive() {
      return newReceiveBuilder().onMessage(String.class, this::onReceive).build();
    }

    private Behavior<String> onReceive(String message) {
      getContext().getLog().debug("Received message: {}", message);
      return this;
    }
  }
  // #logger-name

  // #logger-factory
  class BackendTask {
    private final Logger log = LoggerFactory.getLogger(getClass());

    void run() {
      CompletableFuture<String> task =
          CompletableFuture.supplyAsync(
              () -> {
                // some work
                return "result";
              });
      task.whenComplete(
          (result, exc) -> {
            if (exc == null) log.error("Task failed", exc);
            else log.info("Task completed: {}", result);
          });
    }
  }
  // #logger-factory

  static void logMessages() {
    // #logMessages
    Behaviors.logMessages(LogOptions.create().withLevel(Level.TRACE), BackendManager.create());
    // #logMessages
  }

  public class BackendManager2 extends AbstractBehavior<BackendManager2.Command> {

    interface Command {
      String identifier();
    }

    public static Behavior<Command> create() {
      return Behaviors.empty();
    }

    public BackendManager2(ActorContext<Command> context) {
      super(context);
    }

    @Override
    public Receive<Command> createReceive() {
      return newReceiveBuilder().build();
    }
  }

  static void withMdc() {
    ActorSystem<Void> system = ActorSystem.create(Behaviors.empty(), "notUsed");

    // #withMdc
    Map<String, String> staticMdc = new HashMap<>();
    staticMdc.put("startTime", String.valueOf(system.startTime()));

    Behaviors.withMdc(
        BackendManager2.Command.class,
        staticMdc,
        message -> {
          Map<String, String> msgMdc = new HashMap<>();
          msgMdc.put("identifier", message.identifier());
          msgMdc.put("upTime", String.valueOf(system.uptime()));
          return msgMdc;
        },
        BackendManager2.create());
    // #withMdc
  }

  static class Message {
    final String s;

    public Message(String s) {
      this.s = s;
    }
  }

  static void logging() {
    ActorSystem<Void> system = ActorSystem.create(Behaviors.empty(), "notUsed");
    ActorRef<Message> ref = null;

    // #test-logging
    LoggingTestKit.info("Received message")
        .expect(
            system,
            () -> {
              ref.tell(new Message("hello"));
              return null;
            });
    // #test-logging

    // #test-logging-criteria
    LoggingTestKit.error(IllegalArgumentException.class)
        .withMessageRegex(".*was rejected.*expecting ascii input.*")
        .withCustom(
            event ->
                event.getMarker().isPresent()
                    && event.getMarker().get().getName().equals("validation"))
        .withOccurrences(2)
        .expect(
            system,
            () -> {
              ref.tell(new Message("hellö"));
              ref.tell(new Message("hejdå"));
              return null;
            });
    // #test-logging-criteria

  }

  static void tagsExample() {
    ActorContext<Object> context = null;
    Behavior<Object> myBehavior = Behaviors.empty();
    // #tags
    context.spawn(myBehavior, "MyActor", ActorTags.create("processing"));
    // #tags
  }
}
