#!/usr/bin/env bash
# Host wrapper: build CI image + run pipeline with log capture.
#
# Usage:
#   ./scripts/docker-build-runner.sh --build-id 42 --workdir /path/to/repo
#
# Env: see ci-build-and-publish-ota.sh
# When invoked from PHP (BUILD_LOG_STDOUT_ONLY=1), log via stdout only — parent redirects to build.log.
#
# Parallel builds: set BUILD_ISOLATE_SOURCE=1 (default from builder) to shallow-clone into
#   $OUT_DIR/src instead of mutating the shared deploy checkout.
# Per-build step logs: $OUT_DIR/docker-build.log, $OUT_DIR/docker-run.log (also echoed to build.log).
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
IMAGE="${ANDROIDCAST_CI_IMAGE:-androidcast-ci:latest}"
CI_VERSION="${ANDROIDCAST_CI_VERSION:-00.01.00.1000}"
BUILD_ID="${BUILD_ID:-local}"
LOG_FILE="${BUILD_LOG_FILE:-${ROOT}/out/builds/${BUILD_ID}/build.log}"
WORK_DIR="${BUILD_WORK_DIR:-${ROOT}}"
OUT_DIR="${BUILD_OUT_DIR:-${WORK_DIR}/out/builds/${BUILD_ID}}"
LOG_STDOUT_ONLY="${BUILD_LOG_STDOUT_ONLY:-0}"
ISOLATE_SOURCE="${BUILD_ISOLATE_SOURCE:-1}"
CLONE_DEPTH="${BUILD_GIT_DEPTH:-1}"
SOURCE_DIR="${OUT_DIR}/src"
DOCKER_WORKSPACE="${WORK_DIR}"
CONTAINER_NAME="androidcast-bld-${BUILD_ID}"
RESUME_FROM="${BUILD_RESUME_FROM:-}"
REUSE_SRC_DIR="${BUILD_REUSE_SRC_DIR:-}"
SSH_DEBUG="${BUILD_SSH_DEBUG:-0}"
SSH_DEBUG_SECONDS="${BUILD_SSH_DEBUG_SECONDS:-7200}"

mkdir -p "$(dirname "$LOG_FILE")" "$OUT_DIR"

log() {
  if [[ "$LOG_STDOUT_ONLY" == "1" ]]; then
    echo "$@"
  else
    echo "$@" | tee -a "$LOG_FILE"
  fi
}

run_logged() {
  if [[ "$LOG_STDOUT_ONLY" == "1" ]]; then
    "$@"
  else
    "$@" 2>&1 | tee -a "$LOG_FILE"
  fi
}

# Mirror command output to build.log (stdout) and a per-build step file for parallel debugging.
run_step() {
  local step_name="$1"
  shift
  local step_log="${OUT_DIR}/${step_name}.log"
  log "[runner] step ${step_name} -> ${step_log}"
  "$@" 2>&1 | tee -a "${step_log}"
}

on_runner_err() {
  local ec=$?
  log "[runner] ERROR: command failed (exit ${ec}) at ${BASH_SOURCE[0]}:${LINENO}: ${BASH_COMMAND}"
  if command -v docker >/dev/null 2>&1; then
    if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qx "${CONTAINER_NAME}"; then
      log "[runner] docker logs ${CONTAINER_NAME} (last 100 lines):"
      docker logs --tail 100 "${CONTAINER_NAME}" 2>&1 | while IFS= read -r line; do log "$line"; done
    fi
  fi
  echo "${ec}" > "${OUT_DIR}/runner.exit"
  exit "${ec}"
}

on_runner_exit() {
  local ec=$?
  echo "${ec}" > "${OUT_DIR}/runner.exit"
}

trap on_runner_err ERR
trap on_runner_exit EXIT

# Git 2.35+ refuses repos owned by another uid (PHP-FPM nginx vs deploy user).
git_safe() {
  git -c safe.directory="${WORK_DIR}" "$@"
}

resolve_git_remote() {
  if [[ -n "${GIT_REMOTE:-}" ]]; then
    echo "$GIT_REMOTE"
    return
  fi
  if [[ -d "${WORK_DIR}/.git" ]]; then
    git_safe -C "${WORK_DIR}" remote get-url origin 2>/dev/null || true
  fi
}

prepare_isolated_source() {
  local ref="${GIT_REF:-}"
  local remote sha
  remote="$(resolve_git_remote)"
  if [[ -z "$ref" || -z "$remote" ]]; then
    log "[runner] isolate: skip (need GIT_REF and origin remote)"
    return 0
  fi

  rm -rf "${SOURCE_DIR}"
  mkdir -p "${SOURCE_DIR}"

  log "[runner] isolate: shallow clone depth=${CLONE_DEPTH} ref=${ref} remote=${remote} -> ${SOURCE_DIR}"
  if git clone --depth "${CLONE_DEPTH}" --recurse-submodules --shallow-submodules \
      --single-branch --branch "${ref}" "${remote}" "${SOURCE_DIR}"; then
    :
  else
    log "[runner] isolate: branch clone failed; clone default branch then checkout ${ref}"
    run_step git-clone git clone --depth "${CLONE_DEPTH}" --recurse-submodules --shallow-submodules \
      "${remote}" "${SOURCE_DIR}"
    run_step git-fetch git -c safe.directory="${SOURCE_DIR}" -C "${SOURCE_DIR}" fetch --depth "${CLONE_DEPTH}" origin "${ref}"
    run_step git-checkout git -c safe.directory="${SOURCE_DIR}" -C "${SOURCE_DIR}" checkout "${ref}"
  fi

  if [[ -n "${GIT_SHA:-}" ]]; then
    log "[runner] isolate: checkout pin GIT_SHA=${GIT_SHA}"
    run_step git-pin git -c safe.directory="${SOURCE_DIR}" -C "${SOURCE_DIR}" checkout "${GIT_SHA}"
  fi

  run_step git-rev-parse git -c safe.directory="${SOURCE_DIR}" -C "${SOURCE_DIR}" rev-parse HEAD
  DOCKER_WORKSPACE="${SOURCE_DIR}"
}

sync_shared_checkout() {
  if [[ -z "${GIT_REF:-}" ]] || [[ ! -d "${WORK_DIR}/.git" ]]; then
    return 0
  fi
  log "[runner] syncing shared checkout ref=${GIT_REF}"
  run_logged git_safe -C "${WORK_DIR}" fetch origin --prune || true
  run_logged git_safe -C "${WORK_DIR}" checkout "${GIT_REF}"
  run_logged git_safe -C "${WORK_DIR}" pull --rebase origin "${GIT_REF}" || true
  if [[ -n "${GIT_SHA:-}" ]]; then
    run_logged git_safe -C "${WORK_DIR}" checkout "${GIT_SHA}"
  fi
  run_logged git_safe -C "${WORK_DIR}" rev-parse HEAD
}

should_skip_git() {
  [[ "$RESUME_FROM" == "docker-build" || "$RESUME_FROM" == "docker-run" ]]
}

should_skip_docker_build() {
  [[ "$RESUME_FROM" == "docker-run" ]] && docker image inspect "$IMAGE" >/dev/null 2>&1
}

reuse_source_tree() {
  if [[ -n "$REUSE_SRC_DIR" && -d "${REUSE_SRC_DIR}/.git" ]]; then
    log "[runner] reuse: source tree from ${REUSE_SRC_DIR}"
    rm -rf "${SOURCE_DIR}"
    mkdir -p "${SOURCE_DIR}"
    cp -a "${REUSE_SRC_DIR}/." "${SOURCE_DIR}/"
    DOCKER_WORKSPACE="${SOURCE_DIR}"
    return 0
  fi
  return 1
}

run_git_step() {
  if [[ "$ISOLATE_SOURCE" == "1" ]]; then
    prepare_isolated_source
  else
    sync_shared_checkout
    DOCKER_WORKSPACE="${WORK_DIR}"
  fi
}

start_ssh_debug_container() {
  log "[runner] SSH debug: starting container ${CONTAINER_NAME} (${SSH_DEBUG_SECONDS}s)"
  docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true
  run_step docker-run-ssh docker run -d --name "${CONTAINER_NAME}" \
    -v "${DOCKER_WORKSPACE}:/workspace" \
    -v "${ROOT}/out:/workspace/out" \
    -w /workspace \
    -e BUILD_ID="${BUILD_ID}" \
    -e OUT_DIR="/workspace/out/builds/${BUILD_ID}" \
    "$IMAGE" \
    sleep "${SSH_DEBUG_SECONDS}"
  printf '%s\n' 'ssh_debug' > "${OUT_DIR}/runner.mode"
  local host
  host="$(hostname -f 2>/dev/null || hostname 2>/dev/null || echo builder)"
  log "[runner] SSH debug: session open on ${host}"
  log "[runner] SSH debug:   ssh <you>@${host}"
  log "[runner] SSH debug:   docker exec -it ${CONTAINER_NAME} bash"
  log "[runner] SSH debug: workspace is /workspace inside the container"
  log "[runner] finished build_id=${BUILD_ID} (ssh debug — stop build or wait ${SSH_DEBUG_SECONDS}s)"
}

log "[runner] image=${IMAGE} ci_version=${CI_VERSION} build_id=${BUILD_ID} container=${CONTAINER_NAME}"
log "[runner] workdir=${WORK_DIR} out=${OUT_DIR} isolate=${ISOLATE_SOURCE} resume=${RESUME_FROM:-full}"

if should_skip_git; then
  if reuse_source_tree; then
    log "[runner] resume: skipped git (reused source)"
  elif [[ "$ISOLATE_SOURCE" != "1" && -d "${WORK_DIR}/.git" ]]; then
    DOCKER_WORKSPACE="${WORK_DIR}"
    log "[runner] resume: skipped git (shared workdir ${WORK_DIR})"
  else
    log "[runner] resume: no reusable source; running git step"
    run_git_step
  fi
else
  run_git_step
fi

log "[runner] docker workspace=${DOCKER_WORKSPACE}"

if should_skip_docker_build; then
  log "[runner] resume: skipped docker-build (image ${IMAGE} exists)"
else
  run_step docker-build docker build \
    --build-arg ANDROIDCAST_CI_VERSION="${CI_VERSION}" \
    -t "$IMAGE" \
    -f "${ROOT}/Dockerfile" \
    "${ROOT}"
fi

if [[ "$SSH_DEBUG" == "1" ]]; then
  start_ssh_debug_container
  exit 0
fi

run_step docker-run docker run --rm --name "${CONTAINER_NAME}" \
  -v "${DOCKER_WORKSPACE}:/workspace" \
  -v "${ROOT}/out:/workspace/out" \
  -w /workspace \
  -e BUILD_ID="${BUILD_ID}" \
  -e OUT_DIR="/workspace/out/builds/${BUILD_ID}" \
  -e OTA_BASE_URL="${OTA_BASE_URL:-}" \
  -e OTA_CHANNEL="${OTA_CHANNEL:-staging}" \
  -e GRADLE_TASK="${GRADLE_TASK:-assembleDebug}" \
  -e RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-1}" \
  -e RUN_NATIVE="${RUN_NATIVE:-1}" \
  -e AUTO_OTA="${AUTO_OTA:-0}" \
  -e GIT_REF="${GIT_REF:-}" \
  -e GIT_SHA="${GIT_SHA:-}" \
  -e RELEASE_NOTES="${RELEASE_NOTES:-}" \
  "$IMAGE" \
  ./scripts/ci-build-and-publish-ota.sh

log "[runner] finished build_id=${BUILD_ID}"
