#!/usr/bin/env bash
# test-syno-slice.sh — verify Synology Recording.Download offsetTimeMs/playTimeMs slicing
#
# Usage:
#   ./test-syno-slice.sh "2026-05-02 22:20:30"          # 2 min before, 1 min after (defaults)
#   ./test-syno-slice.sh "2026-05-02 22:20:30" 120 60   # explicit pre/post seconds
#   COMMISSION_TS=1746224430 ./test-syno-slice.sh        # raw UTC unix timestamp
#
# Requires: curl, jq, date (GNU or BSD).

set -euo pipefail

# ── CONFIG ─────────────────────────────────────────────────────────────────
NAS_URL="${SYNOLOGY_URL:-http://192.168.1.56:5000}"
NAS_USER="${SYNOLOGY_USER:-adminyk}"
NAS_PASS="${SYNOLOGY_PASSWORD:-X63Yipmz??B^w}"
CAMERA_IDS="${SYNOLOGY_CAMERA_IDS:-2}"     # comma-sep, e.g. "2,7"
PRE_SECONDS="${2:-120}"                    # seconds before the timestamp
POST_SECONDS="${3:-60}"                    # seconds after
OUT_DIR="${OUT_DIR:-./syno-test}"
API_VERSION=6
# ───────────────────────────────────────────────────────────────────────────

CYAN='\033[96m'; GRN='\033[92m'; RED='\033[91m'; DIM='\033[90m'; END='\033[0m'

# Resolve the commission unix timestamp
if [[ -n "${COMMISSION_TS:-}" ]]; then
  TS_UNIX="$COMMISSION_TS"
elif [[ -n "${1:-}" ]]; then
  # Try GNU date first, fall back to BSD date
  TS_UNIX=$(date -u -d "$1" +%s 2>/dev/null || date -u -j -f "%Y-%m-%d %H:%M:%S" "$1" +%s 2>/dev/null || true)
  if [[ -z "$TS_UNIX" ]]; then
    echo "Could not parse timestamp: $1" >&2
    echo "Use ISO-ish format like '2026-05-02 22:20:30' or pass COMMISSION_TS=… as a unix epoch." >&2
    exit 1
  fi
else
  echo "Pass a timestamp: ./test-syno-slice.sh \"2026-05-02 22:20:30\"" >&2
  exit 1
fi

FROM_TS=$(( TS_UNIX - PRE_SECONDS ))
TO_TS=$(( TS_UNIX + POST_SECONDS ))
WINDOW_MS=$(( (PRE_SECONDS + POST_SECONDS) * 1000 ))

mkdir -p "$OUT_DIR"

echo -e "${CYAN}Window:${END} $(date -u -r "$FROM_TS" 2>/dev/null || date -u -d "@$FROM_TS")  →  $(date -u -r "$TO_TS" 2>/dev/null || date -u -d "@$TO_TS")  (UTC)"
echo -e "${CYAN}Cameras:${END} $CAMERA_IDS"
echo -e "${CYAN}Commission ts:${END} $TS_UNIX  (pre=${PRE_SECONDS}s post=${POST_SECONDS}s → ${WINDOW_MS}ms slice)"
echo

# ── 1) LOGIN ───────────────────────────────────────────────────────────────
echo -e "${DIM}→ Logging in…${END}"
LOGIN_JSON=$(curl -sS -G "${NAS_URL}/webapi/entry.cgi" \
  --data-urlencode "api=SYNO.API.Auth" \
  --data-urlencode "method=login" \
  --data-urlencode "version=7" \
  --data-urlencode "account=${NAS_USER}" \
  --data-urlencode "passwd=${NAS_PASS}" \
  --data-urlencode "session=SurveillanceStation" \
  --data-urlencode "format=sid")

SID=$(echo "$LOGIN_JSON" | jq -r '.data.sid // empty')
if [[ -z "$SID" ]]; then
  echo -e "${RED}Login failed:${END} $LOGIN_JSON" >&2
  exit 1
fi
echo -e "  ${GRN}✓${END} sid=${SID:0:12}…"

cleanup() {
  curl -sS -G "${NAS_URL}/webapi/entry.cgi" \
    --data-urlencode "api=SYNO.API.Auth" \
    --data-urlencode "method=logout" \
    --data-urlencode "version=7" \
    --data-urlencode "session=SurveillanceStation" \
    --data-urlencode "_sid=${SID}" >/dev/null 2>&1 || true
}
trap cleanup EXIT

# ── 2) LIST RECORDINGS ─────────────────────────────────────────────────────
echo -e "${DIM}→ Listing recordings overlapping window…${END}"
LIST_JSON=$(curl -sS -G "${NAS_URL}/webapi/entry.cgi" \
  --data-urlencode "api=SYNO.SurveillanceStation.Recording" \
  --data-urlencode "method=List" \
  --data-urlencode "version=${API_VERSION}" \
  --data-urlencode "cameraIds=${CAMERA_IDS}" \
  --data-urlencode "fromTime=${FROM_TS}" \
  --data-urlencode "toTime=${TO_TS}" \
  --data-urlencode "_sid=${SID}")

if [[ "$(echo "$LIST_JSON" | jq -r '.success')" != "true" ]]; then
  echo -e "${RED}List failed:${END} $LIST_JSON" >&2
  exit 1
fi

# Try both shapes: data.recordings, data.events
COUNT=$(echo "$LIST_JSON" | jq '(.data.recordings // .data.events // []) | length')
if [[ "$COUNT" -eq 0 ]]; then
  echo -e "${RED}No recordings in window.${END}"
  exit 1
fi
echo -e "  ${GRN}✓${END} ${COUNT} recording(s) in window"

# Dump the raw shape of the first recording so we can see the actual
# field names this DSM/SS build returns. Recording objects on different
# Surveillance Station versions use any of: startTime, start_time,
# recordingTime, recordTime, time, recordTimeBegin, …
echo -e "${DIM}→ Raw first recording object (for field inspection):${END}"
echo "$LIST_JSON" | jq '(.data.recordings // .data.events // [])[0]' | sed 's/^/    /'
echo

# DSM/SS on this build doesn't expose a startTime JSON field — instead
# the recording's start UTC timestamp (in milliseconds) is embedded in
# the filePath, e.g.:
#   Kommissionierung-1-20260503-002000-1777760400027-7.mp4
#                                       ^^^^^^^^^^^^^ ms
# We extract the trailing 13-digit number before -<N>.mp4 and divide
# by 1000 to get unix seconds. sizeByte is reported in MB on this build,
# not bytes — handy for picking but not load-bearing here.
CHOSEN=$(echo "$LIST_JSON" | jq --argjson ts "$TS_UNIX" '
  def parse_start_from_path:
    (capture("(?<ms>[0-9]{13})-[0-9]+\\.mp4$") | (.ms | tonumber / 1000 | floor))
    // 0;
  (.data.recordings // .data.events // [])
  | map({
      id:        (.id // .recordId),
      filePath:  (.filePath // ""),
      sizeMB:    (.sizeByte // 0),
      startTime: (try (.filePath | parse_start_from_path) catch 0)
    })
  | map(. + {stopTime: 0})
  | sort_by(((.startTime - $ts)) | if . < 0 then -. else . end)
  | .[0]')

REC_ID=$(echo "$CHOSEN" | jq -r '.id')
REC_START=$(echo "$CHOSEN" | jq -r '.startTime')
REC_PATH=$(echo "$CHOSEN" | jq -r '.filePath')
REC_SIZE_MB=$(echo "$CHOSEN" | jq -r '.sizeMB')

echo -e "  ${CYAN}→ chosen id=${REC_ID}${END}"
echo -e "      path  : ${REC_PATH}"
echo -e "      start : ${REC_START} ($(date -u -r "$REC_START" 2>/dev/null || date -u -d "@$REC_START"))"
echo -e "      size  : ~${REC_SIZE_MB} MB (per List response)"

if [[ "$REC_START" -lt 1000000000 ]]; then
  echo -e "  ${RED}!${END} startTime still looks bogus (${REC_START}). Inspect the dump above and tell me which key holds the start timestamp."
  echo -e "  ${DIM}Skipping slice computation — using offsetTimeMs=0, playTimeMs=${WINDOW_MS} so we can still test that the params are accepted.${END}"
  OFFSET_MS=0
  PLAY_MS=${WINDOW_MS}
else
  SLICE_START=$(( FROM_TS > REC_START ? FROM_TS : REC_START ))
  OFFSET_MS=$(( (SLICE_START - REC_START) * 1000 ))
  PLAY_MS=$(( (TO_TS - SLICE_START) * 1000 ))
  [[ $PLAY_MS -lt 1000 ]] && PLAY_MS=1000
fi

echo -e "  ${CYAN}→ slice${END} offsetTimeMs=${OFFSET_MS}  playTimeMs=${PLAY_MS}"
echo

# ── 3a) DOWNLOAD FULL FILE (for comparison) ────────────────────────────────
FULL_FILE="${OUT_DIR}/cam${CAMERA_IDS}_id${REC_ID}_FULL.mp4"
echo -e "${DIM}→ Downloading FULL file…${END}"
curl -sS -G "${NAS_URL}/webapi/entry.cgi" \
  --data-urlencode "api=SYNO.SurveillanceStation.Recording" \
  --data-urlencode "method=Download" \
  --data-urlencode "version=${API_VERSION}" \
  --data-urlencode "id=${REC_ID}" \
  --data-urlencode "mountId=0" \
  --data-urlencode "_sid=${SID}" \
  -o "$FULL_FILE" -w "  HTTP %{http_code}  ${GRN}✓${END} %{size_download} bytes  in %{time_total}s\n"

# ── 3b) DOWNLOAD SLICED FILE ───────────────────────────────────────────────
SLICE_FILE="${OUT_DIR}/cam${CAMERA_IDS}_id${REC_ID}_SLICE_${OFFSET_MS}_${PLAY_MS}.mp4"
echo -e "${DIM}→ Downloading SLICED file (offsetTimeMs=${OFFSET_MS}, playTimeMs=${PLAY_MS})…${END}"
curl -sS -G "${NAS_URL}/webapi/entry.cgi" \
  --data-urlencode "api=SYNO.SurveillanceStation.Recording" \
  --data-urlencode "method=Download" \
  --data-urlencode "version=${API_VERSION}" \
  --data-urlencode "id=${REC_ID}" \
  --data-urlencode "mountId=0" \
  --data-urlencode "offsetTimeMs=${OFFSET_MS}" \
  --data-urlencode "playTimeMs=${PLAY_MS}" \
  --data-urlencode "_sid=${SID}" \
  -o "$SLICE_FILE" -w "  HTTP %{http_code}  ${GRN}✓${END} %{size_download} bytes  in %{time_total}s\n"

# ── 4) REPORT ──────────────────────────────────────────────────────────────
FULL_SIZE=$(wc -c <"$FULL_FILE" | tr -d ' ')
SLICE_SIZE=$(wc -c <"$SLICE_FILE" | tr -d ' ')

human() {
  local n=$1
  awk -v n="$n" 'BEGIN{
    s="B KB MB GB TB"; split(s,u," ");
    i=1; while (n>=1024 && i<5) {n=n/1024; i++}
    printf "%.2f %s", n, u[i]
  }'
}

echo
echo "──────────────────────────────────────────────"
echo -e "  FULL  : $(human "$FULL_SIZE")  →  $FULL_FILE"
echo -e "  SLICE : $(human "$SLICE_SIZE")  →  $SLICE_FILE"
if [[ "$FULL_SIZE" -gt 0 ]]; then
  RATIO=$(awk -v s="$SLICE_SIZE" -v f="$FULL_SIZE" 'BEGIN{printf "%.1f%%", (s/f)*100}')
  echo -e "  Slice is ${CYAN}${RATIO}${END} of full"
fi
echo "──────────────────────────────────────────────"

# Quick sanity check — both files should be playable MP4s
file "$FULL_FILE" "$SLICE_FILE" 2>/dev/null || true

# If a "download" came back as HTML/JSON/text, dump it so we can see the
# Synology-side error message instead of just an opaque byte count.
for f in "$FULL_FILE" "$SLICE_FILE"; do
  if ! file "$f" | grep -qi 'iso media\|mp4\|video'; then
    echo
    echo -e "${RED}!${END} $f is NOT an MP4 — server said:"
    head -c 1500 "$f" | sed 's/^/    /'
    echo
  fi
done
