#!/usr/bin/env bash

set -euo pipefail

# Clean up zip files on exit.
cleanup() {
  if [[ -n "${zip_file:-}" ]] && [[ -f "$zip_file" ]]; then
    rm -f "$zip_file"
  fi
}
trap cleanup EXIT

# Parse arguments.
DIR=""
VERSION=""
STATIC=false

while [[ $# -gt 0 ]]; do
  case $1 in
    --dir)
      DIR="$2"
      shift 2
      ;;
    --version)
      VERSION="$2"
      shift 2
      ;;
    --static)
      STATIC=true
      shift
      ;;
    *)
      echo "Unknown argument: $1"
      echo "Usage: $0 --dir <frameworks_dir> --version <version> [--static]"
      exit 1
      ;;
  esac
done

if [[ -z "$DIR" ]] || [[ -z "$VERSION" ]]; then
  echo "Error: Both --dir and --version are required"
  echo "Usage: $0 --dir <frameworks_dir> --version <version> [--static]"
  exit 1
fi

# Ensure DIR is absolute or relative to current directory.
if [[ ! "$DIR" = /* ]]; then
  DIR="$(pwd)/$DIR"
fi

# Determine framework type.
if [[ "$STATIC" == "true" ]]; then
  FRAMEWORK_TYPE="static"
  XCFRAMEWORK_NAME="Sentry.xcframework"
else
  FRAMEWORK_TYPE="dynamic"
  XCFRAMEWORK_NAME="Sentry-Dynamic-WithARM64e.xcframework"
fi

# Framework path.
FRAMEWORK_PATH="$DIR/$XCFRAMEWORK_NAME/macos-arm64_arm64e_x86_64/Sentry.framework"

# Function to get framework version from Info.plist.
get_framework_version() {
  local framework_path="$1"
  local plist_path="$framework_path/Resources/Info.plist"

  if [[ ! -f "$plist_path" ]]; then
    echo ""
    return
  fi

  /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$plist_path" 2>/dev/null || echo ""
}

# Fetch a URL with retries. Writes the response body to stdout.
# Usage: fetch_with_retries <url> [curl_args...]
fetch_with_retries() {
  local url="$1"
  shift
  local max_retries=3
  local attempt=1
  local wait_seconds=2

  while (( attempt <= max_retries )); do
    local http_code
    local response

    # Fetch the URL, capturing both the response body and the HTTP status code.
    # --fail-with-body ensures we still get the response body on HTTP errors.
    response=$(curl --fail-with-body --show-error --write-out "\n%{http_code}" "$@" "$url") || true
    http_code=$(echo "$response" | tail -n1)
    response=$(echo "$response" | sed '$d')

    if [[ "$http_code" =~ ^2 ]]; then
      echo "$response"
      return 0
    fi

    # Only retry on transient errors (rate limiting or server errors).
    local is_retryable=false
    if [[ "$http_code" == "429" || "$http_code" =~ ^5 ]]; then
      is_retryable=true
    fi

    echo "[attempt $attempt/$max_retries] HTTP $http_code from $url" >&2
    if [[ "$is_retryable" == "true" ]] && (( attempt < max_retries )); then
      echo "Retrying in ${wait_seconds}s..." >&2
      sleep "$wait_seconds"
      wait_seconds=$(( wait_seconds * 2 ))
      attempt=$(( attempt + 1 ))
    else
      if [[ "$is_retryable" != "true" ]]; then
        echo "Non-retryable HTTP status; giving up." >&2
      fi
      break
    fi
  done

  echo "Error: Failed to fetch $url (HTTP $http_code). Response:" >&2
  echo "$response" >&2
  return 1
}

# Function to download and extract framework.
download_framework() {
  local xcframework_name="$1"
  local xcframework_dir="$DIR/$xcframework_name"
  local download_url="https://github.com/getsentry/sentry-cocoa/releases/download/${VERSION}/${xcframework_name}.zip"
  zip_file="$DIR/${xcframework_name}.zip"

  echo "Downloading $xcframework_name version $VERSION..."

  # Create directory if it doesn't exist.
  mkdir -p "$DIR"

  # Require jq for SHA256 verification.
  if ! command -v jq &> /dev/null; then
    echo "Error: jq is required for SHA256 verification"
    exit 1
  fi

  # Fetch release metadata from GitHub API.
  # Prefer `gh api` for authenticated requests (avoids GitHub rate limits in CI).
  local api_url="https://api.github.com/repos/getsentry/sentry-cocoa/releases/tags/${VERSION}"
  echo "Fetching release metadata from $api_url"
  local api_response
  if command -v gh &> /dev/null; then
    if ! api_response=$(gh api "repos/getsentry/sentry-cocoa/releases/tags/${VERSION}"); then
      exit 1
    fi
  else
    if ! api_response=$(fetch_with_retries "$api_url" --silent); then
      exit 1
    fi
  fi

  # Validate that the response contains an assets array before trying to iterate it.
  local assets_type
  assets_type=$(echo "$api_response" | jq -r '.assets | type')
  if [[ "$assets_type" != "array" ]]; then
    echo "Error: GitHub API response does not contain an assets array (got: $assets_type)." >&2
    echo "Raw API response:" >&2
    echo "$api_response" | head -50 >&2
    exit 1
  fi

  local expected_sha256
  expected_sha256=$(echo "$api_response" | jq -r ".assets[] | select(.name == \"$xcframework_name.zip\") | .digest // empty" | cut -d: -f2)

  if [[ -z "$expected_sha256" ]]; then
    echo "Error: Could not find SHA256 checksum for $xcframework_name.zip in release $VERSION."
    echo "Available assets:"
    echo "$api_response" | jq -r '.assets[].name'
    exit 1
  fi

  echo "Expected SHA256: $expected_sha256"

  # Download the framework. Prefer `gh release download` for authentication.
  echo "Downloading from $download_url"
  if command -v gh &> /dev/null; then
    if ! gh release download "${VERSION}" --repo getsentry/sentry-cocoa --pattern "${xcframework_name}.zip" --dir "$DIR" --clobber; then
      echo "Error: Failed to download $xcframework_name via gh CLI"
      exit 1
    fi
  else
    if ! curl -L -f --show-error -o "$zip_file" "$download_url"; then
      echo "Error: Failed to download $xcframework_name from $download_url"
      exit 1
    fi
  fi

  # Verify checksum if we have one.
  if [[ -n "$expected_sha256" ]]; then
    echo "Verifying checksum..."
    local actual_sha256
    actual_sha256=$(shasum -a 256 "$zip_file" | awk '{print $1}')

    if [[ "$actual_sha256" != "$expected_sha256" ]]; then
      echo "Error: SHA256 checksum mismatch!"
      echo "  Expected: $expected_sha256"
      echo "  Actual:   $actual_sha256"
      exit 1
    fi
    echo "Checksum verified successfully"
  fi

  # Remove existing framework if present.
  if [[ -d "$xcframework_dir" ]]; then
    rm -rf "$xcframework_dir"
  fi

  # Extract only the macos-arm64_arm64e_x86_64 subdirectory.
  echo "Extracting $xcframework_name (macOS frameworks only)..."
  if ! unzip -q "$zip_file" "${xcframework_name}/macos-arm64_arm64e_x86_64/*" -d "$DIR"; then
    echo "Error: Failed to extract framework from zip. Expected path: ${xcframework_name}/macos-arm64_arm64e_x86_64/"
    exit 1
  fi
  
  echo "Successfully installed $xcframework_name version $VERSION"
}

# Check and download the needed framework.
CURRENT_VERSION=$(get_framework_version "$FRAMEWORK_PATH")
if [[ -z "$CURRENT_VERSION" ]] || [[ "$CURRENT_VERSION" != "$VERSION" ]]; then
  if [[ -z "$CURRENT_VERSION" ]]; then
    echo "$FRAMEWORK_TYPE framework not found or missing Info.plist"
  else
    echo "$FRAMEWORK_TYPE framework version mismatch: found $CURRENT_VERSION, expected $VERSION"
  fi
  download_framework "$XCFRAMEWORK_NAME"
else
  echo "$FRAMEWORK_TYPE framework version $VERSION already installed"
fi

echo "Framework up to date"
