#!/usr/bin/env bash
# Shared bootstrap functions for local dev and CI.
# Source this file after setting these variables:
#   ANVIL_RPC         - Anvil JSON-RPC URL (required)
#   DEPLOYER_PK       - Deployer private key (defaults to Anvil account 0)
#   DEPLOYER_ADDR     - Deployer address (defaults to Anvil account 0)
#   TXNBOT_ADDRESS    - TxnBot wallet address (optional)
#   TXNBOT_PRIVATE_KEY- TxnBot private key (optional)
#   TXNBOT_FUND_VALUE - Amount to fund txnBot (default: 1ether)
#   CONTRACT_ENV      - Path to write contracts.env (required)
#   LOG_FILE          - Log file for cast/forge output (default: /dev/null)
#   ONCHAIN_DIR       - Path to onchain/ directory (required)
#   KRAIKEN_LIB_DIR   - Path to kraiken-lib/ directory (optional, for CI build)

set -euo pipefail

# ── Constants ──────────────────────────────────────────────────────────
FEE_DEST=0xf6a3eef9088A255c32b6aD2025f83E57291D9011
WETH=0x4200000000000000000000000000000000000006
SWAP_ROUTER_SEPOLIA=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4
SWAP_ROUTER_MAINNET=0x2626664c2603336E57B271c5C0b26F421741e481
SWAP_ROUTER=""  # resolved lazily by detect_swap_router()
MAX_UINT=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

DEFAULT_DEPLOYER_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
DEFAULT_DEPLOYER_ADDR=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

DEFAULT_TXNBOT_PK=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
DEFAULT_TXNBOT_ADDR=0x70997970C51812dc3A010C7d01b50e0d17dc79C8

# ── Defaults ───────────────────────────────────────────────────────────
DEPLOYER_PK=${DEPLOYER_PK:-$DEFAULT_DEPLOYER_PK}
DEPLOYER_ADDR=${DEPLOYER_ADDR:-$DEFAULT_DEPLOYER_ADDR}
TXNBOT_FUND_VALUE=${TXNBOT_FUND_VALUE:-1ether}
LOG_FILE=${LOG_FILE:-/dev/null}

# ── Helpers ────────────────────────────────────────────────────────────
bootstrap_log() {
  echo "[bootstrap] $*"
}

# ── Functions ──────────────────────────────────────────────────────────

detect_swap_router() {
  # Idempotency: only detect once
  if [[ -n "$SWAP_ROUTER" ]]; then
    return 0
  fi
  local chain_id
  chain_id="$(cast chain-id --rpc-url "$ANVIL_RPC" 2>/dev/null || echo "")"
  if [[ "$chain_id" == "8453" ]]; then
    SWAP_ROUTER="$SWAP_ROUTER_MAINNET"
    bootstrap_log "Detected Base mainnet (chain ID 8453) — using mainnet SwapRouter"
  else
    SWAP_ROUTER="$SWAP_ROUTER_SEPOLIA"
    bootstrap_log "Using Base Sepolia SwapRouter (chain ID: ${chain_id:-unknown})"
  fi
}

wait_for_rpc() {
  for _ in {1..120}; do
    if cast chain-id --rpc-url "$ANVIL_RPC" >/dev/null 2>&1; then
      return 0
    fi
    sleep 1
  done
  bootstrap_log "Timed out waiting for Anvil at $ANVIL_RPC"
  return 1
}

run_forge_script() {
  bootstrap_log "Deploying contracts to fork"
  pushd "$ONCHAIN_DIR" >/dev/null
  local _forge_log
  _forge_log="$(mktemp)"
  if ! forge script script/DeployLocal.sol --tc DeployLocal --fork-url "$ANVIL_RPC" --broadcast >"$_forge_log" 2>&1; then
    bootstrap_log "forge script FAILED — output:"
    cat "$_forge_log" >&2
    rm -f "$_forge_log"
    popd >/dev/null
    return 1
  fi
  cat "$_forge_log" >>"$LOG_FILE" 2>/dev/null || true
  rm -f "$_forge_log"
  popd >/dev/null
}

extract_addresses() {
  local run_file
  run_file="$(ls -t "$ONCHAIN_DIR/broadcast/DeployLocal.sol"/*/run-latest.json 2>/dev/null | head -n1)"
  if [[ -z "$run_file" ]]; then
    bootstrap_log "Deployment artifact not found"
    exit 1
  fi
  bootstrap_log "Using artifact $run_file"
  LIQUIDITY_MANAGER="$(jq -r '.transactions[] | select(.contractName=="LiquidityManager") | .contractAddress' "$run_file" | head -n1)"
  KRAIKEN="$(jq -r '.transactions[] | select(.contractName=="Kraiken") | .contractAddress' "$run_file" | head -n1)"
  STAKE="$(jq -r '.transactions[] | select(.contractName=="Stake") | .contractAddress' "$run_file" | head -n1)"
  OPTIMIZER_PROXY="$(jq -r '.transactions[] | select(.contractName=="ERC1967Proxy") | .contractAddress' "$run_file" | head -n1)"
  DEPLOY_BLOCK="$(jq -r '.receipts[0].blockNumber' "$run_file" | xargs printf "%d")"
  if [[ -z "$LIQUIDITY_MANAGER" || "$LIQUIDITY_MANAGER" == "null" ]]; then
    bootstrap_log "LiquidityManager address missing"
    exit 1
  fi
}

write_contracts_env() {
  cat >"$CONTRACT_ENV" <<EOCONTRACTS
LIQUIDITY_MANAGER=$LIQUIDITY_MANAGER
KRAIKEN=$KRAIKEN
STAKE=$STAKE
EOCONTRACTS
}

fund_liquidity_manager() {
  bootstrap_log "Funding LiquidityManager"
  cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$LIQUIDITY_MANAGER" --value 10ether >>"$LOG_FILE" 2>&1
}


bootstrap_vwap() {
  detect_swap_router
  # WARNING: If the second recenter() call later in this function fails mid-sequence, the LM is left
  # with positions deployed but cumulativeVolume == 0 (partial bootstrap).
  # For mainnet recovery see docs/mainnet-bootstrap.md or scripts/recover-bootstrap.sh.
  # Idempotency guard: if a previous run already bootstrapped VWAP, skip.
  local cumvol
  cumvol="$(cast call --rpc-url "$ANVIL_RPC" \
    "$LIQUIDITY_MANAGER" "cumulativeVolume()(uint256)" 2>/dev/null || echo "0")"
  if [[ "$cumvol" != "0" && -n "$cumvol" ]]; then
    bootstrap_log "VWAP already bootstrapped (cumulativeVolume=$cumvol) -- skipping"
    return 0
  fi

  local recenter_pk="${TXNBOT_PRIVATE_KEY:-$DEPLOYER_PK}"

  # Fund LM with 1 ETH (thin bootstrap positions; 0.5 ETH seed swap moves >400 ticks)
  bootstrap_log "Funding LM with 1 ETH for VWAP bootstrap..."
  cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$LIQUIDITY_MANAGER" --value 1ether >>"$LOG_FILE" 2>&1

  # Advance Anvil time 301s so TWAP oracle has sufficient history for _isPriceStable()
  cast rpc --rpc-url "$ANVIL_RPC" evm_increaseTime 301 >>"$LOG_FILE" 2>&1
  cast rpc --rpc-url "$ANVIL_RPC" evm_mine >>"$LOG_FILE" 2>&1

  # First recenter: places initial bootstrap positions; no fees yet, cumulativeVolume stays 0
  bootstrap_log "First recenter (places bootstrap positions)..."
  cast send --rpc-url "$ANVIL_RPC" --private-key "$recenter_pk" \
    "$LIQUIDITY_MANAGER" "recenter()" >>"$LOG_FILE" 2>&1

  # Seed buy: wrap 0.5 ETH to WETH and swap WETH->KRK
  # Generates a non-zero WETH fee in the anchor position and moves price >400 ticks.
  # sqrtPriceLimitX96 is direction-dependent: MIN+1 when WETH<KRK (token0), MAX-1 otherwise.
  bootstrap_log "Executing seed buy (0.5 ETH WETH->KRK)..."
  cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$WETH" "deposit()" --value 0.5ether >>"$LOG_FILE" 2>&1
  cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$WETH" "approve(address,uint256)" "$SWAP_ROUTER" "$MAX_UINT" >>"$LOG_FILE" 2>&1

  local weth_addr kraiken_addr sqrt_limit
  weth_addr=$(echo "$WETH" | tr '[:upper:]' '[:lower:]' | sed 's/^0x//')
  kraiken_addr=$(echo "$KRAIKEN" | tr '[:upper:]' '[:lower:]' | sed 's/^0x//')
  if [[ "$weth_addr" < "$kraiken_addr" ]]; then
    sqrt_limit=4295128740  # WETH=token0, zeroForOne=true, price decreases
  else
    sqrt_limit=1461446703485210103287273052203988822378723970341  # WETH=token1, price increases
  fi

  cast send --legacy --gas-limit 300000 --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$SWAP_ROUTER" "exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))" \
    "($WETH,$KRAIKEN,10000,$DEPLOYER_ADDR,500000000000000000,0,$sqrt_limit)" >>"$LOG_FILE" 2>&1

  # Advance time 301s so TWAP settles at post-buy price and cooldown (60s) elapses
  cast rpc --rpc-url "$ANVIL_RPC" evm_increaseTime 301 >>"$LOG_FILE" 2>&1
  cast rpc --rpc-url "$ANVIL_RPC" evm_mine >>"$LOG_FILE" 2>&1

  # Second recenter: cumulativeVolume==0 path fires (bootstrap), ethFee>0 -> records VWAP
  bootstrap_log "Second recenter (records VWAP)..."
  cast send --rpc-url "$ANVIL_RPC" --private-key "$recenter_pk" \
    "$LIQUIDITY_MANAGER" "recenter()" >>"$LOG_FILE" 2>&1

  # Verify VWAP bootstrap succeeded
  cumvol="$(cast call --rpc-url "$ANVIL_RPC" \
    "$LIQUIDITY_MANAGER" "cumulativeVolume()(uint256)" 2>/dev/null || echo "0")"
  if [[ "$cumvol" == "0" || -z "$cumvol" ]]; then
    bootstrap_log "ERROR: VWAP bootstrap failed -- cumulativeVolume is 0"
    return 1
  fi
  bootstrap_log "VWAP bootstrapped (cumulativeVolume=$cumvol)"
}

seed_application_state() {
  detect_swap_router
  bootstrap_log "Wrapping ETH to WETH"
  cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$WETH" "deposit()" --value 2ether >>"$LOG_FILE" 2>&1
  bootstrap_log "Approving router"
  cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$WETH" "approve(address,uint256)" "$SWAP_ROUTER" "$MAX_UINT" >>"$LOG_FILE" 2>&1

  # Swap with retry — recenter may not position liquidity at the right tick on first call
  local swap_success=false
  for attempt in 1 2 3; do
    bootstrap_log "KRK swap attempt $attempt/3"
    local balance_before balance_after
    balance_before=$(cast call --rpc-url "$ANVIL_RPC" "$KRAIKEN" "balanceOf(address)(uint256)" "$DEPLOYER_ADDR" 2>/dev/null || echo "0")

    cast send --legacy --gas-limit 300000 --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
      "$SWAP_ROUTER" "exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))" \
      "($WETH,$KRAIKEN,10000,$DEPLOYER_ADDR,1000000000000000000,0,4295128740)" >>"$LOG_FILE" 2>&1 || true

    balance_after=$(cast call --rpc-url "$ANVIL_RPC" "$KRAIKEN" "balanceOf(address)(uint256)" "$DEPLOYER_ADDR" 2>/dev/null || echo "0")

    if [[ "$balance_after" != "$balance_before" && "$balance_after" != "0" ]]; then
      bootstrap_log "Swap successful — got KRK tokens (balance: $balance_after)"
      swap_success=true
      break
    fi

    bootstrap_log "Swap returned 0 KRK — recentering and retrying"
    # Advance 61 s to clear the 60-second recenter cooldown, then mine a block.
    cast rpc --rpc-url "$ANVIL_RPC" evm_increaseTime 61 >>"$LOG_FILE" 2>&1 || true
    cast rpc --rpc-url "$ANVIL_RPC" evm_mine >>"$LOG_FILE" 2>&1 || true
    local recenter_pk="${TXNBOT_PRIVATE_KEY:-$DEPLOYER_PK}"
    cast send --rpc-url "$ANVIL_RPC" --private-key "$recenter_pk" \
      "$LIQUIDITY_MANAGER" "recenter()" >>"$LOG_FILE" 2>&1 || true
  done

  if [[ "$swap_success" != "true" ]]; then
    bootstrap_log "WARNING: All swap attempts returned 0 KRK. Pool may have no liquidity at current tick."
  fi
}

fund_txn_bot_wallet() {
  if [[ -z "${TXNBOT_ADDRESS:-}" ]]; then
    return
  fi
  bootstrap_log "Funding txnBot wallet $TXNBOT_ADDRESS with $TXNBOT_FUND_VALUE"
  cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
    "$TXNBOT_ADDRESS" --value "$TXNBOT_FUND_VALUE" >>"$LOG_FILE" 2>&1 || true
  local wei hex
  wei="$(cast --to-unit "$TXNBOT_FUND_VALUE" wei)"
  hex="$(cast --to-hex "$wei")"
  cast rpc --rpc-url "$ANVIL_RPC" anvil_setBalance "$TXNBOT_ADDRESS" "$hex" >>"$LOG_FILE" 2>&1
}

write_deployments_json() {
  local target="${1:-$ONCHAIN_DIR/deployments-local.json}"
  cat >"$target" <<EODEPLOYMENTS
{
  "contracts": {
    "Kraiken": "$KRAIKEN",
    "Stake": "$STAKE",
    "LiquidityManager": "$LIQUIDITY_MANAGER",
    "OptimizerProxy": "$OPTIMIZER_PROXY"
  },
  "infrastructure": {
    "weth": "$WETH"
  }
}
EODEPLOYMENTS
}
