#!/usr/bin/env zsh

# Publish a new release.

set -euo pipefail

# For docs see docs/RELEASE.md

autoload -U colors && colors

project_dir=${0:a:h:h}
cd $project_dir

# Deny all warnings in CI.
export RUSTFLAGS="-D warnings"

main() {
  echo -e "${fg[magenta]}Publishing new version of the up-rs CLI...${reset_color}"

  # Make sure docker is running as we'll need it later.
  pgrep -q Docker || open -a docker

  # Bail on uncommitted diffs.
  diff=$(git diff --color=always)
  if [[ -n $diff ]]; then
    error "${fg[cyan]}-> Repo has uncommitted diffs:${reset_color}
    $diff"
  fi

  # Bail on untracked files.
  untracked=$(git ls-files . --exclude-standard --others | head)
  if [[ -n $untracked ]]; then
    error "Repo has untracked files:\n$untracked"
  fi

  # Test Darwin
  log_and_run cargo test --release --features=CI

  # TODO(gib): automatically calculate new version from conventional commit messages.
  changelog_version=$(head -1 CHANGELOG.md | awk -F'"' '{print $2}')
  last_tag=$(git tag -l | gsort -V | tail -1)
  tag_before_last=$(git tag -l | gsort -V | tail -2 | head -1)
  last_release=$(gh release list -L 1 | awk '{print $1}')
  cargo_toml_version=$(awk -F\" '/^version = /{print $2; exit}' Cargo.toml)

  # Bump version in Cargo.toml and changelog:

  if [[ $changelog_version != $last_release && $last_release != $cargo_toml_version ]]; then
    prompt_to_skip "Last release was $last_release, but changelog updated to $changelog_version. Skipping changelog update..."
    new_version=$changelog_version
  else
    log_section "Updating changelog..."
    read "new_version?New version (old version is ${last_release?}): "
    # TODO(gib): make this follow (and link to in CHANGELOG.md) https://keepachangelog.com/en/1.0.0/
    clog -C CHANGELOG.md --from="${last_release?}" --setversion="${new_version?}"
    # Make the header a link pointing to the release.
    gsed -i "s/^## ${new_version?} (/## [${new_version?}][] (/" CHANGELOG.md
    echo "[${new_version?}]: https://github.com/gibfahn/up-rs/releases/tag/${new_version?}" >>CHANGELOG.md

    log_section "Updating Cargo.toml..."
    gsed -i -E "0,/^version = \"${last_release?}\"\$/s//version = \"${new_version?}\"/" Cargo.toml
    log_and_run cargo check # Bumps version in lockfile too.
    log_section "Committing version updates..."
    git add Cargo.toml Cargo.lock CHANGELOG.md
    git commit -m "chore: bump version to ${new_version?}"
    git show # Check version is correct.
    prompt_to_skip "Does this look correct (should change Cargo.toml, Cargo.lock, CHANGELOG.md)?"
  fi

  # Build and test Linux (static) and Darwin binaries locally:

  # Check the documentation is buildable.
  log_and_run cargo doc
  # Tests musl static Linux binaries.
  log_and_run meta/cargo-docker
  # Runs end-to-end bootstrap tests.
  log_and_run meta/bootstrap-test

  # Build Darwin release binaries (without the CI feature).
  log_and_run cargo build --release
  # Builds musl static Linux release binaries.
  log_and_run meta/cargo-docker build --release

  latest_crate_version=$(curl https://crates.io/api/v1/crates/up-rs | jq -r .crate.newest_version)
  if [[ $latest_crate_version == $new_version ]]; then
    prompt_to_skip "Skipping cargo publish as latest release is already $latest_crate_version."
  else
    # Publish to crates.io:
    log_and_run cargo publish
  fi

  log_and_run git push up main

  # This allows them to be downloaded as `up-$(uname)`.
  mkdir -p out
  cp target/release/up out/up-Darwin
  cp target/x86_64-unknown-linux-musl/release/up out/up-Linux
  gh release create "${new_version?}" --target=main \
    --notes="${new_version?}"$'\n\n'"$(clog --from="${last_release?}" --setversion="${new_version?}")" \
    out/up-Darwin out/up-Linux
  rm -r out

  new_release=$(gh release list -L 1 | awk '{print $1}')
  gh release view $new_release
  if [[ $new_release != $new_version ]]; then
    error "Something went wrong, latest GitHub version is not what the script just released."
  fi

  # Pull in the tag we just created remotely.
  log_section "Fetching just-created tag..."
  git fetch --all
}

log_section() {
  echo "
${fg[cyan]}=> $*${reset_color}"
}

log_and_run() {
  log_section "Running $*"
  "$@"
}

# $1: Error message
# $2: Error code (default: 1).
error() {
  echo -e "${fg[red]}ERROR${reset_color}: $1" >&2
  exit "${2:-1}"
}

prompt_to_skip() {
  read "user_input?$1
  Press Enter to continue, type anything or press Ctrl-C to cancel: "
  if [[ -n ${user_input:-} ]]; then
    error "User entered text."
  fi
}

main "$@"
