#!/usr/bin/env bash

# Generate changelog from conventional commits
# Usage: gc-changelog [from_ref] [to_ref]
#        gc-changelog              # all commits
#        gc-changelog v1.0.0       # from tag to HEAD
#        gc-changelog v1.0.0 v2.0.0 # between tags

set -o errexit
set -o nounset

from_ref=${1:-}
to_ref=${2:-HEAD}

if [ -n "$from_ref" ]; then
    range="$from_ref..$to_ref"
else
    range=""
fi

# Collect commits by type
declare -A titles=(
    [feat]="Features"
    [fix]="Bug Fixes"
    [docs]="Documentation"
    [style]="Styles"
    [refactor]="Refactoring"
    [test]="Tests"
    [chore]="Chores"
    [perf]="Performance"
    [ci]="CI"
    [build]="Build"
)

declare -A commits
declare -a breaking_changes

for type in feat fix docs style refactor test chore perf ci build; do
    commits[$type]=""
done

# Parse commits
# Full pattern: type(scope)!: description
while IFS= read -r line; do
    [ -z "$line" ] && continue

    msg=$(echo "$line" | cut -d' ' -f2-)

    for type in feat fix docs style refactor test chore perf ci build; do
        # Match: type, optional (scope), optional !, colon, space
        if echo "$msg" | grep -qE "^$type(\([^)]+\))?!?: "; then
            is_breaking=false
            if echo "$msg" | grep -qE "^$type(\([^)]+\))?!:"; then
                is_breaking=true
            fi

            # Extract scope and description
            if echo "$msg" | grep -qE "^$type\([^)]+\)"; then
                scope=$(echo "$msg" | sed -E "s/^$type\(([^)]+)\)!?: .*/\1/")
                desc=$(echo "$msg" | sed -E "s/^$type\([^)]+\)!?: //")
            else
                scope=""
                desc=$(echo "$msg" | sed -E "s/^$type!?: //")
            fi

            # Format the entry
            if [ -n "$scope" ]; then
                entry="- **$scope**: $desc"
            else
                entry="- $desc"
            fi

            if [ "$is_breaking" = true ]; then
                breaking_changes+=("$entry")
            fi

            commits[$type]+="$entry"$'\n'
            break
        fi
    done
done < <(git log --oneline $range)

# Output changelog
echo "# Changelog"
echo ""

# Breaking changes first
if [ ${#breaking_changes[@]} -gt 0 ]; then
    echo "## BREAKING CHANGES"
    echo ""
    for change in "${breaking_changes[@]}"; do
        echo "$change"
    done
    echo ""
fi

# Then by type
for type in feat fix perf refactor docs style test chore ci build; do
    if [ -n "${commits[$type]}" ]; then
        echo "## ${titles[$type]}"
        echo ""
        echo -n "${commits[$type]}"
        echo ""
    fi
done
