#!/usr/bin/env bash
#MISE description="Release with release-plz"
set -euxo pipefail

# Ensure this script is only run in GitHub Actions
if [[ -z ${GITHUB_ACTIONS:-} ]]; then
	echo "Error: This script must be run in GitHub Actions"
	echo "The release-plz script should only be executed in the CI/CD pipeline"
	exit 1
fi

git config user.name mise-en-dev
git config user.email release@mise.jdx.dev

exists_on_crates() {
	crate="$1"
	version="$2"
	if cargo info --registry "crates-io" --color never --quiet "$crate" | grep -E "^version:\s+$version$" >/dev/null 2>&1; then
		return 0
	fi
	if curl -fsSL "https://crates.io/api/v1/crates/$crate/$version" >/dev/null 2>&1; then
		return 0
	fi
	return 1
}

# Check if a subcrate has changes since its last tagged release
# Returns 0 if changes exist (should publish), 1 if no changes
has_crate_changes() {
	local crate_name="$1"
	local crate_dir="$2"

	# Get the latest tag for this crate
	local latest_tag
	latest_tag=$(git tag --list "${crate_name}-v*" --sort=-v:refname | head -1)

	if [[ -z $latest_tag ]]; then
		echo "No previous tag found for $crate_name, will publish"
		return 0
	fi

	# Check if there are changes in the crate directory since the tag
	if git diff --quiet "$latest_tag" -- "$crate_dir"; then
		echo "No changes in $crate_name since $latest_tag"
		return 1
	else
		echo "Changes detected in $crate_name since $latest_tag"
		return 0
	fi
}

# Get the latest published version of a crate from crates.io
get_latest_crates_version() {
	local crate="$1"
	cargo info --registry "crates-io" --color never --quiet "$crate" 2>/dev/null | grep "^version:" | cut -d' ' -f2 || echo ""
}

# Check if a directory has uncommitted changes (staged or unstaged)
has_uncommitted_changes() {
	local dir="$1"
	# Check for any changes (staged or unstaged) in the directory
	if git diff --quiet HEAD -- "$dir" 2>/dev/null && git diff --quiet --cached -- "$dir" 2>/dev/null; then
		# Also check for untracked files
		if [[ -z $(git ls-files --others --exclude-standard -- "$dir") ]]; then
			return 1 # No changes
		fi
	fi
	return 0 # Has changes
}

# Bump subcrate version if it has uncommitted changes (for prep phase)
bump_subcrate_if_changed() {
	local crate_name="$1"
	local crate_dir="$2"

	if ! has_uncommitted_changes "$crate_dir"; then
		echo "No uncommitted changes in $crate_name, keeping current version"
		return
	fi

	echo "Uncommitted changes detected in $crate_name, bumping version"
	local cur_version
	cur_version="$(cargo pkgid -p "$crate_name" | cut -d# -f2)"

	# Use calver: YYYY.M.PATCH
	local year month
	year="$(date +%Y)"
	month="$(date +%-m)"

	if echo "$cur_version" | grep -qE "^$year\.$month\."; then
		# Same year.month, bump patch (e.g., 2025.12.0 -> 2025.12.1)
		cargo set-version --bump patch -p "$crate_name"
	else
		# New month or new year, start fresh at YYYY.MM.0
		cargo set-version "$year.$month.0" -p "$crate_name"
	fi

	local new_version
	new_version="$(cargo pkgid -p "$crate_name" | cut -d# -f2)"
	echo "Bumped $crate_name from $cur_version to $new_version"
}

# Bump and publish a subcrate if it has changes
# Sets PUBLISHED_<CRATE>_VERSION variable with the version to use
bump_and_publish_subcrate() {
	local crate_name="$1"
	local crate_dir="$2"
	local var_name="$3" # Variable name to set (e.g., VFOX_VERSION)

	local cur_version
	cur_version="$(cargo pkgid -p "$crate_name" | cut -d# -f2)"

	if ! has_crate_changes "$crate_name" "$crate_dir"; then
		# No changes - use the current published version
		local published_version
		published_version=$(get_latest_crates_version "$crate_name")
		if [[ -n $published_version ]]; then
			echo "Using existing $crate_name@$published_version (no changes)"
			eval "$var_name=$published_version"
		else
			echo "Warning: $crate_name not found on crates.io, using current version"
			eval "$var_name=$cur_version"
		fi
		return
	fi

	# Has changes - bump version and publish
	# Use calver: YYYY.M.PATCH
	local year month
	year="$(date +%Y)"
	month="$(date +%-m)"

	if echo "$cur_version" | grep -qE "^$year\.$month\."; then
		# Same year.month, bump patch (e.g., 2025.12.0 -> 2025.12.1)
		cargo set-version --bump patch -p "$crate_name"
	else
		# New month or new year, start fresh at YYYY.MM.0
		cargo set-version "$year.$month.0" -p "$crate_name"
	fi

	local new_version
	new_version="$(cargo pkgid -p "$crate_name" | cut -d# -f2)"

	if exists_on_crates "$crate_name" "$new_version"; then
		echo "$crate_name@$new_version already on crates.io, skipping publish"
	else
		echo "Publishing $crate_name@$new_version"
		cargo publish --allow-dirty -p "$crate_name"
	fi

	# Create tag for this subcrate release
	local tag_name="${crate_name}-v${new_version}"
	if git rev-parse -q --verify "refs/tags/$tag_name" >/dev/null ||
		git ls-remote --exit-code --tags origin "$tag_name" >/dev/null 2>&1; then
		echo "Tag $tag_name already exists, skipping tag creation"
	else
		git tag "$tag_name" -s -m "Release $crate_name $new_version"
	fi

	eval "$var_name=$new_version"
}

cur_version="$(cargo pkgid mise | cut -d# -f2)"
latest_version="$(cargo info --registry "crates-io" --color never --quiet mise | grep "^version:" | cut -d' ' -f2)"
if [[ $cur_version != "$latest_version" ]]; then
	echo "Releasing mise $cur_version"

	# Handle subcrates with independent versioning
	VFOX_VERSION=""
	AQUA_REGISTRY_VERSION=""

	bump_and_publish_subcrate "vfox" "crates/vfox" "VFOX_VERSION"
	cargo add "vfox@$VFOX_VERSION"

	bump_and_publish_subcrate "aqua-registry" "crates/aqua-registry" "AQUA_REGISTRY_VERSION"
	cargo add "aqua-registry@$AQUA_REGISTRY_VERSION"

	if exists_on_crates mise "$cur_version"; then
		echo "mise@$cur_version already on crates.io, skipping publish"
	else
		cargo publish --allow-dirty -p mise
	fi

	changelog="$(git cliff --tag "v$cur_version" --strip all --unreleased)"
	changelog="$(echo "$changelog" | tail -n +3)"
	if git rev-parse -q --verify "refs/tags/v$cur_version" >/dev/null ||
		git ls-remote --exit-code --tags origin "v$cur_version" >/dev/null 2>&1; then
		echo "Tag v$cur_version already exists, skipping tag creation"
	else
		git tag "v$cur_version" -s -m "$changelog"
	fi

	# Push all tags (mise + any subcrate tags)
	git push --tags
	exit 0
fi

year="$(date +%Y)"
month="$(date +%-m)"
if echo "$cur_version" | grep -e "^$year\.$month\."; then
	cargo set-version --bump patch -p mise
elif echo "$cur_version" | grep -e "^$year\."; then
	cargo set-version --bump minor -p mise
else
	cargo set-version "$year.1.0" -p mise
fi

version="$(cargo pkgid mise | cut -d# -f2)"
sed -i.bak "s/^[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc\.[0-9]\+\)\? macos-arm64 (a1b2d3e [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\})$/$version macos-arm64 (a1b2d3e $(date +%Y-%m-%d))/" README.md
sed -i.bak "s/^Version: [0-9]\+\.[0-9]\+\.[0-9]\+\(-rc\.[0-9]\+\)\?$/Version: $version/" packaging/rpm/mise.spec
sed -i.bak "s/version = \"[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc\.[0-9]\+\)\?\";$/version = \"$version\";/" default.nix
sed -i.bak "s/version: \"[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc\.[0-9]\+\)\?\"$/version: \"$version\"/" snapcraft.yaml

# Update GitHub star count using gh CLI
stars_raw="$(gh api repos/jdx/mise --jq '.stargazers_count')"
if [[ -n $stars_raw ]]; then
	if [[ $stars_raw -ge 1000 ]]; then
		# Format as k notation (e.g., 19346 -> 19.3k)
		stars_formatted="$(echo "scale=1; $stars_raw / 1000" | bc)k"
		# Remove trailing .0k
		stars_formatted="${stars_formatted/.0k/k}"
	else
		stars_formatted="$stars_raw"
	fi

	# Update the stars data file
	cat >docs/.vitepress/stars.data.ts <<EOF
// This file is auto-updated by xtasks/release-plz
// Current star count from GitHub API
export default {
  load() {
    return {
      stars: "$stars_formatted",
    };
  },
};
EOF
	echo "Updated star count to $stars_formatted"
fi

# fetch-gpg-keys must run before render (which includes build) since it
# recreates src/assets/gpg/ which is read at compile time via include_str!
mise run fetch-gpg-keys
mise run render ::: lint-fix

# Capture current aqua-registry package list before updating
OLD_AQUA_PKGS=""
if [[ -d crates/aqua-registry/aqua-registry/pkgs ]]; then
	OLD_AQUA_PKGS="$(find crates/aqua-registry/aqua-registry/pkgs -name registry.yaml -type f | sed 's|crates/aqua-registry/aqua-registry/pkgs/||;s|/registry.yaml||' | sort)"
fi

rm -rf crates/aqua-registry/aqua-registry
git clone https://github.com/aquaproj/aqua-registry crates/aqua-registry/aqua-registry
# Keep only pkgs/**/registry.yaml files, remove everything else
find crates/aqua-registry/aqua-registry -type f ! -path "crates/aqua-registry/aqua-registry/pkgs/*/registry.yaml" ! -name LICENSE -delete
find crates/aqua-registry/aqua-registry -type d -empty -delete

# Capture new aqua-registry package list after updating
NEW_AQUA_PKGS="$(find crates/aqua-registry/aqua-registry/pkgs -name registry.yaml -type f | sed 's|crates/aqua-registry/aqua-registry/pkgs/||;s|/registry.yaml||' | sort)"

# Generate aqua-registry changelog section for both CHANGELOG.md and PR body
AQUA_CHANGELOG_MD="$(./scripts/gen-aqua-changelog.sh "$OLD_AQUA_PKGS" "$NEW_AQUA_PKGS" "###" || true)"
AQUA_CHANGELOG_PR="$(./scripts/gen-aqua-changelog.sh "$OLD_AQUA_PKGS" "$NEW_AQUA_PKGS" "##" || true)"

# Generate only the new release section (unreleased changes)
# Use --prepend to preserve existing changelog content
git cliff --tag "v$version" --unreleased --prepend CHANGELOG.md

# Inject aqua-registry updates into CHANGELOG.md if there are any
if [[ -n $AQUA_CHANGELOG_MD ]]; then
	# Find the FIRST release section (the one we just added) and inject aqua section at the end, before the next release
	awk -v aqua_section="$AQUA_CHANGELOG_MD" '
		/^## \[/ {
			if (release_count == 1 && !inserted) {
				# We are at the second release, inject aqua section before it
				print aqua_section
				inserted = 1
			}
			release_count++
		}
		{print}
		END {
			# If we never hit a second release (only one release in file), append at end
			if (release_count == 1 && !inserted) {
				print aqua_section
			}
		}
	' CHANGELOG.md >CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md
fi

# Generate changelog for PR body
changelog="$(git cliff --tag "v$version" --unreleased --strip all)"
changelog="$(echo "$changelog" | tail -n +3)"

mise up
mise lock

# Update embedded vfox plugins (delete all and re-clone fresh, similar to aqua-registry)
EMBEDDED_PLUGINS_DIR="crates/vfox/embedded-plugins"
rm -rf "$EMBEDDED_PLUGINS_DIR"/vfox-*

# Clone all tools where vfox is the FIRST (default) backend
mise registry | awk '$2 ~ /^vfox:/ {print $2}' | sed 's|vfox:||' | sort -u | while read -r repo_path; do
	plugin="$(basename "$repo_path")"
	echo "Cloning $plugin from https://github.com/$repo_path"
	TEMP_DIR="$(mktemp -d)"
	git clone --depth 1 "https://github.com/$repo_path.git" "$TEMP_DIR" 2>&1 | grep -v "^Cloning" || true
	DEST_DIR="$EMBEDDED_PLUGINS_DIR/$plugin"
	mkdir -p "$DEST_DIR/hooks"
	cp "$TEMP_DIR/metadata.lua" "$DEST_DIR/"
	cp "$TEMP_DIR/hooks/"*.lua "$DEST_DIR/hooks/" 2>/dev/null || true
	if [[ -d "$TEMP_DIR/lib" ]]; then
		mkdir -p "$DEST_DIR/lib"
		cp "$TEMP_DIR/lib/"*.lua "$DEST_DIR/lib/" 2>/dev/null || true
	fi
	rm -rf "$TEMP_DIR"
done

# Bump subcrate versions if they have changes (from aqua-registry/vfox updates)
bump_subcrate_if_changed "aqua-registry" "crates/aqua-registry"
bump_subcrate_if_changed "vfox" "crates/vfox"

git status
# cargo update
git add \
	Cargo.lock \
	Cargo.toml \
	CHANGELOG.md \
	README.md \
	crates/aqua-registry/aqua-registry \
	crates/aqua-registry/Cargo.toml \
	crates/vfox/embedded-plugins \
	crates/vfox/Cargo.toml \
	default.nix \
	snapcraft.yaml \
	docs/.vitepress/stars.data.ts \
	packaging/rpm/mise.spec \
	mise.usage.kdl \
	completions \
	mise.lock \
	man/ \
	src/assets/gpg/
git clean -df
git checkout -B release
git commit -m "chore: release $version"
git push origin release --force

if [[ "$(gh pr list --label release)" == "" ]]; then
	# Append aqua-registry updates to PR body if available
	PR_BODY="$changelog"
	if [[ -n $AQUA_CHANGELOG_PR ]]; then
		PR_BODY="$changelog"$'\n\n'"$AQUA_CHANGELOG_PR"
	fi
	gh pr create --title "chore: release $version" --body "$PR_BODY" --label "release" --head release
else
	# Append aqua-registry updates to PR body if available
	PR_BODY="$changelog"
	if [[ -n $AQUA_CHANGELOG_PR ]]; then
		PR_BODY="$changelog"$'\n\n'"$AQUA_CHANGELOG_PR"
	fi
	gh pr edit --title "chore: release $version" --body "$PR_BODY"
fi
