#!/bin/bash
#
# generate_changelog.sh
#
# Generates a weekly changelog from merged PRs, uses Claude to create a synopsis,
# and sends an HTML email via Fastmail JMAP API.
#
# Usage: ./scripts/generate_changelog.sh [--dry-run]
#
# Configuration files (in ~/.config/eagle0/):
#   fastmail_token      - API token (required)
#   changelog_recipient - Email addresses, one per line (optional, defaults to sender)
#
# To set up:
#   mkdir -p ~/.config/eagle0
#   echo 'your-token' > ~/.config/eagle0/fastmail_token
#   chmod 600 ~/.config/eagle0/fastmail_token
#
#   # Optional: configure recipients (one per line, # for comments)
#   cat > ~/.config/eagle0/changelog_recipient << EOF
#   alice@example.com
#   bob@example.com
#   EOF
#
# The script tracks its last run using a git tag 'changelog-last-run'.
# On first run (no tag), it defaults to the previous Friday at 4pm.

set -euo pipefail

# Ensure homebrew binaries are in PATH
export PATH="/opt/homebrew/bin:$PATH"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
TAG_NAME="changelog-last-run"
DRY_RUN=false
FASTMAIL_API="https://api.fastmail.com/jmap/api/"
CONFIG_DIR="$HOME/.config/eagle0"
TOKEN_FILE="$CONFIG_DIR/fastmail_token"
RECIPIENT_FILE="$CONFIG_DIR/changelog_recipient"

# Load API token from file or environment
load_api_token() {
    # Environment variable takes precedence
    if [[ -n "${FASTMAIL_API_TOKEN:-}" ]]; then
        return 0
    fi

    # Try loading from config file
    if [[ -f "$TOKEN_FILE" ]]; then
        FASTMAIL_API_TOKEN=$(cat "$TOKEN_FILE" | tr -d '[:space:]')
        if [[ -n "$FASTMAIL_API_TOKEN" ]]; then
            echo "Loaded API token from $TOKEN_FILE"
            export FASTMAIL_API_TOKEN
            return 0
        fi
    fi

    return 1
}

# Load recipient emails from config file (one per line)
# Returns JSON array fragment like: {"email": "a@b.com"}, {"email": "c@d.com"}
load_recipients_json() {
    local recipients=""
    if [[ -f "$RECIPIENT_FILE" ]]; then
        while IFS= read -r line || [[ -n "$line" ]]; do
            # Skip empty lines and comments
            line=$(echo "$line" | tr -d '[:space:]')
            [[ -z "$line" || "$line" == \#* ]] && continue

            if [[ -n "$recipients" ]]; then
                recipients="$recipients, "
            fi
            recipients="$recipients{\"email\": \"$line\"}"
        done < "$RECIPIENT_FILE"
    fi
    echo "$recipients"
}

# Get human-readable list of recipients
load_recipients_display() {
    if [[ -f "$RECIPIENT_FILE" ]]; then
        grep -v '^#' "$RECIPIENT_FILE" | grep -v '^[[:space:]]*$' | tr '\n' ', ' | sed 's/, $//'
    fi
}

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        *)
            echo "Unknown option: $1"
            echo "Usage: $0 [--dry-run]"
            exit 1
            ;;
    esac
done

cd "$REPO_ROOT"

# Get the cutoff date - either from tag or previous Friday 4pm
get_cutoff_date() {
    # Try to get the date from the tag
    if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
        # Get the commit date of the tagged commit
        git log -1 --format="%aI" "$TAG_NAME"
    else
        # Calculate previous Friday at 4pm
        # Get current day of week (1=Monday, 7=Sunday)
        local dow=$(date +%u)
        local days_since_friday

        if [[ $dow -ge 5 ]]; then
            # Friday (5), Saturday (6), or Sunday (7)
            days_since_friday=$((dow - 5))
        else
            # Monday (1) through Thursday (4)
            days_since_friday=$((dow + 2))
        fi

        # Get previous Friday at 4pm in ISO format
        if [[ "$(uname)" == "Darwin" ]]; then
            date -v-"${days_since_friday}d" -v16H -v0M -v0S +"%Y-%m-%dT%H:%M:%S%z"
        else
            date -d "$days_since_friday days ago 16:00:00" --iso-8601=seconds
        fi
    fi
}

# Fetch merged PRs since the cutoff date
fetch_merged_prs() {
    local since_date="$1"
    local output_file="$2"

    echo "Fetching PRs merged since: $since_date"

    # Use gh to search for merged PRs
    gh pr list \
        --state merged \
        --base main \
        --json number,title,body,mergedAt,author \
        --jq ".[] | select(.mergedAt >= \"$since_date\")" \
        > "$output_file.json"

    # Format the output nicely
    echo "# Merged PRs since $since_date" > "$output_file"
    echo "" >> "$output_file"

    # Process each PR
    jq -r '
        "## PR #\(.number): \(.title)\n" +
        "Author: \(.author.login)\n" +
        "Merged: \(.mergedAt)\n\n" +
        "### Description\n" +
        (.body // "(No description)") +
        "\n\n---\n"
    ' "$output_file.json" >> "$output_file"

    # Count PRs
    local pr_count=$(jq -s 'length' "$output_file.json")
    echo "Found $pr_count merged PRs"

    rm -f "$output_file.json"

    if [[ $pr_count -eq 0 ]]; then
        echo "No PRs found since $since_date"
        return 1
    fi

    return 0
}

# Generate synopsis using Claude
generate_synopsis() {
    local input_file="$1"
    local output_file="$2"

    echo "Generating synopsis with Claude..."

    # Create a prompt file to avoid shell escaping issues
    local prompt_file="/tmp/eagle0_prompt_$$.txt"
    # Get repo URL for PR links
    local repo_url=$(gh repo view --json url -q '.url')

    cat > "$prompt_file" <<PROMPT_HEADER
You are summarizing changes for a weekly engineering update email.

Read the following list of merged PRs and create a concise synopsis grouped by theme/feature/area of the codebase.

Structure:
1. <h1> title (e.g., "Eagle0 Weekly Update")
2. <h2>BLUF</h2> (Bottom Line Up Front) - A short prose paragraph (2-4 sentences) highlighting the 1-3 most important changes this week and what to look for when testing. This should be conversational and help readers quickly understand what matters most.
3. Synopsis sections (<h2> headings with bullet point summaries)
4. <hr> divider
5. <h2>PR Details</h2> with the same groupings, but smaller (<h3> headings) and listing PR links
   - Format each PR as: <a href="${repo_url}/pull/NUMBER">#NUMBER</a>: Title

Guidelines for the SYNOPSIS sections:
- Group related changes together under clear headings (use <h2> tags)
- Use bullet points (<ul><li>) for individual changes
- Highlight any significant new features, breaking changes, or important fixes
- Keep the tone professional but accessible
- Don't include PR numbers in the synopsis - focus on what changed and why it matters

IMPORTANT: Output valid HTML that can be used directly in an email body. Do NOT wrap in \`\`\`html code blocks - just output the raw HTML.

Here are the merged PRs:

PROMPT_HEADER

    cat "$input_file" >> "$prompt_file"
    echo "" >> "$prompt_file"
    echo "Generate the synopsis now:" >> "$prompt_file"

    # Use Claude CLI to generate the synopsis, wrapped in proper HTML with charset
    local raw_output="/tmp/eagle0_raw_$$.html"
    cat "$prompt_file" | claude --print > "$raw_output"

    # Wrap in HTML document with UTF-8 charset
    cat > "$output_file" <<'HTML_HEAD'
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
HTML_HEAD
    cat "$raw_output" >> "$output_file"
    echo "</body></html>" >> "$output_file"

    rm -f "$prompt_file" "$raw_output"
    echo "Synopsis generated at: $output_file"
}

# Get Fastmail session info (account ID, identity ID, drafts mailbox ID)
get_fastmail_session() {
    echo "Fetching Fastmail session info..." >&2

    # Get session
    local session=$(curl -s \
        -H "Authorization: Bearer $FASTMAIL_API_TOKEN" \
        "https://api.fastmail.com/jmap/session")

    # Extract account ID (first account)
    FASTMAIL_ACCOUNT_ID=$(echo "$session" | jq -r '.primaryAccounts["urn:ietf:params:jmap:mail"]')

    if [[ -z "$FASTMAIL_ACCOUNT_ID" || "$FASTMAIL_ACCOUNT_ID" == "null" ]]; then
        echo "Error: Could not get Fastmail account ID. Check your API token." >&2
        return 1
    fi
    echo "Account ID: $FASTMAIL_ACCOUNT_ID" >&2

    # Get identity ID
    local identity_response=$(curl -s \
        -H "Authorization: Bearer $FASTMAIL_API_TOKEN" \
        -H "Content-Type: application/json" \
        -X POST \
        -d "{
            \"using\": [\"urn:ietf:params:jmap:core\", \"urn:ietf:params:jmap:mail\", \"urn:ietf:params:jmap:submission\"],
            \"methodCalls\": [
                [\"Identity/get\", {\"accountId\": \"$FASTMAIL_ACCOUNT_ID\"}, \"0\"]
            ]
        }" \
        "$FASTMAIL_API")

    FASTMAIL_IDENTITY_ID=$(echo "$identity_response" | jq -r '.methodResponses[0][1].list[0].id')
    FASTMAIL_FROM_EMAIL=$(echo "$identity_response" | jq -r '.methodResponses[0][1].list[0].email')

    if [[ -z "$FASTMAIL_IDENTITY_ID" || "$FASTMAIL_IDENTITY_ID" == "null" ]]; then
        echo "Error: Could not get Fastmail identity ID." >&2
        return 1
    fi
    echo "Identity ID: $FASTMAIL_IDENTITY_ID (${FASTMAIL_FROM_EMAIL})" >&2

    # Get drafts mailbox ID
    local mailbox_response=$(curl -s \
        -H "Authorization: Bearer $FASTMAIL_API_TOKEN" \
        -H "Content-Type: application/json" \
        -X POST \
        -d "{
            \"using\": [\"urn:ietf:params:jmap:core\", \"urn:ietf:params:jmap:mail\"],
            \"methodCalls\": [
                [\"Mailbox/query\", {\"accountId\": \"$FASTMAIL_ACCOUNT_ID\", \"filter\": {\"role\": \"drafts\"}}, \"0\"]
            ]
        }" \
        "$FASTMAIL_API")

    FASTMAIL_DRAFTS_ID=$(echo "$mailbox_response" | jq -r '.methodResponses[0][1].ids[0]')

    if [[ -z "$FASTMAIL_DRAFTS_ID" || "$FASTMAIL_DRAFTS_ID" == "null" ]]; then
        echo "Error: Could not get Fastmail drafts mailbox ID." >&2
        return 1
    fi
    echo "Drafts mailbox ID: $FASTMAIL_DRAFTS_ID" >&2

    return 0
}

# Send email via Fastmail JMAP API
send_email_fastmail() {
    local synopsis_file="$1"
    local recipients_json="$2"  # JSON array fragment: {"email": "a@b.com"}, {"email": "c@d.com"}

    local subject="Eagle0 Weekly Changelog - $(date +%Y-%m-%d)"
    local html_body=$(cat "$synopsis_file" | jq -Rs .)

    echo "Sending email via Fastmail JMAP API..."

    # Create the email and send it in one request
    local response=$(curl -s \
        -H "Authorization: Bearer $FASTMAIL_API_TOKEN" \
        -H "Content-Type: application/json" \
        -X POST \
        -d "{
            \"using\": [
                \"urn:ietf:params:jmap:core\",
                \"urn:ietf:params:jmap:mail\",
                \"urn:ietf:params:jmap:submission\"
            ],
            \"methodCalls\": [
                [\"Email/set\", {
                    \"accountId\": \"$FASTMAIL_ACCOUNT_ID\",
                    \"create\": {
                        \"draft\": {
                            \"from\": [{\"email\": \"$FASTMAIL_FROM_EMAIL\"}],
                            \"to\": [$recipients_json],
                            \"subject\": \"$subject\",
                            \"mailboxIds\": {\"$FASTMAIL_DRAFTS_ID\": true},
                            \"keywords\": {\"\$draft\": true},
                            \"htmlBody\": [{\"partId\": \"body\", \"type\": \"text/html\"}],
                            \"bodyValues\": {
                                \"body\": {
                                    \"charset\": \"utf-8\",
                                    \"value\": $html_body
                                }
                            }
                        }
                    }
                }, \"0\"],
                [\"EmailSubmission/set\", {
                    \"accountId\": \"$FASTMAIL_ACCOUNT_ID\",
                    \"onSuccessDestroyEmail\": [\"#sendIt\"],
                    \"create\": {
                        \"sendIt\": {
                            \"emailId\": \"#draft\",
                            \"identityId\": \"$FASTMAIL_IDENTITY_ID\"
                        }
                    }
                }, \"1\"]
            ]
        }" \
        "$FASTMAIL_API")

    # Check for errors
    local error=$(echo "$response" | jq -r '.methodResponses[0][1].notCreated.draft.description // empty')
    if [[ -n "$error" ]]; then
        echo "Error creating email: $error" >&2
        echo "Full response: $response" >&2
        return 1
    fi

    local send_error=$(echo "$response" | jq -r '.methodResponses[1][1].notCreated.sendIt.description // empty')
    if [[ -n "$send_error" ]]; then
        echo "Error sending email: $send_error" >&2
        echo "Full response: $response" >&2
        return 1
    fi

    echo "Email sent successfully"
}

# Generate player-friendly "What's New" summary
# This creates a short summary suitable for in-game display
generate_whats_new_summary() {
    local pr_file="$1"
    local output_file="/tmp/eagle0_whats_new_$$.txt"

    echo "Generating player-friendly What's New summary..." >&2

    # Create a prompt for player-facing summary
    local prompt_file="/tmp/eagle0_whats_new_prompt_$$.txt"

    cat > "$prompt_file" <<'WHATS_NEW_PROMPT'
Based on these merged PRs, write a SHORT player-friendly summary.
Focus only on changes players will notice. Ignore internal/technical changes.

Output format (exactly this format, no markdown, no extra text):
TITLE: (5-10 words describing the main change)
DESCRIPTION: (1-2 sentences, what players can now do differently)
CATEGORY: (one of: feature, improvement, fix, content)

If there are multiple notable player-visible changes, pick the single most important one.
If there are no player-visible changes at all, output exactly: NONE

Examples of good output:
TITLE: Bug Reporting
DESCRIPTION: You can now report bugs directly from the Settings menu.
CATEGORY: feature

TITLE: Faster Reconnection
DESCRIPTION: The game now reconnects more smoothly after network interruptions.
CATEGORY: improvement

Here are the merged PRs:

WHATS_NEW_PROMPT

    cat "$pr_file" >> "$prompt_file"

    # Use Claude CLI to generate the summary
    if ! command -v claude &> /dev/null; then
        echo "NONE"
        rm -f "$prompt_file"
        return
    fi

    cat "$prompt_file" | claude --print > "$output_file" 2>/dev/null

    rm -f "$prompt_file"

    # Check if the output is NONE
    if grep -q "^NONE$" "$output_file"; then
        echo "NONE"
        rm -f "$output_file"
        return
    fi

    # Return the output
    cat "$output_file"
    rm -f "$output_file"
}

# URL encode a string for use in query parameters
urlencode() {
    local string="$1"
    local strlen=${#string}
    local encoded=""
    local pos c o

    for (( pos=0 ; pos<strlen ; pos++ )); do
        c=${string:$pos:1}
        case "$c" in
            [-_.~a-zA-Z0-9] ) o="${c}" ;;
            * )               printf -v o '%%%02x' "'$c"
        esac
        encoded+="${o}"
    done
    echo "${encoded}"
}

# Open the admin console with pre-populated what's new content
open_whats_new_preview() {
    local title="$1"
    local description="$2"
    local category="$3"

    local encoded_title=$(urlencode "$title")
    local encoded_desc=$(urlencode "$description")
    local encoded_cat=$(urlencode "$category")

    local preview_url="https://admin.eagle0.net/whats-new?preview=true&title=${encoded_title}&description=${encoded_desc}&category=${encoded_cat}"

    echo ""
    echo "=== What's New Preview ==="
    echo "Title: $title"
    echo "Description: $description"
    echo "Category: $category"
    echo ""
    echo "Opening admin console preview..."

    # Open the URL in the default browser
    if [[ "$(uname)" == "Darwin" ]]; then
        open "$preview_url"
    elif command -v xdg-open &> /dev/null; then
        xdg-open "$preview_url"
    else
        echo "Preview URL: $preview_url"
    fi
}

# Update the tag to mark this run
update_tag() {
    echo "Updating $TAG_NAME tag..."

    # Delete existing tag if present
    git tag -d "$TAG_NAME" 2>/dev/null || true
    git push origin --delete "$TAG_NAME" 2>/dev/null || true

    # Create new tag at HEAD
    git tag "$TAG_NAME"
    git push origin "$TAG_NAME"

    echo "Tag updated to current HEAD"
}

# Main
main() {
    echo "=== Eagle0 Weekly Changelog Generator ==="
    echo ""

    # Load API token (only required for actual send)
    if [[ "$DRY_RUN" != "true" ]]; then
        if ! load_api_token; then
            echo "Error: No Fastmail API token found."
            echo ""
            echo "To create a token:"
            echo "1. Go to Fastmail Settings -> Password & Security -> API tokens"
            echo "2. Create a new token with 'Email submission' scope"
            echo "3. Save it using one of these methods:"
            echo ""
            echo "   Option A (recommended): Store in config file"
            echo "     mkdir -p ~/.config/eagle0"
            echo "     echo 'your-token' > ~/.config/eagle0/fastmail_token"
            echo "     chmod 600 ~/.config/eagle0/fastmail_token"
            echo ""
            echo "   Option B: Set environment variable"
            echo "     export FASTMAIL_API_TOKEN='your-token'"
            exit 1
        fi
    fi

    # Get cutoff date
    local cutoff_date=$(get_cutoff_date)
    echo "Cutoff date: $cutoff_date"

    # Create temp files
    local pr_file="/tmp/eagle0_prs_$(date +%s).md"
    local synopsis_file="/tmp/eagle0_synopsis_$(date +%s).html"

    # Fetch PRs
    if ! fetch_merged_prs "$cutoff_date" "$pr_file"; then
        echo "No changes to report. Exiting."
        exit 0
    fi

    echo ""
    echo "PR details saved to: $pr_file"

    # Generate synopsis
    generate_synopsis "$pr_file" "$synopsis_file"

    if [[ "$DRY_RUN" == "true" ]]; then
        echo ""
        echo "=== DRY RUN - Synopsis content ==="
        cat "$synopsis_file"
        echo ""
        echo "=== DRY RUN - Skipping email send and tag update ==="
    else
        # Get Fastmail session info
        if ! get_fastmail_session; then
            echo "Failed to get Fastmail session info. Exiting."
            exit 1
        fi

        # Determine recipients (from config file, or default to sender)
        local recipients_json=$(load_recipients_json)
        if [[ -z "$recipients_json" ]]; then
            recipients_json="{\"email\": \"$FASTMAIL_FROM_EMAIL\"}"
            echo "No recipients configured, sending to self ($FASTMAIL_FROM_EMAIL)"
        else
            local recipients_display=$(load_recipients_display)
            echo "Sending to: $recipients_display"
        fi

        # Send email
        send_email_fastmail "$synopsis_file" "$recipients_json"

        # Update tag for next run
        update_tag

        # Generate player-friendly What's New summary and open admin preview
        echo ""
        echo "Generating What's New summary for in-game display..."
        whats_new_output=$(generate_whats_new_summary "$pr_file")

        if [[ "$whats_new_output" != "NONE" ]]; then
            # Parse the output
            title=$(echo "$whats_new_output" | grep "^TITLE:" | sed 's/^TITLE:[[:space:]]*//')
            description=$(echo "$whats_new_output" | grep "^DESCRIPTION:" | sed 's/^DESCRIPTION:[[:space:]]*//')
            category=$(echo "$whats_new_output" | grep "^CATEGORY:" | sed 's/^CATEGORY:[[:space:]]*//')

            if [[ -n "$title" && -n "$description" ]]; then
                open_whats_new_preview "$title" "$description" "$category"
            else
                echo "Could not parse What's New output, skipping preview"
            fi
        else
            echo "No player-visible changes detected, skipping What's New preview"
        fi
    fi

    echo ""
    echo "Done!"
    echo "PR details: $pr_file"
    echo "Synopsis: $synopsis_file"
}

main
