#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

WITH_CUTOVER=false
DRY_RUN=false

for arg in "$@"; do
  case "$arg" in
    --with-cutover) WITH_CUTOVER=true ;;
    --dry-run) DRY_RUN=true ;;
    *)
      echo "Unknown option: $arg" >&2
      echo "Usage: $0 [--dry-run] [--with-cutover]" >&2
      exit 1
      ;;
  esac
done

source "$SMOM_ROOT/scripts/load-env.sh" >/dev/null 2>&1

command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; }
command -v forge >/dev/null 2>&1 || { echo "forge is required" >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }

ENV_FILE="$SMOM_ROOT/.env"
DEPLOYER_ADDRESS="$(cast wallet address "$PRIVATE_KEY")"
RPC="$RPC_URL_138"
CW_VAULT="${CW_STABLECOIN_RESERVE_VAULT:-}"
TARGET_USDT_BACKING="${CW_VAULT_SEED_USDT_AMOUNT:-}"
TARGET_USDC_BACKING="${CW_VAULT_SEED_USDC_AMOUNT:-}"

log() {
  printf '[cw-vault] %s\n' "$*"
}

redact_secrets() {
  local text="$1"
  if [[ -n "${PRIVATE_KEY:-}" ]]; then
    text="${text//${PRIVATE_KEY}/\$PRIVATE_KEY}"
  fi
  printf '%s\n' "$text"
}

format_command() {
  printf '%q ' "$@"
  printf '\n'
}

run_command() {
  if $DRY_RUN; then
    redact_secrets "$(format_command "$@")"
  else
    "$@"
  fi
}

has_code() {
  local address="$1"
  [[ -n "$address" ]] || return 1
  local code
  code="$(cast code "$address" --rpc-url "$RPC" 2>/dev/null || true)"
  [[ -n "$code" && "$code" != "0x" ]]
}

send_cast() {
  local to="$1"
  local signature="$2"
  shift 2

  local cmd=(cast send)
  local gas_price
  gas_price="$(cast gas-price --rpc-url "$RPC" 2>/dev/null || true)"
  if [[ "$gas_price" =~ ^[0-9]+$ && "$gas_price" -gt 0 ]]; then
    gas_price=$((gas_price + gas_price / 5 + 1000000))
    cmd+=(--gas-price "$gas_price")
  fi

  cmd+=(--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy "$to" "$signature")
  if [[ "$#" -gt 0 ]]; then
    cmd+=("$@")
  fi

  run_command "${cmd[@]}"
}

set_env_value() {
  local file="$1"
  local key="$2"
  local value="$3"
  local escaped_value
  escaped_value="$(printf '%s' "$value" | sed -e 's/[\\/&]/\\&/g')"
  if grep -q "^${key}=" "$file"; then
    sed -i "s/^${key}=.*/${key}=${escaped_value}/" "$file"
  else
    printf '%s=%s\n' "$key" "$value" >>"$file"
  fi
}

deploy_from_broadcast() {
  local script_file="$1"
  local script_contract="$2"
  local extra_env="$3"
  local broadcast_path="$SMOM_ROOT/broadcast/${script_file}/138/run-latest.json"

  if $DRY_RUN; then
    redact_secrets "$extra_env forge script script/reserve/${script_file}:${script_contract} --rpc-url \"$RPC\" --broadcast --private-key \"$PRIVATE_KEY\" --legacy -vvv"
    return 0
  fi

  (
    cd "$SMOM_ROOT"
    eval "$extra_env forge script script/reserve/${script_file}:${script_contract} --rpc-url \"$RPC\" --broadcast --private-key \"\$PRIVATE_KEY\" --legacy -vvv >/tmp/${script_contract}.log"
  )

  jq -r '.transactions[] | select(.transactionType == "CREATE") | .contractAddress' "$broadcast_path" | tail -1
}

call_uint() {
  cast call "$1" "$2" "${@:3}" --rpc-url "$RPC"
}

call_addr() {
  cast call "$1" "$2" --rpc-url "$RPC"
}

normalize_uint() {
  printf '%s\n' "$1" | awk '{print $1}'
}

ensure_vault() {
  if has_code "$CW_VAULT"; then
    log "Using existing stablecoin reserve vault $CW_VAULT"
    return
  fi

  log "Deploying stablecoin reserve vault"
  if $DRY_RUN; then
    deploy_from_broadcast "DeployStablecoinReserveVault.s.sol" "DeployStablecoinReserveVault" ""
    CW_VAULT="0x0000000000000000000000000000000000000140"
    return
  fi

  CW_VAULT="$(
    deploy_from_broadcast "DeployStablecoinReserveVault.s.sol" "DeployStablecoinReserveVault" ""
  )"
  set_env_value "$ENV_FILE" "CW_STABLECOIN_RESERVE_VAULT" "$CW_VAULT"
}

ensure_official_balance() {
  local token="$1"
  local amount_needed="$2"
  local label="$3"
  local owner balance shortfall

  owner="$(cast call "$token" "owner()(address)" --rpc-url "$RPC")"
  balance="$(normalize_uint "$(cast call "$token" "balanceOf(address)(uint256)" "$DEPLOYER_ADDRESS" --rpc-url "$RPC")")"

  if [[ "$balance" =~ ^[0-9]+$ ]] && (( balance >= amount_needed )); then
    log "$label deployer balance already sufficient: $balance"
    return
  fi

  shortfall=$((amount_needed - balance))
  if [[ "${owner,,}" != "${DEPLOYER_ADDRESS,,}" ]]; then
    echo "Deployer is not owner of $label token $token; cannot mint shortfall $shortfall" >&2
    exit 1
  fi

  if (( shortfall > 0 )); then
    send_cast "$token" "mint(address,uint256)" "$DEPLOYER_ADDRESS" "$shortfall"
  fi
}

ensure_seeded_backing() {
  local official_token="$1"
  local reserve_getter="$2"
  local seed_signature="$3"
  local target_amount="$4"
  local label="$5"

  local current_reserve shortfall
  if $DRY_RUN; then
    current_reserve=0
  else
    current_reserve="$(normalize_uint "$(cast call "$CW_VAULT" "$reserve_getter" --rpc-url "$RPC")")"
  fi
  if [[ -z "$target_amount" ]]; then
    target_amount="$(normalize_uint "$(cast call "$official_token" "totalSupply()(uint256)" --rpc-url "$RPC")")"
  else
    target_amount="$(normalize_uint "$target_amount")"
  fi

  if (( current_reserve >= target_amount )); then
    log "$label reserve already seeded: $current_reserve"
    return
  fi

  shortfall=$((target_amount - current_reserve))
  ensure_official_balance "$official_token" "$shortfall" "$label"
  send_cast "$official_token" "approve(address,uint256)" "$CW_VAULT" "$shortfall"
  send_cast "$CW_VAULT" "$seed_signature" "$shortfall"
}

ensure_compliant_owner() {
  local token="$1"
  local label="$2"
  local owner
  if $DRY_RUN; then
    owner="$DEPLOYER_ADDRESS"
  else
    owner="$(cast call "$token" "owner()(address)" --rpc-url "$RPC")"
  fi
  if [[ "${owner,,}" == "${CW_VAULT,,}" ]]; then
    log "$label ownership already transferred to vault"
    return
  fi
  if [[ "${owner,,}" != "${DEPLOYER_ADDRESS,,}" ]]; then
    echo "$label owner is $owner, expected deployer or vault" >&2
    exit 1
  fi
  send_cast "$token" "transferOwnership(address)" "$CW_VAULT"
}

verify_vault_state() {
  local usdt_owner usdc_owner usdt_ratio usdc_ratio
  usdt_owner="$(cast call "$COMPLIANT_USDT_ADDRESS" "owner()(address)" --rpc-url "$RPC")"
  usdc_owner="$(cast call "$COMPLIANT_USDC_ADDRESS" "owner()(address)" --rpc-url "$RPC")"
  usdt_ratio="$(cast call "$CW_VAULT" "getBackingRatio(address)(uint256,uint256,uint256)" "$COMPLIANT_USDT_ADDRESS" --rpc-url "$RPC" | tr '\n' ' ' | xargs)"
  usdc_ratio="$(cast call "$CW_VAULT" "getBackingRatio(address)(uint256,uint256,uint256)" "$COMPLIANT_USDC_ADDRESS" --rpc-url "$RPC" | tr '\n' ' ' | xargs)"
  log "cUSDT owner: $usdt_owner"
  log "cUSDC owner: $usdc_owner"
  log "cUSDT backing tuple: $usdt_ratio"
  log "cUSDC backing tuple: $usdc_ratio"
}

log "Deployer: $DEPLOYER_ADDRESS"
log "Dry run: $DRY_RUN"

if [[ -z "$TARGET_USDT_BACKING" ]]; then
  TARGET_USDT_BACKING="$(normalize_uint "$(cast call "$COMPLIANT_USDT_ADDRESS" "totalSupply()(uint256)" --rpc-url "$RPC")")"
fi
if [[ -z "$TARGET_USDC_BACKING" ]]; then
  TARGET_USDC_BACKING="$(normalize_uint "$(cast call "$COMPLIANT_USDC_ADDRESS" "totalSupply()(uint256)" --rpc-url "$RPC")")"
fi

ensure_vault

if ! $DRY_RUN; then
  set_env_value "$ENV_FILE" "CW_STABLECOIN_RESERVE_VAULT" "$CW_VAULT"
  set_env_value "$ENV_FILE" "CW_REQUIRE_VAULT_BACKING" "1"
  set_env_value "$ENV_FILE" "CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT" "1"
  set_env_value "$ENV_FILE" "CW_REQUIRE_RESERVE_SYSTEM_BALANCE" "0"
fi

ensure_seeded_backing "$OFFICIAL_USDT_ADDRESS" "usdtReserveBalance()(uint256)" "seedUSDTReserve(uint256)" "$TARGET_USDT_BACKING" "USDT"
ensure_seeded_backing "$OFFICIAL_USDC_ADDRESS" "usdcReserveBalance()(uint256)" "seedUSDCReserve(uint256)" "$TARGET_USDC_BACKING" "USDC"

ensure_compliant_owner "$COMPLIANT_USDT_ADDRESS" "cUSDT"
ensure_compliant_owner "$COMPLIANT_USDC_ADDRESS" "cUSDC"

if ! $DRY_RUN; then
  verify_vault_state
fi

if $WITH_CUTOVER; then
  if $DRY_RUN; then
    echo "(cd \"$SMOM_ROOT\" && ./scripts/deployment/complete-nonprefunded-avax-cutover.sh --dry-run)"
  else
    (cd "$SMOM_ROOT" && ./scripts/deployment/complete-nonprefunded-avax-cutover.sh)
  fi
fi
