#!/bin/bash

# Script to generate release notes with changelog from commits between tags
# Usage: ./changelog [version/tag] [output_file]
#   - If version/tag is provided, generates changelog from previous tag to that version
#   - If not provided, generates changelog from latest tag to HEAD (next release)
#   - If output_file is not provided, outputs to stdout

set -e

# Parse arguments
TARGET_VERSION=""
OUTPUT_FILE=""

# Check if first argument looks like a version/tag (starts with 'v' or is a version pattern)
if [ -n "${1:-}" ]; then
    if [[ "$1" =~ ^v?[0-9] ]] || [[ "$1" =~ ^v[0-9]+\.[0-9]+ ]] || git rev-parse "$1" >/dev/null 2>&1; then
        TARGET_VERSION="$1"
        OUTPUT_FILE="${2:-}"
    else
        OUTPUT_FILE="$1"
    fi
fi

# Determine target tag and previous tag
if [ -n "$TARGET_VERSION" ]; then
    # Normalize version (add 'v' prefix if missing)
    if [[ ! "$TARGET_VERSION" =~ ^v ]]; then
        TARGET_TAG="v${TARGET_VERSION}"
    else
        TARGET_TAG="$TARGET_VERSION"
    fi
    
    # Verify the tag exists
    if ! git rev-parse "$TARGET_TAG" >/dev/null 2>&1; then
        echo "Error: Tag $TARGET_TAG does not exist" >&2
        exit 1
    fi
    
    # Find the previous tag before the target tag
    # Get all tags sorted by version (newest first), find the one that comes after TARGET_TAG
    ALL_TAGS=$(git tag --sort=-version:refname 2>/dev/null || echo "")
    
    if [ -n "$ALL_TAGS" ]; then
        # Find the tag that comes right after TARGET_TAG in the sorted list (which is the previous release)
        FOUND_TARGET=false
        PREVIOUS_TAG=""
        while IFS= read -r tag; do
            if [ "$FOUND_TARGET" = true ]; then
                PREVIOUS_TAG="$tag"
                break
            fi
            if [ "$tag" = "$TARGET_TAG" ]; then
                FOUND_TARGET=true
            fi
        done <<< "$ALL_TAGS"
    fi
    
    # If still not found, try GitHub releases
    if [ -z "$PREVIOUS_TAG" ]; then
        PREVIOUS_TAG=$(gh release list --json tagName -q ".[] | select(.tagName != \"$TARGET_TAG\") | .tagName" 2>/dev/null | grep -v "^$TARGET_TAG$" | head -1 || echo "")
    fi
    
    # Last resort: use git describe to find previous tag
    if [ -z "$PREVIOUS_TAG" ]; then
        PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${TARGET_TAG}^" 2>/dev/null || echo "")
    fi
    
    if [ -z "$PREVIOUS_TAG" ]; then
        echo "Warning: Could not find previous tag for $TARGET_TAG, using first commit" >&2
        PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD 2>/dev/null || echo "")
    fi
    
    CURRENT_VERSION="${TARGET_VERSION#v}"
    COMPARE_TO="$TARGET_TAG"
    echo "Generating changelog from $PREVIOUS_TAG to $TARGET_TAG (v${CURRENT_VERSION})..." >&2
else
    # Default behavior: generate for next release (latest tag to HEAD)
    PREVIOUS_TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName' 2>/dev/null || echo "")
    
    if [ -z "$PREVIOUS_TAG" ]; then
        echo "Warning: Could not find release tags from GitHub, trying git tags" >&2
        PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
        
        if [ -z "$PREVIOUS_TAG" ]; then
            echo "Error: Could not find any release tags" >&2
            exit 1
        fi
    fi
    
    # Get current version from build.zig.zon for the release title
    CURRENT_VERSION=$(grep -E '^\s*\.version\s*=' build.zig.zon | sed -E 's/.*"([^"]+)".*/\1/' || echo "HEAD")
    COMPARE_TO="HEAD"
    echo "Generating changelog from $PREVIOUS_TAG to HEAD (v${CURRENT_VERSION})..." >&2
fi

# Read the base RELEASE.md
BASE_CONTENT=$(cat .github/RELEASE.md)

# Get repository owner and name for commit URLs
REPO_OWNER="nurulhudaapon"
REPO_NAME="zx"
REPO_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}"

# Get commits between previous tag and HEAD with SHA, message, and author
# Use temporary files to avoid subshell issues with arrays
TMP_COMMITS="/tmp/changelog_commits.txt"
rm -f "$TMP_COMMITS"

# Try GitHub API first (includes author login)
gh api "repos/${REPO_OWNER}/${REPO_NAME}/compare/${PREVIOUS_TAG}...${COMPARE_TO}" --jq '.commits[] | "\(.sha)|\(.commit.message | split("\n")[0])|\(if .author.login then .author.login elif .committer.login then .committer.login else "" end)"' > "$TMP_COMMITS" 2>/dev/null || true

# If GitHub API failed or returned empty, use git log
if [ ! -s "$TMP_COMMITS" ]; then
    echo "Warning: GitHub API failed, using git log" >&2
    # Get author email and try to map to GitHub username
    git log "${PREVIOUS_TAG}..${COMPARE_TO}" --pretty=format:"%H|%s|%ae" > "$TMP_COMMITS" 2>/dev/null || true
fi

# Categorize commits with links
FEATURES=()
DOCS=()
FIXES=()
OTHER=()

# Track unique contributors (using a temporary file for compatibility with bash 3.x)
TMP_CONTRIBUTORS="/tmp/changelog_contributors.txt"
rm -f "$TMP_CONTRIBUTORS"

# Process commits from file
while IFS='|' read -r sha message author || [ -n "$sha" ]; do
    if [[ -z "$sha" ]] || [[ -z "$message" ]]; then
        continue
    fi
    
    # Get GitHub username from author
    # When using GitHub API, author should be the login (username)
    # When using git log fallback, author will be an email address
    GITHUB_USERNAME=""
    if [[ "$author" =~ @[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} ]]; then
        # It's an email address (from git log fallback), skip to avoid incorrect tags
        GITHUB_USERNAME=""
    elif [ -n "$author" ] && [ "$author" != "unknown" ] && [ "$author" != "" ]; then
        # It's a GitHub username from the API
        GITHUB_USERNAME="$author"
        # Add to contributors file (if not already present)
        if [ -f "$TMP_CONTRIBUTORS" ]; then
            if ! grep -q "^${GITHUB_USERNAME}$" "$TMP_CONTRIBUTORS" 2>/dev/null; then
                echo "$GITHUB_USERNAME" >> "$TMP_CONTRIBUTORS"
            fi
        else
            echo "$GITHUB_USERNAME" > "$TMP_CONTRIBUTORS"
        fi
    fi
    
    # Create commit link (short SHA)
    SHORT_SHA="${sha:0:7}"
    COMMIT_LINK="[\`${SHORT_SHA}\`](${REPO_URL}/commit/${sha})"
    
    # Format commit entry (without contributor mention)
    COMMIT_ENTRY="${message} ${COMMIT_LINK}"
    
    if [[ "$message" =~ ^feat ]]; then
        FEATURES+=("$COMMIT_ENTRY")
    elif [[ "$message" =~ ^docs ]]; then
        DOCS+=("$COMMIT_ENTRY")
    elif [[ "$message" =~ ^fix ]]; then
        FIXES+=("$COMMIT_ENTRY")
    else
        OTHER+=("$COMMIT_ENTRY")
    fi
done < "$TMP_COMMITS"

rm -f "$TMP_COMMITS"

# Generate changelog section
{
    echo "## Changelog"
    echo ""
    echo "### v${CURRENT_VERSION}"
    echo ""
    
    if [ ${#FEATURES[@]} -gt 0 ]; then
        echo "#### Features"
        for feat in "${FEATURES[@]}"; do
            echo "- ${feat}"
        done
        echo ""
    fi
    

    
    if [ ${#FIXES[@]} -gt 0 ]; then
        echo "#### Fixes"
        for fix in "${FIXES[@]}"; do
            echo "- ${fix}"
        done
        echo ""
    fi

    if [ ${#DOCS[@]} -gt 0 ]; then
        echo "#### Documentation"
        for doc in "${DOCS[@]}"; do
            echo "- ${doc}"
        done
        echo ""
    fi
    
    if [ ${#OTHER[@]} -gt 0 ]; then
        echo "#### Miscellaneous"
        for other in "${OTHER[@]}"; do
            echo "- ${other}"
        done
        echo ""
    fi
    
    # Add Contributors section if there are any
    if [ -f "$TMP_CONTRIBUTORS" ] && [ -s "$TMP_CONTRIBUTORS" ]; then
        echo "### Contributors"
        echo ""
        # Sort contributors alphabetically and format
        sort "$TMP_CONTRIBUTORS" | while read -r contributor; do
            echo "- @${contributor}"
        done
        echo ""
    fi
} > /tmp/changelog.txt

# Cleanup contributors file
rm -f "$TMP_CONTRIBUTORS"

# Combine base content with changelog
if [ -n "$OUTPUT_FILE" ]; then
    {
        echo "$BASE_CONTENT"
        echo ""
        cat /tmp/changelog.txt
    } > "$OUTPUT_FILE"
    echo "Release notes generated: $OUTPUT_FILE" >&2
else
    echo "$BASE_CONTENT"
    echo ""
    cat /tmp/changelog.txt
fi
rm -f /tmp/changelog.txt

