/*
 * 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 org.apache.pekko.persistence

import scala.language.implicitConversions
import scala.jdk.CollectionConverters._
import scala.util.Try

import org.apache.pekko
import pekko.annotation.DoNotInherit

/** Not for user extension */
@DoNotInherit sealed abstract class CapabilityFlag {
  private val capturedStack: String =
    StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk { stream =>
      stream.iterator().asScala
        .filter(_.getMethodName.startsWith("supports"))
        .find { frame =>
          Try(frame.getDeclaringClass.getDeclaredMethod(frame.getMethodName).getReturnType)
            .toOption
            .contains(classOf[CapabilityFlag])
        }
        .map(_.getMethodName)
    }.getOrElse("[unknown]")

  def name: String = capturedStack
  def value: Boolean
}
object CapabilityFlag {
  def on(): CapabilityFlag =
    new CapabilityFlag { override def value = true }
  def off(): CapabilityFlag =
    new CapabilityFlag { override def value = false }

  /** Java DSL */
  def create(`val`: Boolean): CapabilityFlag =
    new CapabilityFlag { override def value = `val` }

  // conversions

  implicit def mkFlag(v: Boolean): CapabilityFlag =
    new CapabilityFlag { override def value = v }
}

sealed trait CapabilityFlags

//#journal-flags
trait JournalCapabilityFlags extends CapabilityFlags {

  /**
   * When `true` enables tests which check if the Journal properly rejects
   * writes of objects which are not `java.lang.Serializable`.
   */
  protected def supportsRejectingNonSerializableObjects: CapabilityFlag

  /**
   * When `true` enables tests which check if the Journal properly serialize and
   * deserialize events.
   */
  protected def supportsSerialization: CapabilityFlag

  /**
   * When `true` enables tests which check if the Journal stores and returns
   * metadata for an event
   */
  protected def supportsMetadata: CapabilityFlag

  /**
   * When `true` enables tests which check if the Journal supports a replay window that
   * spans a deleted prefix.
   * <p>
   *   Relates to https://github.com/apache/pekko-persistence-jdbc/pull/517.
   *   pekko-persistence-dynamodb doesn't yet fully support this.
   * </p>
   */
  protected def supportsReplayWindowSpanningDeletedPrefix: CapabilityFlag

}
//#journal-flags

//#snapshot-store-flags
trait SnapshotStoreCapabilityFlags extends CapabilityFlags {

  /**
   * When `true` enables tests which check if the snapshot store properly serialize and
   * deserialize snapshots.
   */
  protected def supportsSerialization: CapabilityFlag

  /**
   * When `true` enables tests which check if the snapshot store properly stores and
   * loads metadata (needed for replication) along with the snapshots
   */
  protected def supportsMetadata: CapabilityFlag
}
//#snapshot-store-flags

//#durable-state-store-flags
trait DurableStateStoreCapabilityFlags extends CapabilityFlags {

  /**
   * When `true` enables tests which check if the durable state store properly rejects
   * a `deleteObject` call when the revision does not match the stored revision.
   */
  protected def supportsDeleteWithRevisionCheck: CapabilityFlag

  /**
   * When `true` enables tests which check if the durable state store properly rejects
   * an `upsertObject` call when the revision is stale (does not match the expected next revision).
   * This is the optimistic concurrency check on writes.
   */
  protected def supportsUpsertWithRevisionCheck: CapabilityFlag

  /**
   * When `true` enables tests which check if the durable state store properly serializes
   * and deserializes values via the configured Pekko serializer infrastructure.
   */
  protected def supportsSerialization: CapabilityFlag

  /**
   * When `true` enables tests which check the deprecated single-argument `deleteObject(persistenceId)`
   * overload, which deletes regardless of the current revision.
   */
  protected def supportsSoftDelete: CapabilityFlag
}
//#durable-state-store-flags
