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

package akka.actor.typed;

import akka.actor.setup.ActorSystemSetup;
import akka.actor.typed.javadsl.Behaviors;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.junit.Test;
import org.scalatestplus.junit.JUnitSuite;

import java.util.function.Function;

import static junit.framework.TestCase.assertSame;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class ExtensionsTest extends JUnitSuite {

  public static class MyExtImpl implements Extension {}

  public static class MyExtension extends ExtensionId<MyExtImpl> {

    private static final MyExtension instance = new MyExtension();

    private MyExtension() {}

    public static MyExtension getInstance() {
      return instance;
    }

    public MyExtImpl createExtension(ActorSystem<?> system) {
      return new MyExtImpl();
    }

    public static MyExtImpl get(ActorSystem<?> system) {
      return instance.apply(system);
    }
  }

  public static class MyExtImplViaSetup extends MyExtImpl {}

  public static class MyExtensionSetup extends ExtensionSetup<MyExtImpl> {
    public MyExtensionSetup(Function<ActorSystem<?>, MyExtImpl> createExtension) {
      super(MyExtension.getInstance(), createExtension);
    }
  }

  @Test
  public void loadJavaExtensionsFromConfig() {
    Config cfg =
        ConfigFactory.parseString(
                "akka.actor.typed.extensions += \"akka.actor.typed.ExtensionsTest$MyExtension\"")
            .resolve();
    final ActorSystem<Object> system =
        ActorSystem.create(Behaviors.empty(), "loadJavaExtensionsFromConfig", cfg);

    try {
      // note that this is not the intended end user way to access it
      assertTrue(system.hasExtension(MyExtension.getInstance()));

      MyExtImpl instance1 = MyExtension.get(system);
      MyExtImpl instance2 = MyExtension.get(system);

      assertSame(instance1, instance2);
    } finally {
      system.terminate();
    }
  }

  @Test
  public void loadScalaExtension() {
    final ActorSystem<Object> system = ActorSystem.create(Behaviors.empty(), "loadScalaExtension");
    try {
      DummyExtension1 instance1 = DummyExtension1.get(system);
      DummyExtension1 instance2 = DummyExtension1.get(system);

      assertSame(instance1, instance2);
    } finally {
      system.terminate();
    }
  }

  @Test
  public void overrideExtensionsViaActorSystemSetup() {
    final ActorSystem<Object> system =
        ActorSystem.create(
            Behaviors.empty(),
            "overrideExtensionsViaActorSystemSetup",
            ActorSystemSetup.create(new MyExtensionSetup(sys -> new MyExtImplViaSetup())));

    try {
      MyExtImpl instance1 = MyExtension.get(system);
      assertEquals(MyExtImplViaSetup.class, instance1.getClass());

      MyExtImpl instance2 = MyExtension.get(system);
      assertSame(instance1, instance2);

    } finally {
      system.terminate();
    }
  }
}
