#!/usr/bin/env bash
#
# Cloud Run Jobs entrypoint for preview-realtime.
#
# Responsibilities:
#   1. Fetch a fresh shallow clone of main.
#   2. Run scripts/preview-realtime.py (which itself appends rows and pushes
#      via boatrace.git_operations).
#
# Required env (injected by the Cloud Run Job spec):
#   GITHUB_TOKEN  - fine-grained PAT with Contents:Write on the repo (from
#                   Secret Manager: github-token).
#
# Optional env (sensible defaults baked in):
#   GITHUB_REPO        owner/name of the GitHub repo
#   GIT_BRANCH         branch to clone and push to
#   GIT_USER_NAME      committer name
#   GIT_USER_EMAIL     committer email
#   PREVIEW_EXTRA_ARGS extra flags forwarded to preview-realtime.py (e.g. --dry-run)
#
# Exit codes:
#   0  success (including "no eligible races, nothing to do")
#   1  unrecoverable failure (clone failed, python crashed, etc.)
#
# Idempotency: the python script dedupes against existing CSV rows, so a
# re-run after a transient failure is safe.

set -Eeuo pipefail

log() {
  printf '[run.sh %s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*"
}

on_error() {
  local exit_code=$?
  log "FAILED (exit=${exit_code}) at line $1"
  exit "${exit_code}"
}
trap 'on_error $LINENO' ERR

: "${GITHUB_TOKEN:?GITHUB_TOKEN is required (mount Secret Manager secret github-token)}"

GITHUB_REPO="${GITHUB_REPO:-BoatraceCSV/boatracecsv.github.io}"
GIT_BRANCH="${GIT_BRANCH:-main}"
GIT_USER_NAME="${GIT_USER_NAME:-preview-realtime-bot}"
GIT_USER_EMAIL="${GIT_USER_EMAIL:-preview-realtime-bot@users.noreply.github.com}"
PREVIEW_EXTRA_ARGS="${PREVIEW_EXTRA_ARGS:-}"

# fun-site への CSV ミラーと Pub/Sub 通知用の環境変数 (Cloud Run Job 側で注入推奨)。
# 未設定なら scripts/boatrace/gcs_publisher.py が自動で no-op になる。
export BOATRACE_GCS_CSV_BUCKET="${BOATRACE_GCS_CSV_BUCKET:-}"
export BOATRACE_PUBSUB_TOPIC="${BOATRACE_PUBSUB_TOPIC:-}"

WORKDIR="$(mktemp -d -t preview-realtime.XXXXXX)"
cleanup() {
  # Best-effort cleanup; never fail the job because of this.
  rm -rf "${WORKDIR}" 2>/dev/null || true
}
trap cleanup EXIT

cd "${WORKDIR}"

# Embed the token in the URL only for the clone; immediately rewrite the
# remote so subsequent fetch/push from the python helper also authenticate
# without ever logging the token in `git remote -v`-style debug output.
REMOTE_WITH_TOKEN="https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPO}.git"
REMOTE_PUBLIC="https://github.com/${GITHUB_REPO}.git"

# The repo is ~5 GB (mostly data/ and models/). A naive `git clone` OOMs on
# the 512 MiB Cloud Run Job. We do a partial clone (commits/trees only, no
# blobs) and then a cone-mode sparse-checkout of just the paths the script
# actually reads or writes:
#
#   - scripts/                         python sources (preview-realtime.py + build_index.py)
#   - .boatrace/                       runtime config (load_config)
#   - data/estimate/stadium/           win_rate.csv / sui_params.csv /
#                                      weights/<predictor>/*.csv
#   - data/estimate/<predictor>/<YYYY>/<MM>/  active 予想者ごとの今日の index CSV
#                                      (read+write target)
#   - data/programs/recent_national/<YYYY>/<MM>/ feature input — 全国近況5節
#   - data/programs/recent_local/<YYYY>/<MM>/    feature input — 当地近況5節
#   - data/programs/motor_stats/<YYYY>/<MM>/     feature input — モーター期成績 (this month)
#   - data/programs/motor_stats/<PREV_YM>/       previous month — motor_pt has 7-day fallback
#                                       that crosses month boundaries near month start
#   - data/previews/{tkz,stt,sui,original_exhibition,tokuten_hayami,od1,od2,od3}/<YYYY>/<MM>/
#                                       existing CSVs for today (for dedup) + write target
#                                       (od1/od2/od3 = 集計中オッズ。cone 外だと
#                                       git add が無視されて永続化されない)
#                                       ※ preview-realtime.py の PREVIEW_SOURCES に
#                                       種別を足したら必ずここも足すこと。cone 外の
#                                       パスが 1 つでも混ざると `git add` 全体が
#                                       失敗し、その回の commit が丸ごと落ちる。
#   - data/results/realtime/<YYYY>/<MM>/        bc_rs1_2 由来の realtime 結果 CSV
#                                       (existing CSV for dedup + write target).
#                                       cone 外だと git add が無視され、
#                                       ファイルが永続化されない。
#   - data/results/payouts/<YYYY>/<MM>/         bc_rs2 由来の払戻金 CSV
#                                       (existing CSV for dedup + write target).
#                                       results/realtime と同じく cone 外だと
#                                       git add が無視されて永続化されない。
#   - data/programs/title/<YYYY>/<MM>/        GCS ミラー対象 (fun-site が読む)
#   - data/programs/race_cards/<YYYY>/<MM>/   GCS ミラー対象 (fun-site が読む)
#
# Today's YYYY/MM is computed in JST because csv_path_for() uses JST dates.
TODAY_YM=$(TZ=Asia/Tokyo date +'%Y/%m')
PREV_YM=$(TZ=Asia/Tokyo date -d "$(TZ=Asia/Tokyo date +'%Y-%m-15') -1 month" +'%Y/%m')

# Active な予想者の ID リスト。scripts/boatrace/predictors/registry.py の
# ``active_predictors()`` と必ず同期させる (新規予想者追加時は両方更新)。
ACTIVE_PREDICTORS=(v1_basic v10_kimarite)  # 2026-08-22: v9_suji を退役 (B案 v10_kimarite と穴予想スロットが重複。回収率では差を示せず、主判定 log-loss に載る B案を残した) / 2026-08-13: 穴予想 B案 v10_kimarite を投入 (決まり手モデル。判定は 3連単 log-loss) / 2026-08-12: 穴予想 A案 v9_suji を投入 (control と同一成分・買い目のみ差分) / 2026-08-10: v4_motor/v5_slit を退役 (control 比で有意差なし + 買い目が control とほぼ重複) / 2026-08-09: v6_course/v7_aggregate/v8_aionly を退役 (control 比で有意に低回収率) / 2026-07-19: v2_tenkai/v3_tenkai を退役 (registry active_predictors() と同期)

log "Cloning ${REMOTE_PUBLIC} (branch=${GIT_BRANCH}, partial+sparse, ym=${TODAY_YM}, prev=${PREV_YM})"
git clone \
  --depth 1 \
  --filter=blob:none \
  --no-checkout \
  --no-tags \
  --single-branch \
  --branch "${GIT_BRANCH}" \
  "${REMOTE_WITH_TOKEN}" \
  repo

cd repo

sparse_paths=(
  scripts
  .boatrace
  data/estimate/stadium
  # 決まり手注釈テーブル (静的、月次更新)。v9_suji 退役後も
  # build_kimarite_picks.py が kimarite_table.csv を読むので外さないこと
  # (2026-08-22 退役。registry.py 冒頭コメント参照)。
  data/estimate/suji/tables
  # 荒れ度メーター / 穴予想 v10_kimarite: 決まり手セルの学習済み係数と
  # セル条件付きの 2-3 着表 (静的、月次更新)。
  data/estimate/kimarite/tables
  "data/programs/recent_national/${TODAY_YM}"
  "data/programs/recent_local/${TODAY_YM}"
  "data/programs/motor_stats/${TODAY_YM}"
  "data/programs/motor_stats/${PREV_YM}"
  "data/previews/tkz/${TODAY_YM}"
  "data/previews/stt/${TODAY_YM}"
  "data/previews/sui/${TODAY_YM}"
  "data/previews/original_exhibition/${TODAY_YM}"
  "data/previews/tokuten_hayami/${TODAY_YM}"
  "data/previews/od1/${TODAY_YM}"
  "data/previews/od2/${TODAY_YM}"
  "data/previews/od3/${TODAY_YM}"
  "data/estimate/kimarite/${TODAY_YM}"
  "data/estimate/kimarite/picks/${TODAY_YM}"
  "data/results/realtime/${TODAY_YM}"
  "data/results/payouts/${TODAY_YM}"
  "data/programs/title/${TODAY_YM}"
  "data/programs/race_cards/${TODAY_YM}"
)
for predictor in "${ACTIVE_PREDICTORS[@]}"; do
  sparse_paths+=("data/estimate/${predictor}/${TODAY_YM}")
done
git sparse-checkout init --cone
git sparse-checkout set "${sparse_paths[@]}"

# Materialize the working tree. Missing blobs are fetched on demand from the
# promisor remote (origin) thanks to --filter=blob:none.
git checkout "${GIT_BRANCH}"

# Ensure boatrace.git_operations push() can fetch + rebase. With depth=1 the
# rebase normally has nothing to do (we just cloned HEAD) but a credential
# helper covers the case where another job advanced origin in the meantime.
git config --local user.name "${GIT_USER_NAME}"
git config --local user.email "${GIT_USER_EMAIL}"
git config --local --replace-all "url.${REMOTE_WITH_TOKEN}.insteadOf" "${REMOTE_PUBLIC}"
# Prevent the token from leaking via `git remote -v` debug output.
git remote set-url origin "${REMOTE_PUBLIC}"

mkdir -p logs

log "Running scripts/preview-realtime.py ${PREVIEW_EXTRA_ARGS}"
# shellcheck disable=SC2086  # PREVIEW_EXTRA_ARGS is intentionally word-split.
python scripts/preview-realtime.py ${PREVIEW_EXTRA_ARGS}

log "Done"
