#!/bin/bash
set -euo pipefail

GITEA_TOKEN_FILE="${GITEA_TOKEN_FILE:-$HOME/.config/gitea/token}"

function usage() {
    echo "Usage: gitea-pr create [--title TITLE] [--body BODY | --body-file FILE] [--base BRANCH]"
    echo "       gitea-pr comments PR_NUMBER"
    echo "       gitea-pr merge PR_NUMBER"
    echo ""
    echo "--body-file reads the body from FILE, or from stdin when FILE is '-'."
    echo "Prefer it over --body for anything with quotes, backticks or newlines."
    echo ""
    echo "Requires GITEA_SSH_HOST and GITEA_API set in the environment (e.g. in" \
         "your shell profile), and a token at $GITEA_TOKEN_FILE (or GITEA_TOKEN" \
         "set for this invocation only)."
}

function require_config() {
    if [ -z "${GITEA_SSH_HOST:-}" ]; then
        echo "GITEA_SSH_HOST is not set. Add 'export GITEA_SSH_HOST=<gitea ssh hostname>'" \
             "(e.g. gitea.example.com) to ~/.profile.local" >&2
        exit 1
    fi
    if [ -z "${GITEA_API:-}" ]; then
        echo "GITEA_API is not set. Add 'export GITEA_API=https://<gitea host>/api/v1'" \
             "(e.g. https://gitea.example.com/api/v1) to ~/.profile.local" >&2
        exit 1
    fi
}

function require_token() {
    if [ -z "${GITEA_TOKEN:-}" ] && [ -f "$GITEA_TOKEN_FILE" ]; then
        GITEA_TOKEN=$(<"$GITEA_TOKEN_FILE")
    fi
    if [ -z "${GITEA_TOKEN:-}" ]; then
        echo "No Gitea token found. Generate one in the Gitea web UI" \
             "(Settings > Applications, scopes write:repository + write:issue)" \
             "and save it to $GITEA_TOKEN_FILE (chmod 600), or set GITEA_TOKEN" \
             "in the environment for this invocation only." >&2
        exit 1
    fi
}

# Feeds the auth header to curl as a config on stdin rather than as an
# argument. Command lines are world-readable through /proc, so `-H
# "Authorization: token $GITEA_TOKEN"` exposes the token to every other
# process on the host for as long as the request runs - and to anything that
# scrapes `ps` output, including the agents this repo exists to sandbox.
# printf is a bash builtin, so the token never lands in another argv.
function gitea_api() {
    printf 'header = "Authorization: token %s"\n' "$GITEA_TOKEN" |
        curl -sS -w '\n%{http_code}' -K - "$@"
}

# Reads a body from a file, or from stdin for '-'. The point is to keep prose
# out of argv: a body composed on a command line has to survive shell quoting,
# and the things that break it - an apostrophe, a backtick, a $ - are exactly
# what English and code fences are made of. A file (or a heredoc into '-') has
# no such layer.
function read_body_file() {
    local file="$1"
    if [ "$file" = "-" ]; then
        cat
        return
    fi
    if [ ! -f "$file" ]; then
        echo "No such body file: $file" >&2
        exit 1
    fi
    cat "$file"
}

function gitea_owner_repo() {
    local remote_url host owner_repo
    remote_url=$(git remote get-url origin 2>/dev/null) || {
        echo "No 'origin' remote found in this repository" >&2
        exit 1
    }
    host=$(echo "$remote_url" | sed -E 's#^[^@]+@([^:]+):.*#\1#')
    if [ "$host" != "$GITEA_SSH_HOST" ]; then
        echo "origin remote ($remote_url) is not on $GITEA_SSH_HOST; gitea-pr only supports this host" >&2
        exit 1
    fi
    owner_repo=$(echo "$remote_url" | sed -E 's#^[^@]+@[^:]+:##; s#\.git$##')
    echo "$owner_repo"
}

if [ "$#" -lt 1 ]; then
    usage
    exit 1
fi

subcommand="$1"
shift

case "$subcommand" in
    create|comments|merge)
        require_config
        require_token
        owner_repo=$(gitea_owner_repo)
        owner="${owner_repo%%/*}"
        repo="${owner_repo##*/}"
        ;;
    *)
        usage
        exit 1
        ;;
esac

case "$subcommand" in
    create)
        title=""
        body=""
        body_file=""
        base=""
        while [ "$#" -gt 0 ]; do
            case "$1" in
                --title) title="$2"; shift 2 ;;
                --body) body="$2"; shift 2 ;;
                --body-file) body_file="$2"; shift 2 ;;
                --base) base="$2"; shift 2 ;;
                *) echo "Unknown option: $1" >&2; usage; exit 1 ;;
            esac
        done
        # Rejected rather than ranked: silently preferring one would make a
        # caller that passed both ship a body it didn't mean to.
        if [ -n "$body" ] && [ -n "$body_file" ]; then
            echo "Pass --body or --body-file, not both" >&2
            exit 1
        fi
        if [ -n "$body_file" ]; then
            body="$(read_body_file "$body_file")"
        fi
        body="**Summary generated by LLM agent**

${body}"

        current_branch=$(git rev-parse --abbrev-ref HEAD)
        if [ "$current_branch" = "HEAD" ]; then
            echo "Detached HEAD: check out the feature branch before creating a PR" >&2
            exit 1
        fi

        if [ -z "$base" ]; then
            base=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's#refs/remotes/origin/##' || true)
            base="${base:-main}"
        fi

        response=$(gitea_api "${GITEA_API}/repos/${owner}/${repo}/pulls?state=open")
        http_code=$(echo "$response" | tail -1)
        json=$(echo "$response" | sed '$d')
        if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
            echo "Failed to check for an existing PR (HTTP $http_code):" >&2
            echo "$json" >&2
            exit 1
        fi
        existing_url=$(echo "$json" | jq -r --arg head "$current_branch" --arg base "$base" \
            '.[] | select(.head.ref == $head and .base.ref == $base) | .html_url' | head -1)
        if [ -n "$existing_url" ]; then
            echo "$existing_url"
            exit 0
        fi
        if [ -z "$title" ]; then
            title="$current_branch"
        fi

        response=$(gitea_api -X POST "${GITEA_API}/repos/${owner}/${repo}/pulls" \
            -H "Content-Type: application/json" \
            -d "$(jq -n --arg head "$current_branch" --arg base "$base" --arg title "$title" --arg body "$body" \
                '{head: $head, base: $base, title: $title, body: $body}')")
        http_code=$(echo "$response" | tail -1)
        json=$(echo "$response" | sed '$d')

        if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
            echo "Failed to create PR (HTTP $http_code):" >&2
            echo "$json" >&2
            exit 1
        fi
        echo "$json" | jq -r '.html_url'
        ;;
    comments)
        pr_number="${1:-}"
        if [ -z "$pr_number" ]; then
            echo "Usage: gitea-pr comments PR_NUMBER" >&2
            exit 1
        fi

        echo "=== Issue comments ==="
        response=$(gitea_api "${GITEA_API}/repos/${owner}/${repo}/issues/${pr_number}/comments")
        http_code=$(echo "$response" | tail -1)
        json=$(echo "$response" | sed '$d')
        if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
            echo "Failed to fetch issue comments (HTTP $http_code):" >&2
            echo "$json" >&2
            exit 1
        fi
        echo "$json" | jq -r '.[] | "[\(.user.login) @ \(.created_at)]\n\(.body)\n"'

        echo "=== Reviews ==="
        response=$(gitea_api "${GITEA_API}/repos/${owner}/${repo}/pulls/${pr_number}/reviews")
        http_code=$(echo "$response" | tail -1)
        reviews_json=$(echo "$response" | sed '$d')
        if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
            echo "Failed to fetch reviews (HTTP $http_code):" >&2
            echo "$reviews_json" >&2
            exit 1
        fi
        echo "$reviews_json" | jq -r '.[] | select(.body != "") | "[\(.user.login) @ \(.submitted_at) — \(.state)]\n\(.body)\n"'

        echo "=== Inline review comments ==="
        review_ids=$(echo "$reviews_json" | jq -r '.[] | select(.comments_count > 0) | .id')
        for review_id in $review_ids; do
            response=$(gitea_api \
                "${GITEA_API}/repos/${owner}/${repo}/pulls/${pr_number}/reviews/${review_id}/comments")
            http_code=$(echo "$response" | tail -1)
            json=$(echo "$response" | sed '$d')
            if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
                echo "Failed to fetch comments for review ${review_id} (HTTP $http_code):" >&2
                echo "$json" >&2
                exit 1
            fi
            echo "$json" | jq -r '.[] | "[\(.user.login) @ \(.path):\(.line // "?")]\n\(.body)\n"'
        done
        ;;
    merge)
        pr_number="${1:-}"
        if [ -z "$pr_number" ]; then
            echo "Usage: gitea-pr merge PR_NUMBER" >&2
            exit 1
        fi

        response=$(gitea_api -X POST "${GITEA_API}/repos/${owner}/${repo}/pulls/${pr_number}/merge" \
            -H "Content-Type: application/json" \
            -d '{"Do": "merge"}')
        http_code=$(echo "$response" | tail -1)
        json=$(echo "$response" | sed '$d')

        if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
            echo "Failed to merge PR #${pr_number} (HTTP $http_code):" >&2
            echo "$json" >&2
            exit 1
        fi
        echo "Merged PR #${pr_number}"
        ;;
esac
