#!/usr/bin/env bash

set -euo pipefail

usage() {
    cat <<'EOF'
Usage: script/release [--dry-run] [--yes] VERSION ["Color Animal"]

Prepare a release, push it, and create draft release notes on GitHub.

Options:
  --dry-run  Validate the release and run tests without changing anything.
  --yes      Skip confirmation prompts.
  -h, --help Show this help.

If no title is supplied, an unused Color Animal title is generated.
EOF
}

die() {
    echo "release: $*" >&2
    exit 1
}

require_command() {
    command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}

confirm() {
    local answer

    if [[ "$assume_yes" == true ]]; then
        return 0
    fi

    read -r -p "$1 [y/N] " answer || return 1
    [[ "$answer" == "y" || "$answer" == "Y" || "$answer" == "yes" || "$answer" == "YES" ]]
}

title_exists() {
    printf '%s\n' "$existing_titles" | grep -Fxi -- "$1" >/dev/null 2>&1
}

generate_title() {
    local colors=(
        Amber Aqua Black Blue Bronze Brown Coral Crimson Cyan Emerald
        Gold Gray Green Indigo Ivory Lavender Lime Magenta Maroon Navy
        Ochre Olive Orange Peach Pink Plum Purple Red Rose Ruby
        Sapphire Scarlet Silver Tan Teal Turquoise Violet White Yellow
    )
    local animals=(
        Alpaca Badger Bear Beaver Bison Boa Bobcat Buffalo Camel Capybara
        Cat Cheetah Cobra Condor Cougar Coyote Crane Crow Deer Dolphin
        Donkey Eagle Elephant Falcon Ferret Finch Fox Gecko Giraffe Goat
        Goldfish Goose Heron Hippo Horse Ibex Iguana Jaguar Koala Lemur
        Leopard Lion Llama Lobster Lynx Mantis Mole Moose Narwhal Octopus
        Otter Owl Oyster Panda Panther Parrot Pelican Penguin Pigeon Puma
        Rabbit Raven Rhino Seal Shark Sheep Sloth Sparrow Squid Swan Tiger
        Toad Toucan Turtle Walrus Whale Wolf Wombat Yak Zebra
    )
    local candidate
    local attempts=0

    while (( attempts < 200 )); do
        candidate="${colors[RANDOM % ${#colors[@]}]} ${animals[RANDOM % ${#animals[@]}]}"
        if ! title_exists "$candidate"; then
            printf '%s\n' "$candidate"
            return 0
        fi
        attempts=$((attempts + 1))
    done

    die "could not generate an unused release title"
}

dry_run=false
assume_yes=false

while (( $# > 0 )); do
    case "$1" in
        --dry-run)
            dry_run=true
            shift
            ;;
        --yes)
            assume_yes=true
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        --)
            shift
            break
            ;;
        -*)
            die "unknown option: $1"
            ;;
        *)
            break
            ;;
    esac
done

(( $# >= 1 && $# <= 2 )) || {
    usage >&2
    exit 1
}

version=$1
requested_title=${2:-}

[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]] || \
    die "version must look like 0.7.0 or 0.7.0-rc.1"

require_command git
require_command gh
require_command bats

repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || die "not inside a Git repository"
cd "$repo_root"

[[ -f bash-preexec.sh ]] || die "run this from the bash-preexec repository"
[[ -z "$(git status --porcelain)" ]] || die "working tree is not clean"

branch=$(git symbolic-ref --quiet --short HEAD) || die "releases cannot be made from a detached HEAD"
default_branch=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)
default_branch=${default_branch#origin/}
default_branch=${default_branch:-master}
[[ "$branch" == "$default_branch" ]] || \
    die "switch to the default branch ($default_branch); currently on $branch"

echo "Fetching the latest branch and tags from origin..."
git fetch --quiet origin "$branch" --tags

[[ "$(git rev-parse HEAD)" == "$(git rev-parse "refs/remotes/origin/$branch")" ]] || \
    die "$branch must exactly match origin/$branch before releasing"

if git rev-parse --quiet --verify "refs/tags/$version" >/dev/null; then
    die "tag already exists: $version"
fi

if gh release view "$version" >/dev/null 2>&1; then
    die "GitHub release already exists: $version"
fi

current_versions=$(sed -n 's/^# V\(.*\)$/\1/p' bash-preexec.sh)
[[ -n "$current_versions" && "$current_versions" != *$'\n'* ]] || \
    die "expected exactly one '# V...' version marker in bash-preexec.sh"
current_version=$current_versions
[[ "$current_version" != "$version" ]] || die "bash-preexec.sh is already at version $version"

existing_titles=$(gh release list --limit 1000 --json name --jq '.[].name')

if [[ -n "$requested_title" ]]; then
    [[ "$requested_title" =~ ^[[:alpha:]]+[[:space:]][[:alpha:]]+$ ]] || \
        die "release title must have the form 'Color Animal'"
    title=$requested_title
    title_exists "$title" && die "release title is already in use: $title"
else
    title=$(generate_title)
fi

echo
echo "Release plan"
echo "  Version: $current_version -> $version"
echo "  Title:   $title"
echo "  Branch:  $branch"
echo "  Notes:   GitHub-generated draft"
echo

if [[ "$dry_run" == true ]]; then
    echo "Running bats test..."
    bats test
    echo
    echo "Dry run complete; no files, commits, tags, or releases were changed."
    exit 0
fi

confirm "Prepare the release commit and tag?" || die "cancelled"

sed -i.bak "s/^# V.*$/# V${version}/" bash-preexec.sh
rm -f bash-preexec.sh.bak

echo "Running bats test..."
if ! bats test; then
    sed -i.bak "s/^# V.*$/# V${current_version}/" bash-preexec.sh
    rm -f bash-preexec.sh.bak
    die "tests failed; restored bash-preexec.sh"
fi

git diff --check
git add -- bash-preexec.sh
git commit -m "Preparing to tag v${version}"
git tag "$version"

if ! confirm "Push $branch and tag $version, then create the draft GitHub release?"; then
    echo
    echo "Release commit and tag were created locally but not pushed."
    echo "To finish later:"
    echo "  git push --atomic origin $branch refs/tags/$version"
    echo "  gh release create $version --verify-tag --title \"$title\" --generate-notes --draft"
    exit 0
fi

git push --atomic origin "$branch" "refs/tags/$version"

echo
echo "Creating draft GitHub release..."
if release_url=$(gh release create "$version" \
    --verify-tag \
    --title "$title" \
    --generate-notes \
    --draft); then
    echo "Draft release created: $release_url"
else
    echo "The commit and tag were pushed, but the draft release could not be created." >&2
    echo "Retry with:" >&2
    echo "  gh release create $version --verify-tag --title \"$title\" --generate-notes --draft" >&2
    exit 1
fi
