/*
 * 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) since 2016 Lightbend Inc. <https://www.lightbend.com>
 */

package org.apache.pekko.stream.connectors.pravega

import org.apache.pekko
import pekko.stream.KillSwitches

import pekko.stream.connectors.pravega.scaladsl.Pravega
import pekko.stream.connectors.testkit.scaladsl.Repeated
import pekko.stream.scaladsl.{ Keep, Sink, Source }
import com.typesafe.config.ConfigFactory
import io.pravega.client.stream.impl.UTF8StringSerializer

import scala.concurrent.duration.DurationInt
import scala.concurrent.{ Await, Promise }
import scala.util.Using

class PravegaGraphSpec extends PravegaBaseSpec with Repeated {

  val serializer = new UTF8StringSerializer
  val nEvent = 1000
  val timeout = 10.seconds

  "Pravega connector" should {

    "runs sources" in {

      val group = newGroupName()
      val scope = newScope()

      val stream1 = "scala-test-stream1"
      val stream2 = "scala-test-stream2"

      createStream(scope, stream1)
      createStream(scope, stream2)

      val readerSettings = ReaderSettingsBuilder(
        system.settings.config
          .getConfig(ReaderSettingsBuilder.configPath)
          .withFallback(ConfigFactory.parseString(s"group-name = ${newGroupName()}"))).withSerializer(serializer)

      val writerSettings = WriterSettingsBuilder(system)
        .withSerializer(serializer)

      val writerSettingsWithRoutingKey = WriterSettingsBuilder(system)
        .withKeyExtractor((str: String) => str.substring(0, 2))
        .withSerializer(serializer)

      logger.info(s"Write $nEvent events")

      // #writing
      val done = time(s"Write $nEvent events",
        Source(1 to nEvent)
          .map(i => f"$i%02d_event")
          .runWith(Pravega.sink(scope, stream1, writerSettings)))

      time("Wait write", Await.ready(done, timeout))

      Source(1 to nEvent)
        .map(i => f"$i%02d_event")
        .runWith(Pravega.sink(scope, stream2, writerSettings))

      Thread.sleep(3000)

      Source(1 to nEvent)
        .map(i => f"$i%02d_event")
        .runWith(Pravega.sink(scope, stream1, writerSettingsWithRoutingKey))

      // #writing

      val finishReading = Promise[Unit]()

      // #reading

      Using(Pravega.readerGroupManager(scope, readerSettings.clientConfig)) { readerGroupManager =>
        readerGroupManager.createReaderGroup(group, stream1, stream2)
      }.foreach { readerGroup =>
        val (kill, fut) = Pravega
          .source(readerGroup, readerSettings)
          .viaMat(KillSwitches.single)(Keep.right)
          .toMat(Sink.fold(nEvent * 3) { (acc, _) =>
            if (acc == 1)
              finishReading.success(())
            acc - 1
          })(Keep.both)
          .run()

        // #reading

        Await.ready(finishReading.future, timeout)

        logger.debug("Die, die by my hand.")
        kill.shutdown()

        whenReady(fut) { r =>
          r mustEqual 0
          logger.info(s"Read $nEvent events.")
        }
      }

    }

  }

}
