#!/usr/bin/env bash
set -euo pipefail
[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && set -x
trap 'echo "[deploy-vault] error at line $LINENO" >&2' ERR

: "${REMOTE_BASE:?missing REMOTE_BASE}"
: "${SSH_USER:?missing SSH_USER}"
: "${SSH_HOST:?missing SSH_HOST}"
: "${VAULT_DIR:?missing VAULT_DIR}"
: "${VAULT_COMPOSE_PROJECT:?missing VAULT_COMPOSE_PROJECT}"

REMOTE_DIR="${REMOTE_BASE%/}/${VAULT_DIR}"
REMOTE_TARGET="${SSH_USER}@${SSH_HOST}"
RUNTIME_ENV_FILE="${RUNTIME_ENV_FILE:-ci/prod/.env.runtime}"
COMPOSE_FILE="vault.yml"
SEED_ENV_FILE="${DEV_VAULT_SEED_FILE:-}"

SSH_OPTS=(
  -i /root/.ssh/id_rsa
  -o StrictHostKeyChecking=no
  -o UserKnownHostsFile=/dev/null
  -o LogLevel=ERROR
  -o BatchMode=yes
  -o PreferredAuthentications=publickey
  -o ConnectTimeout=10
  -q
)
if [[ "${DEBUG_DEPLOY:-0}" = "1" ]]; then
  SSH_OPTS=("${SSH_OPTS[@]/-q/}" -vv)
fi

RSYNC_FLAGS=(-az --delete)
[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && RSYNC_FLAGS=(-avz --delete)

ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" "mkdir -p ${REMOTE_DIR}/{compose,env}"

rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" ci/prod/compose/ "$REMOTE_TARGET:${REMOTE_DIR}/compose/"
rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" "${RUNTIME_ENV_FILE}" "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.runtime"
if [[ -n "${SEED_ENV_FILE}" && -f "${SEED_ENV_FILE}" ]]; then
  rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" "${SEED_ENV_FILE}" "$REMOTE_TARGET:${REMOTE_DIR}/env/dev-vault-seed.env"
fi

ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" \
  REMOTE_BASE="$REMOTE_BASE" \
  REMOTE_DIR="$REMOTE_DIR" \
  COMPOSE_FILE="$COMPOSE_FILE" \
  COMPOSE_PROJECT="$VAULT_COMPOSE_PROJECT" \
  bash -s <<'EOSSH'
set -euo pipefail
trap 'status=$?; echo "[deploy-vault:remote] error at line $LINENO" >&2; docker logs --tail 50 dev-vault >&2 || true; exit "$status"' ERR
cd "${REMOTE_DIR}/compose"
set -a
. ../env/.env.runtime
set +a

COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT"
export COMPOSE_PROJECT_NAME

if base64 -d >/dev/null 2>&1 <<<'AA=='; then
  BASE64_DECODE_FLAG='-d'
else
  BASE64_DECODE_FLAG='--decode'
fi

decode_b64() {
  val="$1"
  if [[ -z "$val" ]]; then
    printf ''
    return
  fi
  printf '%s' "$val" | base64 "${BASE64_DECODE_FLAG}"
}

docker compose -f "$COMPOSE_FILE" pull --quiet 2>/dev/null || docker compose -f "$COMPOSE_FILE" pull
docker compose -f "$COMPOSE_FILE" up -d --remove-orphans

status_json=""
for _ in $(seq 1 30); do
  status_json="$(docker exec dev-vault sh -lc 'export VAULT_ADDR=http://127.0.0.1:8200; vault status -format=json' 2>/dev/null || true)"
  if [[ -n "$status_json" ]]; then
    break
  fi
  sleep 2
done

if [[ -z "$status_json" ]]; then
  echo "vault status did not become available" >&2
  exit 65
fi

INIT_FILE="../env/vault-init.json"
if printf '%s' "$status_json" | grep -Eq '"initialized"[[:space:]]*:[[:space:]]*false'; then
  docker exec dev-vault sh -lc 'export VAULT_ADDR=http://127.0.0.1:8200; vault operator init -format=json -key-shares=1 -key-threshold=1' >"$INIT_FILE"
  chmod 600 "$INIT_FILE"
  status_json="$(docker exec dev-vault sh -lc 'export VAULT_ADDR=http://127.0.0.1:8200; vault status -format=json')"
fi

if [[ ! -s "$INIT_FILE" ]]; then
  echo "vault init file is missing: $INIT_FILE" >&2
  exit 66
fi

INIT_JSON_COMPACT="$(tr -d '\r\n\t ' <"$INIT_FILE")"
UNSEAL_KEY="$(printf '%s' "$INIT_JSON_COMPACT" | sed -n 's/.*"unseal_keys_b64":\["\([^"]*\)".*/\1/p')"
ROOT_TOKEN="$(printf '%s' "$INIT_JSON_COMPACT" | sed -n 's/.*"root_token":"\([^"]*\)".*/\1/p')"

if [[ -z "$UNSEAL_KEY" || -z "$ROOT_TOKEN" ]]; then
  echo "failed to extract vault init credentials" >&2
  exit 67
fi

if printf '%s' "$status_json" | grep -Eq '"sealed"[[:space:]]*:[[:space:]]*true'; then
  docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200; vault operator unseal '${UNSEAL_KEY}' >/dev/null"
fi

docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault auth list -format=json | grep -q '\"approle/\"' || vault auth enable approle >/dev/null"
docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault secrets list -format=json | grep -q '\"kv/\"' || vault secrets enable -path=kv kv-v2 >/dev/null"

if [[ -f ../env/dev-vault-seed.env ]]; then
  set -a
  . ../env/dev-vault-seed.env
  set +a

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv registry \
    user="$(decode_b64 "${REGISTRY_USER_B64:-}")" \
    password="$(decode_b64 "${REGISTRY_PASSWORD_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/db \
    user="$(decode_b64 "${SENDICO_DB_USER_B64:-}")" \
    password="$(decode_b64 "${SENDICO_DB_PASSWORD_B64:-}")" \
    key="$(decode_b64 "${SENDICO_DB_KEY_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/nats \
    user="$(decode_b64 "${SENDICO_NATS_USER_B64:-}")" \
    password="$(decode_b64 "${SENDICO_NATS_PASSWORD_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/api/endpoint \
    secret="$(decode_b64 "${SENDICO_API_ENDPOINT_SECRET_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/notification/mail \
    user="$(decode_b64 "${NOTIFICATION_MAIL_USER_B64:-}")" \
    password="$(decode_b64 "${NOTIFICATION_MAIL_PASSWORD_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/notification/telegram \
    bot_token="$(decode_b64 "${NOTIFICATION_TELEGRAM_BOT_TOKEN_B64:-}")" \
    chat_id="$(decode_b64 "${NOTIFICATION_TELEGRAM_CHAT_ID_B64:-}")" \
    thread_id="$(decode_b64 "${NOTIFICATION_TELEGRAM_THREAD_ID_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/gateway/chain \
    arbitrum_rpc_url="$(decode_b64 "${CHAIN_GATEWAY_RPC_URL_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/gateway/chain/wallet \
    private_key="$(decode_b64 "${CHAIN_GATEWAY_WALLET_PRIVATE_KEY_B64:-}")" \
    address="$(decode_b64 "${CHAIN_GATEWAY_WALLET_ADDRESS_B64:-}")" >/dev/null

  docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN="${ROOT_TOKEN}" dev-vault \
    vault kv put -mount=kv sendico/gateway/tron \
    rpc_url="$(decode_b64 "${TRON_GATEWAY_RPC_URL_B64:-}")" \
    grpc_url="$(decode_b64 "${TRON_GATEWAY_GRPC_URL_B64:-}")" \
    grpc_token="$(decode_b64 "${TRON_GATEWAY_GRPC_TOKEN_B64:-}")" >/dev/null
fi

docker exec -i dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault policy write sendico-dev-apps -" <<'EOF'
path "kv/data/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

path "kv/metadata/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
EOF

docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault write auth/approle/role/sendico-dev-apps token_policies='sendico-dev-apps' token_ttl='24h' token_max_ttl='720h' >/dev/null"

APPROLE_ROLE_ID="$(docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault read -field=role_id auth/approle/role/sendico-dev-apps/role-id")"
APPROLE_SECRET_ID="$(docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault write -force -field=secret_id auth/approle/role/sendico-dev-apps/secret-id")"

if [[ -z "$APPROLE_ROLE_ID" || -z "$APPROLE_SECRET_ID" ]]; then
  echo "failed to create dev approle credentials" >&2
  exit 68
fi

write_vault_env() {
  local service_dir="$1"
  local role_var="$2"
  local secret_var="$3"
  local env_dir="${REMOTE_BASE%/}/${service_dir}/env"
  mkdir -p "$env_dir"
  cat >"${env_dir}/vault.env" <<EOF
VAULT_ADDR=${APP_VAULT_ADDR:-http://dev-vault:8200}
${role_var}=${APPROLE_ROLE_ID}
${secret_var}=${APPROLE_SECRET_ID}
EOF
  chmod 600 "${env_dir}/vault.env"
}

write_vault_env "${DB_DIR}" "VAULT_ROLE_ID" "VAULT_SECRET_ID"
write_vault_env "${BFF_DIR}" "BFF_VAULT_ROLE_ID" "BFF_VAULT_SECRET_ID"
write_vault_env "${CALLBACKS_DIR}" "CALLBACKS_VAULT_ROLE_ID" "CALLBACKS_VAULT_SECRET_ID"
write_vault_env "${CHAIN_GATEWAY_DIR}" "CHAIN_GATEWAY_VAULT_ROLE_ID" "CHAIN_GATEWAY_VAULT_SECRET_ID"
write_vault_env "${TRON_GATEWAY_DIR}" "TRON_GATEWAY_VAULT_ROLE_ID" "TRON_GATEWAY_VAULT_SECRET_ID"

docker compose -f "$COMPOSE_FILE" ps
date -Is > .last_deploy
logger -t "deploy-${COMPOSE_PROJECT_NAME}" "${COMPOSE_PROJECT_NAME} deployed at $(date -Is) in ${REMOTE_DIR}"
EOSSH
