#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SOURCE_DIR="${ROOT_DIR}/approvalProcesses"
BASE_URL=""
USERNAME=""
PASSWORD=""
CLIENT_ID="nexuscrm-ui"
CLIENT_SECRET="nexuscrm-ui-internal"
POLL_SECONDS=60

usage() {
  cat <<'EOF'
Usage:
  scripts/deploy_approval_processes.sh \
    --base-url https://host \
    --username admin@example.invalid \
    --password 'secret' \
    [--source-dir /abs/path/to/approvalProcesses] \
    [--client-id nexuscrm-ui] \
    [--client-secret nexuscrm-ui-internal] \
    [--poll-seconds 60]

Deploy all *.approvalProcess files from SOURCE_DIR through the Metadata Deploy API.
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --base-url)
      BASE_URL="${2:-}"
      shift 2
      ;;
    --username)
      USERNAME="${2:-}"
      shift 2
      ;;
    --password)
      PASSWORD="${2:-}"
      shift 2
      ;;
    --source-dir)
      SOURCE_DIR="${2:-}"
      shift 2
      ;;
    --client-id)
      CLIENT_ID="${2:-}"
      shift 2
      ;;
    --client-secret)
      CLIENT_SECRET="${2:-}"
      shift 2
      ;;
    --poll-seconds)
      POLL_SECONDS="${2:-}"
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "unknown argument: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

: "${BASE_URL:?--base-url is required}"
: "${USERNAME:?--username is required}"
: "${PASSWORD:?--password is required}"

if [[ ! -d "${SOURCE_DIR}" ]]; then
  echo "source dir not found: ${SOURCE_DIR}" >&2
  exit 1
fi

shopt -s nullglob
approval_files=("${SOURCE_DIR}"/*.approvalProcess)
shopt -u nullglob

if (( ${#approval_files[@]} == 0 )); then
  echo "no .approvalProcess files found in ${SOURCE_DIR}" >&2
  exit 1
fi

tmp_dir="$(mktemp -d)"
trap 'rm -rf "${tmp_dir}"' EXIT

login_resp="$(
  curl -fsS -X POST "${BASE_URL%/}/services/oauth2/token" \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'grant_type=password' \
    --data-urlencode "username=${USERNAME}" \
    --data-urlencode "password=${PASSWORD}" \
    --data-urlencode "client_id=${CLIENT_ID}" \
    --data-urlencode "client_secret=${CLIENT_SECRET}"
)"

token="$(jq -r '.access_token // empty' <<<"${login_resp}")"
if [[ -z "${token}" ]]; then
  echo "login failed" >&2
  echo "${login_resp}" >&2
  exit 1
fi

package_xml="${tmp_dir}/package.xml"
zip_path="${tmp_dir}/package.zip"
deploy_response="${tmp_dir}/deploy_response.json"

{
  cat <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
EOF
  for file in "${approval_files[@]}"; do
    member="$(basename "${file}" .approvalProcess)"
    printf '        <members>%s</members>\n' "${member}"
  done
  cat <<'EOF'
        <name>ApprovalProcess</name>
    </types>
    <version>63.0</version>
</Package>
EOF
} > "${package_xml}"

python3 - "${zip_path}" "${package_xml}" "${approval_files[@]}" <<'PY'
import sys
import zipfile
from pathlib import Path

zip_path = Path(sys.argv[1])
package_xml = Path(sys.argv[2])
approval_files = [Path(p) for p in sys.argv[3:]]

with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
    zf.write(package_xml, "package.xml")
    for file_path in approval_files:
        zf.write(file_path, f"approvalProcesses/{file_path.name}")
PY

curl -fsS -o "${deploy_response}" \
  -X POST "${BASE_URL%/}/services/data/v63.0/metadata/deployRequest" \
  -H "Authorization: Bearer ${token}" \
  -F 'entity_content={"deployOptions":{"allowMissingFiles":false,"autoUpdatePackage":false,"checkOnly":false,"ignoreWarnings":true,"rollbackOnError":true,"singlePackage":true}};type=application/json' \
  -F "file=@${zip_path};type=application/zip"

deploy_id="$(jq -r '.id // empty' "${deploy_response}")"
if [[ -z "${deploy_id}" ]]; then
  echo "metadata deploy submit failed" >&2
  cat "${deploy_response}" >&2
  exit 1
fi

echo "deploy_id=${deploy_id}"

poll_response="${tmp_dir}/poll_response.json"
for _ in $(seq 1 "${POLL_SECONDS}"); do
  curl -fsS \
    -H "Authorization: Bearer ${token}" \
    "${BASE_URL%/}/services/data/v63.0/metadata/deployRequest/${deploy_id}?includeDetails=true" \
    > "${poll_response}"

  done_flag="$(jq -r '.deployResult.done' "${poll_response}")"
  poll_status="$(jq -r '.deployResult.status' "${poll_response}")"
  poll_success="$(jq -r '.deployResult.success // empty' "${poll_response}")"
  echo "poll: status=${poll_status} done=${done_flag} success=${poll_success}"

  if [[ "${done_flag}" == "true" ]]; then
    break
  fi
  sleep 1
done

jq '{status:.deployResult.status, done:.deployResult.done, success:.deployResult.success, errorMessage:.deployResult.errorMessage, componentFailures:(.deployResult.details.componentFailures // null)}' "${poll_response}"

if [[ "$(jq -r '.deployResult.done' "${poll_response}")" != "true" ]]; then
  echo "metadata deployment timed out" >&2
  exit 1
fi

if [[ "$(jq -r '.deployResult.success // false' "${poll_response}")" != "true" ]]; then
  echo "metadata deployment failed" >&2
  exit 1
fi
