#!/usr/bin/env bash
# photoup: get the auth_data (Google Photos login token) of a Google account, ready to paste into
# photoup under "Thêm profile Google Photos". Standalone: needs only bash and adb, so it can be
# downloaded from the photoup web UI and run on any Mac/Linux machine.
#
# How: Google Photos ReVanced signs in through GmsCore, and GmsCore writes the login request
# (androidId=...&Token=...) to the Android log. The script signs the device out (so the app must
# log in again), waits for that line while you sign in, then base64-encodes it straight into your
# clipboard. It never prints the token on screen and never writes it to a file.
#
# Needs:
#   - adb:  macOS `brew install android-platform-tools`; Debian/Ubuntu `sudo apt install adb`
#   - an Android emulator (Android Studio) or an Android phone with USB debugging on, with
#     GmsCore and Google Photos ReVanced installed (the script says where to get them if missing)
#
# Usage:
#   ./get-auth-data.sh --email you@gmail.com                       # base64 to the clipboard: paste it in
#                                                                  #   the web UI under "Thêm profile"
#   ./get-auth-data.sh --email you@gmail.com --to stdout \
#       | ssh my-server 'cd photoup && docker compose exec -T photoup python -m app add-profile --user me'
#   Inside a photoup checkout only:
#   ./get-auth-data.sh --email you@gmail.com --to local --user me  # add a profile for user "me" here
#   ./get-auth-data.sh --email you@gmail.com --to docker --user me # same, in the docker-compose container
#
# Options:
#   --email ADDRESS   only accept a login for this account (recommended: devices often hold several)
#   --to TARGET       clipboard (default) | stdout | local | docker — auth_data luôn là base64 một dòng
#   --user NAME       photoup user that gets the profile (--to local / docker)
#   --name LABEL      profile display name (default: the Google account email)
#   --serial SERIAL   adb device to use (default: a running emulator, else the only device)
#   --avd NAME        emulator to start when no device is connected (default: photoup_auth, else the first)
#   --no-fresh        do not clear Photos ReVanced + GmsCore data first. By default they are cleared on
#                     every run, because a signed-in app reuses its cached token and logs nothing. On a
#                     phone this also signs out other ReVanced apps (YouTube...); photos are untouched.
#   --timeout SEC     how long to wait for the login (default: 600)
#
# Environment: APK_DIR (folder with gmscore.apk and photos.apk to install on a device that lacks
# them), ANDROID_HOME (Android SDK location), COMPOSE_SERVICE (default photoup, for --to docker).
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EMAIL=""
TO="clipboard"
PHOTOUP_USER=""
PROFILE_NAME=""
SERIAL=""
AVD=""
FRESH=true
TIMEOUT=600
COMPOSE_SERVICE="${COMPOSE_SERVICE:-photoup}"
APK_DIR="${APK_DIR:-$ROOT/android-apks}"

GMS_PKG="app.revanced.android.gms"
PHOTOS_PKG="app.revanced.android.photos"

say() { printf '%s\n' "$*" >&2; }
die() { say "Lỗi: $*"; exit 1; }

while [ $# -gt 0 ]; do
  case "$1" in
    --email) EMAIL="${2:?}"; shift 2 ;;
    --to) TO="${2:?}"; shift 2 ;;
    --user) PHOTOUP_USER="${2:?}"; shift 2 ;;
    --name) PROFILE_NAME="${2:?}"; shift 2 ;;
    --serial) SERIAL="${2:?}"; shift 2 ;;
    --avd) AVD="${2:?}"; shift 2 ;;
    --no-fresh) FRESH=false; shift ;;
    --fresh) FRESH=true; shift ;; # the default; kept so older commands still work
    --timeout) TIMEOUT="${2:?}"; shift 2 ;;
    -h|--help) sed -n '2,38p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    *) die "không hiểu tham số '$1' (xem --help)" ;;
  esac
done

case "$TO" in
  clipboard)
    if command -v pbcopy >/dev/null; then COPY=(pbcopy)
    elif command -v wl-copy >/dev/null; then COPY=(wl-copy)
    elif command -v xclip >/dev/null; then COPY=(xclip -selection clipboard)
    elif command -v clip.exe >/dev/null; then COPY=(clip.exe)
    else die "không tìm thấy lệnh copy clipboard (pbcopy, wl-copy, xclip, clip.exe). Dùng --to stdout | <lệnh của bạn>."; fi
    ;;
  stdout)
    if [ -t 1 ]; then die "--to stdout chỉ dùng khi chuyển tiếp qua pipe (| ...), để token không hiện ra màn hình"; fi
    ;;
  local|docker)
    [ -f "$ROOT/app/__main__.py" ] || die "--to $TO chỉ dùng được trong thư mục mã nguồn photoup. Dùng --to clipboard rồi dán vào web."
    [ -n "$PHOTOUP_USER" ] || die "--to $TO cần --user <tên đăng nhập photoup> để biết thêm profile cho ai"
    if [ "$TO" = local ]; then
      command -v uv >/dev/null || die "chưa có uv. Cài: brew install uv"
    else
      command -v docker >/dev/null || die "chưa có docker"
      (cd "$ROOT" && docker compose ps --status running --services 2>/dev/null | grep -qx "$COMPOSE_SERVICE") \
        || die "container '$COMPOSE_SERVICE' chưa chạy. Chạy trước: docker compose up -d"
    fi
    ;;
  *) die "--to phải là clipboard, stdout, local hoặc docker" ;;
esac
command -v adb >/dev/null || die "chưa có adb. macOS: brew install android-platform-tools; Ubuntu: sudo apt install adb"

# --- pick a device ---------------------------------------------------------------------

devices() { adb devices | awk 'NR > 1 && $2 == "device" { print $1 }'; }

find_emulator() {
  local sdk
  for sdk in "${ANDROID_HOME:-}" "${ANDROID_SDK_ROOT:-}" "$HOME/Library/Android/sdk" "$HOME/Android/Sdk"; do
    if [ -n "$sdk" ] && [ -x "$sdk/emulator/emulator" ]; then
      printf '%s' "$sdk/emulator/emulator"
      return 0
    fi
  done
  return 1
}

if [ -z "$SERIAL" ]; then
  SERIAL="$(devices | grep '^emulator-' | head -1 || true)"
  if [ -z "$SERIAL" ]; then
    count="$(devices | wc -l | tr -d ' ')"
    if [ "$count" = 1 ]; then
      SERIAL="$(devices)"
    elif [ "$count" -gt 1 ]; then
      die "có nhiều thiết bị, chọn một bằng --serial: $(devices | tr '\n' ' ')"
    fi
  fi
fi

if [ -z "$SERIAL" ]; then
  EMULATOR="$(find_emulator)" || die "không có thiết bị Android nào. Cắm điện thoại (bật Gỡ lỗi USB), hoặc cài Android Studio và tạo một máy ảo."
  AVDS="$("$EMULATOR" -list-avds 2>/dev/null || true)"
  if [ -z "$AVD" ]; then
    AVD="$(printf '%s\n' "$AVDS" | grep -x photoup_auth || printf '%s\n' "$AVDS" | sed -n 1p)"
  fi
  [ -n "$AVD" ] || die "chưa có máy ảo nào. Tạo trong Android Studio > Device Manager (Pixel, Android 14, Google APIs)."
  printf '%s\n' "$AVDS" | grep -qx "$AVD" || die "không có máy ảo '$AVD'. Hiện có: $(printf '%s\n' "$AVDS" | tr '\n' ' ')"
  say "==> Bật máy ảo $AVD (cửa sổ máy ảo sẽ hiện lên)"
  "$EMULATOR" -avd "$AVD" -no-snapshot-save -no-boot-anim >/dev/null 2>&1 &
  for _ in $(seq 1 60); do
    SERIAL="$(devices | grep '^emulator-' | head -1 || true)"
    [ -n "$SERIAL" ] && break
    sleep 2
  done
  [ -n "$SERIAL" ] || die "máy ảo không xuất hiện trong adb"
fi

case "$SERIAL" in emulator-*) IS_EMULATOR=true ;; *) IS_EMULATOR=false ;; esac
if [ "$IS_EMULATOR" = true ]; then
  booted=false
  for _ in $(seq 1 120); do
    if [ "$(adb -s "$SERIAL" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = 1 ]; then booted=true; break; fi
    sleep 2
  done
  [ "$booted" = true ] || die "máy ảo $SERIAL không khởi động xong sau 4 phút"
fi
MODEL="$(adb -s "$SERIAL" shell getprop ro.product.model | tr -d '\r')"
say "==> Thiết bị: $SERIAL ($MODEL, Android $(adb -s "$SERIAL" shell getprop ro.build.version.release | tr -d '\r'))"

# --- make sure GmsCore and Photos ReVanced are there -------------------------------------

has_pkg() { adb -s "$SERIAL" shell pm list packages "$1" | tr -d '\r' | grep -qx "package:$1"; }

need_app() { # package, apk file name, where to download it
  local pkg="$1" apk="$APK_DIR/$2" link="$3"
  has_pkg "$pkg" && return 0
  [ -f "$apk" ] || die "thiết bị chưa có $pkg. Tải APK ở $link rồi cài bằng: adb install <file.apk>
      (hoặc đặt file vào $apk để script tự cài)"
  say "==> Cài $pkg từ $apk"
  adb -s "$SERIAL" install -r "$apk" >/dev/null
}
need_app "$GMS_PKG" gmscore.apk "https://github.com/ReVanced/GmsCore/releases"
need_app "$PHOTOS_PKG" photos.apk "https://github.com/j-hc/revanced-magisk-module/releases (chọn file APK Google Photos, không chọn module)"

# --- sign out, so the app must log in again: a cached token is reused silently and never logged --

if [ "$IS_EMULATOR" = true ]; then
  # The stock Google Photos on emulator images is easy to open by mistake.
  adb -s "$SERIAL" shell pm uninstall --user 0 com.google.android.apps.photos >/dev/null 2>&1 || true
fi
if [ "$FRESH" = true ]; then
  if [ "$IS_EMULATOR" = false ]; then
    say "==> Sắp xoá dữ liệu Google Photos ReVanced và GmsCore trên $MODEL để đăng nhập lại."
    say "    Các app ReVanced khác (YouTube, YouTube Music) cũng bị đăng xuất. Ảnh trong máy không bị ảnh hưởng."
    say "    Không muốn xoá: Ctrl+C trong 5 giây, rồi chạy lại với --no-fresh."
    sleep 5
  fi
  say "==> Đăng xuất phiên cũ trên thiết bị"
  adb -s "$SERIAL" shell pm clear "$PHOTOS_PKG" >/dev/null
  adb -s "$SERIAL" shell pm clear "$GMS_PKG" >/dev/null
fi

adb -s "$SERIAL" logcat -c || true # ignore logins from before this run
OPENED=false
for _ in 1 2 3; do # right after `pm clear` the launcher can refuse for a moment
  if adb -s "$SERIAL" shell monkey -p "$PHOTOS_PKG" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1; then
    OPENED=true
    break
  fi
  sleep 2
done

say ""
say "==> Trên thiết bị, trong Google Photos ReVanced:"
[ "$OPENED" = true ] || say "    0. Mở app Google Photos ReVanced."
say "    1. Bấm Đăng nhập, đăng nhập ${EMAIL:-tài khoản Google bạn muốn upload vào}."
say "    2. Cho phép các quyền GmsCore hỏi, đợi app vào tới màn hình ảnh."
[ "$FRESH" = true ] || say "    App còn đăng nhập sẵn thì sẽ không bắt được gì: hãy đăng xuất rồi đăng nhập lại."
say "    Đang chờ tối đa $TIMEOUT giây (Ctrl+C để huỷ)..."
say ""

# --- capture ------------------------------------------------------------------------------
# logcat flows through a private FIFO into awk, which keeps the first matching login line.
# The token only ever lives in this process's memory.

EMAIL_KEY=""
if [ -n "$EMAIL" ]; then
  EMAIL_KEY="email=$(printf '%s' "$EMAIL" | tr '[:upper:]' '[:lower:]' | sed 's/@/%40/')&"
fi
WORK="$(mktemp -d)"
chmod 700 "$WORK"
mkfifo -m 600 "$WORK/log"
cleanup() { kill "${ADB_PID:-}" "${WATCH_PID:-}" 2>/dev/null || true; rm -rf "$WORK"; }
trap cleanup EXIT

adb -s "$SERIAL" logcat >"$WORK/log" 2>/dev/null &
ADB_PID=$!
( sleep "$TIMEOUT"; kill "$ADB_PID" 2>/dev/null ) &
WATCH_PID=$!

LINE="$(awk -v want="$EMAIL_KEY" '
  index($0, "androidId=") && index($0, "Token=") && index($0, "photos.native") {
    s = substr($0, index($0, "androidId="))
    sub(/[ \t\r]+$/, "", s)
    if (want != "" && index(tolower(s) "&", want) == 0) next
    print s
    exit
  }' <"$WORK/log" || true)"

case "$LINE" in
  androidId=*Email=*Token=*|androidId=*Token=*Email=*) ;;
  *) die "hết thời gian mà chưa thấy dòng đăng nhập${EMAIL:+ của $EMAIL}. Thử chạy lại script và đăng nhập lại trong Photos ReVanced." ;;
esac
ACCOUNT="$(printf '%s' "$LINE" | tr '&' '\n' | sed -n 's/^Email=//p' | sed 's/%40/@/')"
B64="$(printf '%s' "$LINE" | base64 | tr -d '\n')"
LINE=""

PROFILE_ARGS=(--user "$PHOTOUP_USER")
[ -n "$PROFILE_NAME" ] && PROFILE_ARGS+=(--name "$PROFILE_NAME")

case "$TO" in
  clipboard)
    printf '%s' "$B64" | "${COPY[@]}"
    say "==> Đã copy auth_data của $ACCOUNT (base64) vào clipboard."
    say "    Mở photoup > Upload vào > Thêm profile Google Photos, dán vào ô auth_data."
    say "    Sau khi thêm xong, copy một thứ khác để xoá token khỏi clipboard."
    ;;
  stdout)
    printf '%s\n' "$B64"
    say "==> Đã bắt được auth_data của $ACCOUNT."
    ;;
  local)
    printf '%s\n' "$B64" | (cd "$ROOT" && uv run python -m app add-profile "${PROFILE_ARGS[@]}")
    ;;
  docker)
    printf '%s\n' "$B64" | (cd "$ROOT" && docker compose exec -T "$COMPOSE_SERVICE" python -m app add-profile "${PROFILE_ARGS[@]}" >&2)
    ;;
esac
