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

package docs.circuitbreaker

//#imports1
import scala.concurrent.duration._
import org.apache.pekko
import pekko.pattern.CircuitBreaker
import pekko.pattern.pipe
import pekko.actor.{ Actor, ActorLogging, ActorRef }

import scala.concurrent.Future
import scala.util.{ Failure, Success, Try }

//#imports1

class CircuitBreakerDocSpec {}

//#circuit-breaker-initialization
class DangerousActor extends Actor with ActorLogging {
  import context.dispatcher

  val breaker =
    new CircuitBreaker(context.system.scheduler, maxFailures = 5, callTimeout = 10.seconds, resetTimeout = 1.minute)
      .onOpen(notifyMeOnOpen())

  def notifyMeOnOpen(): Unit =
    log.warning("My CircuitBreaker is now open, and will not close for one minute")
  // #circuit-breaker-initialization

  // #circuit-breaker-usage
  def dangerousCall: String = "This really isn't that dangerous of a call after all"

  def receive = {
    case "is my middle name" =>
      breaker.withCircuitBreaker(Future(dangerousCall)).pipeTo(sender())
    case "block for me" =>
      sender() ! breaker.withSyncCircuitBreaker(dangerousCall)
  }
  // #circuit-breaker-usage

}

class TellPatternActor(recipient: ActorRef) extends Actor with ActorLogging {
  import context.dispatcher

  val breaker =
    new CircuitBreaker(context.system.scheduler, maxFailures = 5, callTimeout = 10.seconds, resetTimeout = 1.minute)
      .onOpen(notifyMeOnOpen())

  def notifyMeOnOpen(): Unit =
    log.warning("My CircuitBreaker is now open, and will not close for one minute")

  // #circuit-breaker-tell-pattern
  import org.apache.pekko.actor.ReceiveTimeout

  def receive = {
    case "call" if breaker.isClosed => {
      recipient ! "message"
    }
    case "response" => {
      breaker.succeed()
    }
    case err: Throwable => {
      breaker.fail()
    }
    case ReceiveTimeout => {
      breaker.fail()
    }
  }
  // #circuit-breaker-tell-pattern
}

class EvenNoFailureActor extends Actor {
  import context.dispatcher
  // #even-no-as-failure
  def luckyNumber(): Future[Int] = {
    val evenNumberAsFailure: Try[Int] => Boolean = {
      case Success(n) => n % 2 == 0
      case Failure(_) => true
    }

    val breaker =
      new CircuitBreaker(context.system.scheduler, maxFailures = 5, callTimeout = 10.seconds, resetTimeout = 1.minute)

    // this call will return 8888 and increase failure count at the same time
    breaker.withCircuitBreaker(Future(8888), evenNumberAsFailure)
  }
  // #even-no-as-failure

  override def receive = {
    case x: Int =>
  }
}
