# beacon_chain
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed and distributed under either of
#   * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
#   * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import
  std/[json, strutils, times, sequtils],
  chronos, confutils, chronicles,
  web3, web3/primitives,
  eth/async_utils,
  ../beacon_chain/beacon_chain_db,
  ../beacon_chain/networking/network_metadata,
  ../beacon_chain/el/el_manager,
  ../beacon_chain/spec/[presets, helpers]

type
  CliFlags = object
    network* {.
      defaultValue: "mainnet"
      name: "network".}: string
    elUrls* {.
      name: "el".}: seq[EngineApiUrlConfigValue]
    jwtSecret* {.
      name: "jwt-secret".}: Option[InputFile]
    outDepositsFile* {.
      name: "out-deposits-file".}: Option[OutFile]
    configFile* {.
      desc: "Loads the configuration from a TOML file"
      name: "config-file" .}: Option[InputFile]

proc main(flags: CliFlags) {.async.} =
  let
    db = BeaconChainDB.new("", inMemory = true)
    metadata = getMetadataForNetwork(flags.network)
    beaconTimeFn = proc(): BeaconTime =
      # BEWARE of this hack
      # The EL manager consults the current time in order to determine when the
      # transition configuration exchange should start. We assume Bellatrix has
      # just arrived which should trigger the configuration exchange and allow
      # the downloader to connect to ELs serving the Engine API.
      start_beacon_time(Slot(metadata.cfg.BELLATRIX_FORK_EPOCH * SLOTS_PER_EPOCH))

  let
    elManager = ELManager.new(
      metadata.cfg,
      metadata.depositContractBlock,
      metadata.depositContractBlockHash,
      db,
      toFinalEngineApiUrls(flags.elUrls, flags.jwtSecret),
      eth1Network = metadata.eth1Network)

  elManager.start()

  var depositsFile: File
  if flags.outDepositsFile.isSome:
    depositsFile = open(string flags.outDepositsFile.get, fmWrite)
    depositsFile.write(
      "block", ",",
      "validatorKey", ",",
      "withdrawalCredentials", "\n")
    depositsFile.flushFile()

  var blockIdx = 0
  while not elManager.isSynced():
    await sleepAsync chronos.seconds(1)

    if flags.outDepositsFile.isSome and
       elManager.eth1ChainBlocks.len > blockIdx:
      for i in blockIdx ..< elManager.eth1ChainBlocks.len:
        for deposit in elManager.eth1ChainBlocks[i].deposits:
          depositsFile.write(
            $elManager.eth1ChainBlocks[i].number, ",",
            $deposit.pubkey, ",",
            $deposit.withdrawal_credentials, "\n")
          depositsFile.flushFile()

      blockIdx = elManager.eth1ChainBlocks.len

  info "All deposits downloaded"

waitFor main(
  load(CliFlags,
       secondarySources = proc (
           config: CliFlags, sources: ref SecondarySources
       ) {.raises: [ConfigurationError].} =
        if config.configFile.isSome:
          sources.addConfigFile(Toml, config.configFile.get)))