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

URL="https://dl.duosecurity.com/duo-win-login-latest.exe"
OUT="/opt/autopilot/applications/DUO/duo-win-login-latest.exe"
MANIFEST="/opt/autopilot/applications/DUO/duo-winlogon-manifest.json"
# Promotion delay in days for prod to catch up to UAT
PROMOTION_DAYS=${PROMOTION_DAYS:-14}

download() {
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL --retry 3 -o "$OUT" "$URL"
  elif command -v wget >/dev/null 2>&1; then
    wget -qO "$OUT" "$URL"
  else
    echo "Error: require curl or wget" >&2
    exit 1
  fi
}

get_version() {
  # Prefer accurate PE metadata via exiftool if available
  local version=""
  if command -v exiftool >/dev/null 2>&1; then
    version=$(exiftool -s -s -s -ProductVersion "$OUT" 2>/dev/null || true)
    if [[ -z "$version" ]]; then
      version=$(exiftool -s -s -s -FileVersion "$OUT" 2>/dev/null || true)
    fi
  fi

  # Next: infer from HTTP header filename (often contains major.minor.patch)
  if [[ -z "$version" ]]; then
    local headers filename
    if command -v curl >/dev/null 2>&1; then
      headers=$(curl -sSLI "$URL" || true)
    elif command -v wget >/dev/null 2>&1; then
      headers=$(wget --server-response --spider "$URL" 2>&1 || true)
    fi
    filename=$(printf '%s\n' "$headers" | tr -d '\r' | awk -F'filename=' 'tolower($0) ~ /content-disposition:/ {print $2}' | awk -F';' '{print $1}' | tr -d '"' | tail -n1)
    if [[ -n "$filename" ]]; then
      version=$(printf '%s\n' "$filename" | grep -Eo '[0-9]+(\.[0-9]+){2,3}' | head -n1 || true)
    fi
  fi

  # Last resort: scan binary strings (may be noisy)
  if [[ -z "$version" ]] && command -v strings >/dev/null 2>&1; then
    version=$(strings "$OUT" | grep -Eo '\b[0-9]+(\.[0-9]+){2,3}(?:-[A-Za-z0-9]+)?\b' | head -n1 || true)
  fi

  if [[ -z "$version" ]]; then
    echo "unknown"
  else
    echo "$version"
  fi
}

to_epoch() {
  # Convert DD/MM/YYYY to epoch seconds; returns 0 if empty/unparseable
  local d="$1"
  if [[ -z "$d" ]]; then
    echo 0
    return 0
  fi
  # Reorder to YYYY-MM-DD for GNU date
  local iso
  iso=$(awk -F'/' '{printf "%04d-%02d-%02d", $3, $2, $1}' <<<"$d" 2>/dev/null || true)
  date -d "$iso" +%s 2>/dev/null || echo 0
}

write_manifest_json() {
  local latest_version="$1"
  local latest_date="$2"
  local uat_version="$3"
  local uat_date="$4"
  local prod_version="$5"
  local prod_date="$6"
  cat > "$MANIFEST" <<JSON
{
  "latest_version": "$latest_version",
  "latest_version_date_grabbed": "$latest_date",
  "uat_version": "$uat_version",
  "uat_version_date_grabbed": "$uat_date",
  "prod_version": "$prod_version",
  "prod_version_date_grabbed": "$prod_date"
}
JSON
}

read_manifest_field() {
  # Lightweight JSON field reader for simple string values
  # Usage: read_manifest_field field_name
  local field_name="$1"
  if [[ ! -f "$MANIFEST" ]]; then
    return 0
  fi
  sed -nE 's/.*"'"$field_name"'"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "$MANIFEST" | tail -n1
}

main() {
  download
  local version
  version=$(get_version)
  local short_version
  short_version="${version%%-*}"
  local grab_date
  grab_date=$(date +"%d/%m/%Y")
  echo "Duo Windows Logon version: $short_version"

  # Defaults for first run or no jq
  local latest_v="$short_version" latest_d="$grab_date"
  local uat_v="$short_version" uat_d="$grab_date"
  local prod_v="$short_version" prod_d="$grab_date"

  if [[ "$short_version" == "unknown" || -z "$short_version" ]]; then
    echo "Failed to determine version. Leaving existing manifest unchanged." >&2
    rm -f "$OUT"
    exit 1
  fi

  if [[ -f "$MANIFEST" ]] && command -v jq >/dev/null 2>&1; then
    # Load existing values
    latest_v=$(jq -r '.latest_version // empty' "$MANIFEST")
    latest_d=$(jq -r '.latest_version_date_grabbed // empty' "$MANIFEST")
    uat_v=$(jq -r '.uat_version // empty' "$MANIFEST")
    uat_d=$(jq -r '.uat_version_date_grabbed // empty' "$MANIFEST")
    prod_v=$(jq -r '.prod_version // empty' "$MANIFEST")
    prod_d=$(jq -r '.prod_version_date_grabbed // empty' "$MANIFEST")

    # Update latest to current
    latest_v="$short_version"
    latest_d="$grab_date"

    # UAT tracks latest automatically
    if [[ "$short_version" != "$uat_v" ]]; then
      uat_v="$short_version"
      uat_d="$grab_date"
    fi

    # Promote prod to UAT if PROD is older than UAT and delay elapsed since UAT update
    if [[ -n "$prod_v" && -n "$uat_d" && "$prod_v" != "$uat_v" ]]; then
      local now_epoch uat_epoch days_since
      now_epoch=$(date +%s)
      uat_epoch=$(to_epoch "$uat_d")
      if [[ "$uat_epoch" -gt 0 ]]; then
        days_since=$(( (now_epoch - uat_epoch) / 86400 ))
      else
        days_since=0  # Don't promote if we can't parse the date
      fi
      if (( days_since >= PROMOTION_DAYS )); then
        prod_v="$uat_v"
        prod_d="$grab_date"
      fi
    fi
  elif [[ -f "$MANIFEST" ]]; then
    # If jq is missing, read previous values with a simple parser
    latest_v=$(read_manifest_field latest_version)
    latest_d=$(read_manifest_field latest_version_date_grabbed)
    uat_v=$(read_manifest_field uat_version)
    uat_d=$(read_manifest_field uat_version_date_grabbed)
    prod_v=$(read_manifest_field prod_version)
    prod_d=$(read_manifest_field prod_version_date_grabbed)

    # Update latest to current
    latest_v="$short_version"
    latest_d="$grab_date"

    # UAT tracks latest automatically
    if [[ "$short_version" != "$uat_v" ]]; then
      uat_v="$short_version"
      uat_d="$grab_date"
    fi

    # Promote prod to UAT if PROD is older than UAT and delay elapsed since UAT update
    if [[ -n "$prod_v" && -n "$uat_d" && "$prod_v" != "$uat_v" ]]; then
      local now_epoch uat_epoch days_since
      now_epoch=$(date +%s)
      uat_epoch=$(to_epoch "$uat_d")
      if [[ "$uat_epoch" -gt 0 ]]; then
        days_since=$(( (now_epoch - uat_epoch) / 86400 ))
      else
        days_since=0  # Don't promote if we can't parse the date
      fi
      if (( days_since >= PROMOTION_DAYS )); then
        prod_v="$uat_v"
        prod_d="$grab_date"
      fi
    fi
  else
    # Manifest absent: initialize all tracks to current
    :
  fi

  write_manifest_json "$latest_v" "$latest_d" "$uat_v" "$uat_d" "$prod_v" "$prod_d"
  rm -f "$OUT"
}

main "$@"

