package linea.staterecovery.test

import linea.domain.BlockNumberAndHash
import linea.domain.BlockParameter
import linea.domain.CommonDomainFunctions
import linea.staterecovery.BlockFromL1RecoveredData
import linea.staterecovery.ExecutionLayerClient
import linea.staterecovery.StateRecoveryStatus
import org.apache.logging.log4j.LogManager
import tech.pegasys.teku.infrastructure.async.SafeFuture

class FakeExecutionLayerClient(
  headBlock: BlockNumberAndHash = BlockNumberAndHash(number = 0uL, hash = ByteArray(32) { 0 }),
  initialStateRecoverStartBlockNumber: ULong? = null,
  loggerName: String? = null,
) : ExecutionLayerClient {
  private val log = loggerName
    ?.let { LogManager.getLogger(loggerName) }
    ?: LogManager.getLogger(FakeExecutionLayerClient::class.java)

  private val _importedBlocksInRecoveryMode = mutableListOf<BlockFromL1RecoveredData>()

  val importedBlocksInRecoveryMode: List<BlockFromL1RecoveredData>
    get() = _importedBlocksInRecoveryMode.toList()
  val importedBlockNumbersInRecoveryMode: List<ULong>
    get() = _importedBlocksInRecoveryMode.map { it.header.blockNumber }

  @get:Synchronized @set:Synchronized
  var headBlock: BlockNumberAndHash = headBlock

  @get:Synchronized @set:Synchronized
  var stateRecoverStartBlockNumber = initialStateRecoverStartBlockNumber

  @get:Synchronized
  val stateRecoverStatus: StateRecoveryStatus
    get() = StateRecoveryStatus(
      headBlockNumber = headBlock.number,
      stateRecoverStartBlockNumber = stateRecoverStartBlockNumber,
    )

  @Synchronized
  override fun addLookbackHashes(blocksHashes: Map<ULong, ByteArray>): SafeFuture<Unit> {
    // no-op
    return SafeFuture.completedFuture(Unit)
  }

  @Synchronized
  override fun lineaEngineImportBlocksFromBlob(
    blocks: List<BlockFromL1RecoveredData>,
  ): SafeFuture<Unit> {
    if (log.isTraceEnabled) {
      log.trace("lineaEngineImportBlocksFromBlob($blocks)")
    } else {
      val interval = CommonDomainFunctions.blockIntervalString(
        blocks.first().header.blockNumber,
        blocks.last().header.blockNumber,
      )
      log.debug("lineaEngineImportBlocksFromBlob(interval=$interval)")
    }
    _importedBlocksInRecoveryMode.addAll(blocks)
    headBlock = blocks.last().let { BlockNumberAndHash(it.header.blockNumber, it.header.blockHash) }
    return SafeFuture.completedFuture(Unit)
  }

  @Synchronized
  override fun getBlockNumberAndHash(
    blockParameter: BlockParameter,
  ): SafeFuture<BlockNumberAndHash> {
    log.trace("getBlockNumberAndHash($blockParameter): $headBlock")
    return SafeFuture.completedFuture(headBlock)
  }

  @Synchronized
  override fun lineaGetStateRecoveryStatus(): SafeFuture<StateRecoveryStatus> {
    log.trace("lineaGetStateRecoveryStatus()= $stateRecoverStatus")
    return SafeFuture.completedFuture(stateRecoverStatus)
  }

  @Synchronized
  override fun lineaEnableStateRecovery(
    stateRecoverStartBlockNumber: ULong,
  ): SafeFuture<StateRecoveryStatus> {
    this.stateRecoverStartBlockNumber = stateRecoverStartBlockNumber
    log.debug("lineaEnableStateRecovery($stateRecoverStartBlockNumber) = $stateRecoverStatus")
    return SafeFuture.completedFuture(stateRecoverStatus)
  }
}
