#!/bin/bash
# pr-merge.sh - Merge pull requests on Gitea or GitHub
# Usage: pr-merge.sh -n PR_NUMBER [-m squash] [-d] [--expect-head SHA] [--co-author-trailers --escalate-to PRINCIPAL]

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=packages/mosaic/framework/tools/git/detect-platform.sh
source "$SCRIPT_DIR/detect-platform.sh"

# Default values
PR_NUMBER=""
MERGE_METHOD="squash"
DELETE_BRANCH=false
DRY_RUN=false
EXPECT_HEAD=""
CO_AUTHOR_TRAILERS=false
ESCALATE_TO=""

usage() {
    cat <<EOF
Usage: $(basename "$0") [OPTIONS]

Merge a pull request on the current repository (Gitea or GitHub).

Options:
    -n, --number NUMBER     PR number to merge (required)
    -m, --method METHOD     Merge method: squash only (default: squash)
    -d, --delete-branch     Delete the head branch after merge
        --dry-run           Run metadata/login preflight without merging
        --expect-head SHA   Refuse unless the PR head matches this full commit SHA
        --co-author-trailers Build verified trailers from linked PR commit authors
        --escalate-to NAME  Named principal for an unresolved-author BLOCK
    -h, --help              Show this help message

Examples:
    $(basename "$0") -n 42                    # Merge PR #42
    $(basename "$0") -n 42 -m squash          # Squash merge
    $(basename "$0") -n 42 -d                 # Squash merge and delete branch
    $(basename "$0") -n 42 --expect-head 0123456789abcdef0123456789abcdef01234567
    $(basename "$0") -n 42 --co-author-trailers --escalate-to tl-mosaic
EOF
    exit "${1:-1}"
}

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -n|--number)
            PR_NUMBER="$2"
            shift 2
            ;;
        -m|--method)
            MERGE_METHOD="$2"
            shift 2
            ;;
        -d|--delete-branch)
            DELETE_BRANCH=true
            shift
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        --expect-head)
            if [[ $# -lt 2 ]]; then
                echo "Error: --expect-head requires one full commit SHA." >&2
                exit 1
            fi
            EXPECT_HEAD="$2"
            shift 2
            ;;
        --co-author-trailers)
            CO_AUTHOR_TRAILERS=true
            shift
            ;;
        --escalate-to)
            if [[ $# -lt 2 ]]; then
                echo "Error: --escalate-to requires one principal name." >&2
                exit 1
            fi
            ESCALATE_TO="$2"
            shift 2
            ;;
        -h|--help)
            usage 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            usage
            ;;
    esac
done

if [[ -z "$PR_NUMBER" ]]; then
    echo "Error: PR number is required (-n)" >&2
    usage
fi

if [[ ! "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
    echo "Error: Invalid PR number '$PR_NUMBER'. PR number must contain digits only." >&2
    exit 1
fi

if [[ "$MERGE_METHOD" != "squash" ]]; then
    echo "Error: Mosaic policy enforces squash merge only. Received '$MERGE_METHOD'." >&2
    exit 1
fi
if [[ -n "$EXPECT_HEAD" && ! "$EXPECT_HEAD" =~ ^[0-9a-fA-F]{40}$ ]]; then
    echo "Error: --expect-head must be a full 40-character hexadecimal commit SHA." >&2
    exit 1
fi
if [[ "$CO_AUTHOR_TRAILERS" == true && -z "$ESCALATE_TO" ]]; then
    echo "Error: --co-author-trailers requires --escalate-to with a named principal." >&2
    exit 1
fi
if [[ -n "$ESCALATE_TO" && ! "$ESCALATE_TO" =~ ^[A-Za-z0-9_.-]+$ ]]; then
    echo "Error: --escalate-to must be one exact principal name." >&2
    exit 1
fi
if [[ "$CO_AUTHOR_TRAILERS" != true && -n "$ESCALATE_TO" ]]; then
    echo "Error: --escalate-to is valid only with --co-author-trailers." >&2
    exit 1
fi

PR_METADATA="$("$SCRIPT_DIR/pr-metadata.sh" -n "$PR_NUMBER")"
BASE_BRANCH="$(printf '%s' "$PR_METADATA" | python3 -c 'import json, sys; print((json.load(sys.stdin).get("baseRefName") or "").strip())')"
HEAD_BRANCH="$(printf '%s' "$PR_METADATA" | python3 -c 'import json, sys; print((json.load(sys.stdin).get("headRefName") or "").strip())')"
HEAD_SHA="$(printf '%s' "$PR_METADATA" | python3 -c 'import json, sys; print((json.load(sys.stdin).get("headRefOid") or "").strip())')"
HEAD_REPO="$(printf '%s' "$PR_METADATA" | python3 -c 'import json, sys; value=json.load(sys.stdin).get("headRepository") or ""; print((value.get("nameWithOwner") or value.get("full_name") or "") if isinstance(value, dict) else str(value).strip())')"
PR_TITLE="$(printf '%s' "$PR_METADATA" | python3 -c 'import json, sys; print((json.load(sys.stdin).get("title") or "").strip())')"
PR_AUTHOR="$(printf '%s' "$PR_METADATA" | python3 -c 'import json, sys; value=json.load(sys.stdin).get("author") or ""; print((value.get("login") or "").strip() if isinstance(value, dict) else str(value).strip())')"
if [[ "$BASE_BRANCH" != "main" && "$BASE_BRANCH" != "next" ]]; then
    echo "Error: Mosaic policy allows merges only for PRs targeting 'main' or 'next' (found '$BASE_BRANCH')." >&2
    exit 1
fi
if [[ -z "$HEAD_BRANCH" || -z "$HEAD_REPO" || ! "$HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then
    echo "Error: Could not resolve the PR head branch, repository, and full commit SHA for queue inspection." >&2
    exit 1
fi
if [[ -n "$EXPECT_HEAD" && "$HEAD_SHA" != "$EXPECT_HEAD" ]]; then
    echo "Error: PR head moved: expected $EXPECT_HEAD, found $HEAD_SHA." >&2
    exit 1
fi

if [[ "$DRY_RUN" != true ]]; then
    "$SCRIPT_DIR/ci-queue-wait.sh" \
        --purpose merge \
        -B "$HEAD_BRANCH" \
        -R "$HEAD_REPO" \
        --sha "$HEAD_SHA" \
        -t "${MOSAIC_CI_QUEUE_TIMEOUT_SEC:-900}" \
        -i "${MOSAIC_CI_QUEUE_POLL_SEC:-15}"
fi

PLATFORM=$(detect_platform)
OWNER=$(get_repo_owner)
REPO=$(get_repo_name)

write_curl_auth_config() {
    local mode="$1" credential="$2"
    printf '%s' "$credential" | python3 -c '
import sys
mode = sys.argv[1]
credential = sys.stdin.read()
if not credential or any(char in credential for char in "\r\n"):
    raise SystemExit(1)
escaped = credential.replace("\\", "\\\\").replace("\"", "\\\"")
if mode == "token":
    print(f"header = \"Authorization: token {escaped}\"")
elif mode == "basic":
    print(f"user = \"{escaped}\"")
else:
    raise SystemExit(1)
' "$mode"
}

LAST_GITEA_HTTP_CODE="000"
LAST_GITEA_ERROR=""
MERGE_TEMP_DIRS=()
GITEA_CURL_MAX_BYTES="${MOSAIC_GITEA_CURL_MAX_BYTES:-1048576}"
GITEA_CURL_MAX_TIME="${MOSAIC_GITEA_CURL_MAX_TIME_SEC:-30}"
GITEA_CURL_CONNECT_TIMEOUT="${MOSAIC_GITEA_CURL_CONNECT_TIMEOUT_SEC:-10}"
for bound in "$GITEA_CURL_MAX_BYTES" "$GITEA_CURL_MAX_TIME" "$GITEA_CURL_CONNECT_TIMEOUT"; do
    if [[ ! "$bound" =~ ^[1-9][0-9]*$ ]]; then
        echo "Error: Gitea curl bounds must be positive integers; refusing request." >&2
        exit 1
    fi
done
GITEA_CURL_BOUNDS=(
    --max-filesize "$GITEA_CURL_MAX_BYTES"
    --max-time "$GITEA_CURL_MAX_TIME"
    --connect-timeout "$GITEA_CURL_CONNECT_TIMEOUT"
)

format_gitea_error_response() {
    local response_file="$1"
    python3 - "$response_file" <<'PY'
import json
import sys

with open(sys.argv[1], "rb") as handle:
    raw = handle.read(65536)
try:
    response = json.loads(raw.decode("utf-8", errors="replace"))
except (UnicodeDecodeError, json.JSONDecodeError):
    message = "non-JSON response omitted"
else:
    if isinstance(response, dict):
        message = response.get("message") or response.get("error")
        if not message and response.get("errors") is not None:
            message = json.dumps(response["errors"], separators=(",", ":"))
    else:
        message = None
    if not message:
        message = "JSON response contained no error message"
message = str(message)
if len(message) > 500:
    message = message[:500] + "..."
print(ascii(message))
PY
}

cleanup_merge_temp_dirs() {
    local path
    for path in "${MERGE_TEMP_DIRS[@]}"; do
        [[ -n "$path" ]] && rm -rf -- "$path"
    done
}
trap cleanup_merge_temp_dirs EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

fetch_gitea_pr_head() {
    local host="$1" auth_mode="$2" credential="$3" work_root="$4"
    local response_file raw_code api_url auth_config curl_rc
    response_file=$(mktemp "$work_root/pr-merge-pr.XXXXXX")
    api_url="https://${host}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}"
    if ! auth_config=$(write_curl_auth_config "$auth_mode" "$credential"); then
        echo "Error: Could not construct Gitea authentication config; refusing request." >&2
        rm -f "$response_file"
        return 1
    fi
    raw_code=$(curl -sS -K - "${GITEA_CURL_BOUNDS[@]}" -w '%{http_code}' -o "$response_file" \
        -H "User-Agent: curl/8" "$api_url" <<<"$auth_config")
    curl_rc=$?
    LAST_GITEA_HTTP_CODE="${raw_code:-000}"
    if [[ "$curl_rc" -ne 0 ]]; then
        LAST_GITEA_ERROR="curl transport failed (rc=$curl_rc)"
        rm -f "$response_file"
        return 1
    fi
    if [[ ! "$raw_code" =~ ^2 ]]; then
        LAST_GITEA_ERROR=$(format_gitea_error_response "$response_file")
        rm -f "$response_file"
        return 1
    fi
    if ! python3 - "$response_file" <<'PY'
import json
import re
import sys

with open(sys.argv[1], encoding="utf-8") as handle:
    pull = json.load(handle)
head = pull.get("head") if isinstance(pull, dict) else None
sha = str(head.get("sha") or "") if isinstance(head, dict) else ""
if not re.fullmatch(r"[0-9a-fA-F]{40}", sha):
    raise SystemExit(1)
print(sha)
PY
    then
        echo "Error: Gitea PR response has no valid head SHA; refusing merge." >&2
        rm -f "$response_file"
        return 1
    fi
    rm -f "$response_file"
}

fetch_gitea_pr_commits() {
    local host="$1" auth_mode="$2" credential="$3" work_root="$4"
    local page page_file combined_file merged_file raw_code page_count api_url auth_config curl_rc
    mkdir -p "$work_root"
    if ! auth_config=$(write_curl_auth_config "$auth_mode" "$credential"); then
        echo "Error: Could not construct Gitea authentication config; refusing request." >&2
        return 1
    fi
    combined_file=$(mktemp "$work_root/pr-merge-commits.XXXXXX")
    printf '[]' > "$combined_file"

    page=1
    while true; do
        page_file=$(mktemp "$work_root/pr-merge-commits-page.XXXXXX")
        api_url="https://${host}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/commits?limit=50&page=${page}"
        raw_code=$(curl -sS -K - "${GITEA_CURL_BOUNDS[@]}" -w '%{http_code}' -o "$page_file" \
            -H "User-Agent: curl/8" "$api_url" <<<"$auth_config")
        curl_rc=$?
        LAST_GITEA_HTTP_CODE="${raw_code:-000}"
        if [[ "$curl_rc" -ne 0 ]]; then
            LAST_GITEA_ERROR="curl transport failed (rc=$curl_rc)"
            rm -f "$page_file" "$combined_file"
            return 1
        fi
        if [[ ! "$raw_code" =~ ^2 ]]; then
            LAST_GITEA_ERROR=$(format_gitea_error_response "$page_file")
            rm -f "$page_file" "$combined_file"
            return 1
        fi

        if ! page_count=$(python3 - "$page_file" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as handle:
    page = json.load(handle)
if not isinstance(page, list):
    raise SystemExit(1)
print(len(page))
PY
        ); then
            echo "Error: Gitea PR commits response is not a JSON array; refusing merge." >&2
            rm -f "$page_file" "$combined_file"
            return 1
        fi

        merged_file=$(mktemp "$work_root/pr-merge-commits-merged.XXXXXX")
        if ! python3 - "$combined_file" "$page_file" > "$merged_file" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as handle:
    combined = json.load(handle)
with open(sys.argv[2], encoding="utf-8") as handle:
    page = json.load(handle)
json.dump(combined + page, sys.stdout, separators=(",", ":"))
PY
        then
            echo "Error: Could not combine paginated PR commit metadata; refusing merge." >&2
            rm -f "$page_file" "$combined_file" "$merged_file"
            return 1
        fi
        mv "$merged_file" "$combined_file"
        rm -f "$page_file"

        if [[ "$page_count" -lt 50 ]]; then
            break
        fi
        page=$((page + 1))
        if [[ "$page" -gt 1000 ]]; then
            echo "Error: PR commit pagination exceeded 1000 pages; refusing merge." >&2
            rm -f "$combined_file"
            return 1
        fi
    done

    cat "$combined_file"
    rm -f "$combined_file"
}

# LIMITATION: author.login resolution proves the commit address maps to a registered account.
# It does NOT prove the named principal authored the commit — git author metadata is self-asserted.
# This gate checks ATTRIBUTION LINKAGE, not AUTHORSHIP. Commit signing is out of scope and unadopted.
build_coauthor_message_fields() {
    local commits_file="$1" context_file="$2" head_file="$3"
    python3 - "$commits_file" "$context_file" "$head_file" <<'PY'
import json
import re
import sys

commits_path, context_path, head_path = sys.argv[1:]
with open(commits_path, encoding="utf-8") as handle:
    commits = json.load(handle)
head_sha = open(head_path, encoding="utf-8").read().strip()
context_parts = open(context_path, "rb").read().split(b"\0")
if len(context_parts) != 4 or context_parts[-1] != b"":
    raise SystemExit(1)
poster, title, principal = (part.decode("utf-8") for part in context_parts[:3])

if not isinstance(commits, list) or not commits:
    print(
        f"BLOCK: provider returned no PR commits; author identity is unmeasurable. "
        f"Refusing merge; escalate to named principal '{principal}'.",
        file=sys.stderr,
    )
    raise SystemExit(75)
if not poster:
    print(
        f"BLOCK: PR poster login is empty; refusing merge; "
        f"escalate to named principal '{principal}'.",
        file=sys.stderr,
    )
    raise SystemExit(75)

if not re.fullmatch(r"[0-9a-fA-F]{40}", head_sha):
    print(
        f"BLOCK: inspected PR head SHA is invalid; refusing merge; "
        f"escalate to named principal '{principal}'.",
        file=sys.stderr,
    )
    raise SystemExit(75)

seen = set()
trailers = []
head_seen = False
for item in commits:
    if not isinstance(item, dict):
        print(f"BLOCK: malformed PR commit metadata; escalate to named principal '{principal}'.", file=sys.stderr)
        raise SystemExit(75)
    sha = str(item.get("sha") or "<unknown>")
    if sha == head_sha:
        head_seen = True
    commit = item.get("commit") if isinstance(item.get("commit"), dict) else {}
    commit_author = commit.get("author") if isinstance(commit.get("author"), dict) else {}
    email = str(commit_author.get("email") or "").strip()
    provider_author = item.get("author") if isinstance(item.get("author"), dict) else {}
    login = str(provider_author.get("login") or "").strip()

    if not login:
        diagnostic_email = email or "<missing>"
        print(
            f"BLOCK: commit {sha!r} has author.login=NULL while "
            f"commit.author.email={diagnostic_email!r}; refusing merge; "
            f"escalate to named principal '{principal}'.",
            file=sys.stderr,
        )
        raise SystemExit(75)
    if (
        not email.isascii()
        or not email.isprintable()
        or not re.fullmatch(r"[A-Za-z0-9_.-]+", login)
        or not re.fullmatch(r"[^<>\s]+@[^<>\s]+", email)
    ):
        print(
            f"BLOCK: commit {sha!r} has unusable linked identity "
            f"author.login={login!r}, commit.author.email={email!r}; refusing merge; "
            f"escalate to named principal '{principal}'.",
            file=sys.stderr,
        )
        raise SystemExit(75)
    if login == poster or login in seen:
        continue
    seen.add(login)
    trailers.append(f"Co-authored-by: {login} <{email}>")

if not head_seen:
    print(
        f"BLOCK: inspected PR head is absent from commit enumeration; refusing merge; "
        f"escalate to named principal '{principal}'.",
        file=sys.stderr,
    )
    raise SystemExit(75)
if not trailers:
    print("{}")
    raise SystemExit(0)
if not title:
    print(
        f"BLOCK: PR title is empty; refusing merge; escalate to named principal '{principal}'.",
        file=sys.stderr,
    )
    raise SystemExit(75)
if not title.isprintable() or re.match(r"^[A-Za-z-]+-[Bb]y:", title):
    print(
        f"BLOCK: PR title is not one printable, non-trailer line; refusing merge; "
        f"escalate to named principal '{principal}'.",
        file=sys.stderr,
    )
    raise SystemExit(75)

print(json.dumps({
    "MergeTitleField": title,
    "MergeMessageField": "\n".join(trailers),
}, separators=(",", ":")))
PY
}

merge_gitea_api_attempt() {
    local host="$1" auth_mode="$2" credential="$3"
    local api_url attempt_dir body_file raw_code commits_file fields_file context_file head_file payload_file work_root attempt_rc auth_config curl_rc
    LAST_GITEA_HTTP_CODE="000"
    LAST_GITEA_ERROR=""
    api_url="https://${host}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/merge"
    work_root="${AGENT_WORK_ROOT:-${HOME:-/tmp}/mosaic/agent-work}"
    mkdir -p "$work_root"
    attempt_dir=$(mktemp -d "$work_root/pr-merge-attempt.XXXXXX")
    chmod 0700 "$attempt_dir"
    MERGE_TEMP_DIRS+=("$attempt_dir")
    body_file=$(mktemp "$attempt_dir/api-response.XXXXXX")
    fields_file=$(mktemp "$attempt_dir/message-fields.XXXXXX")
    payload_file=$(mktemp "$attempt_dir/payload.XXXXXX")
    printf '{}' > "$fields_file"

    if [[ "$CO_AUTHOR_TRAILERS" == true ]]; then
        commits_file=$(mktemp "$attempt_dir/pr-merge-commits-input.XXXXXX")
        context_file=$(mktemp "$attempt_dir/pr-merge-message-context.XXXXXX")
        head_file=$(mktemp "$attempt_dir/pr-merge-head-input.XXXXXX")
        printf '%s\0%s\0%s\0' "$PR_AUTHOR" "$PR_TITLE" "$ESCALATE_TO" > "$context_file"
        if fetch_gitea_pr_head "$host" "$auth_mode" "$credential" "$attempt_dir" > "$head_file"; then
            :
        else
            attempt_rc=$?
            rm -f "$body_file" "$fields_file" "$payload_file" "$commits_file" "$context_file" "$head_file"
            return "$attempt_rc"
        fi
        if [[ "$(<"$head_file")" != "$HEAD_SHA" ]]; then
            echo "BLOCK: authenticated PR head moved from reviewed $HEAD_SHA to $(<"$head_file"); refusing merge; escalate to named principal '$ESCALATE_TO'." >&2
            rm -f "$body_file" "$fields_file" "$payload_file" "$commits_file" "$context_file" "$head_file"
            return 75
        fi
        if fetch_gitea_pr_commits "$host" "$auth_mode" "$credential" "$attempt_dir" > "$commits_file"; then
            :
        else
            attempt_rc=$?
            rm -f "$body_file" "$fields_file" "$payload_file" "$commits_file" "$context_file" "$head_file"
            return "$attempt_rc"
        fi
        if build_coauthor_message_fields "$commits_file" "$context_file" "$head_file" > "$fields_file"; then
            :
        else
            attempt_rc=$?
            rm -f "$body_file" "$fields_file" "$payload_file" "$commits_file" "$context_file" "$head_file"
            return "$attempt_rc"
        fi
        rm -f "$commits_file" "$context_file" "$head_file"
    fi

    if ! python3 - "$fields_file" "$HEAD_SHA" "$DELETE_BRANCH" > "$payload_file" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as handle:
    fields = json.load(handle)
head_sha, delete_branch = sys.argv[2:]
payload = {"Do": "squash", "head_commit_id": head_sha}
if delete_branch == "true":
    payload["delete_branch_after_merge"] = True
payload.update(fields)
allowed = {"Do", "head_commit_id", "delete_branch_after_merge", "MergeTitleField", "MergeMessageField"}
if payload.get("Do") != "squash" or set(payload) - allowed:
    raise SystemExit(1)
print(json.dumps(payload, separators=(",", ":")))
PY
    then
        rm -f "$body_file" "$fields_file" "$payload_file"
        return 1
    fi
    rm -f "$fields_file"

    if ! auth_config=$(write_curl_auth_config "$auth_mode" "$credential"); then
        echo "Error: Could not construct Gitea authentication config; refusing request." >&2
        rm -f "$body_file" "$payload_file"
        return 1
    fi
    raw_code=$(curl -sS -K - "${GITEA_CURL_BOUNDS[@]}" -w '%{http_code}' -o "$body_file" \
        -X POST -H "User-Agent: curl/8" \
        -H 'Content-Type: application/json' \
        --data-binary "@$payload_file" "$api_url" <<<"$auth_config")
    curl_rc=$?
    LAST_GITEA_HTTP_CODE="${raw_code:-000}"
    if [[ "$curl_rc" -ne 0 ]]; then
        LAST_GITEA_ERROR="curl transport failed (rc=$curl_rc)"
        rm -f "$body_file" "$payload_file"
        rm -rf -- "$attempt_dir"
        return 1
    fi
    if [[ ! "$raw_code" =~ ^2 ]]; then
        LAST_GITEA_ERROR=$(format_gitea_error_response "$body_file")
    fi
    rm -f "$body_file" "$payload_file"
    rm -rf -- "$attempt_dir"
    [[ "$raw_code" =~ ^2 ]]
}

merge_gitea_with_api() {
    local host="$1" token attempt_rc

    if ! token=$(get_gitea_token "$host"); then
        echo "Error: Could not resolve the required Gitea token; refusing merge without changing principals." >&2
        return 1
    fi
    if [[ -z "$token" ]]; then
        echo "Error: Required Gitea token resolved empty; refusing merge without changing principals." >&2
        return 1
    fi
    if merge_gitea_api_attempt "$host" token "$token"; then
        return 0
    else
        attempt_rc=$?
    fi
    if [[ "$attempt_rc" -eq 75 ]]; then
        return 75
    fi
    if [[ "$LAST_GITEA_HTTP_CODE" != "401" ]]; then
        echo "Error: Gitea API merge failed with the identity-bound token (HTTP ${LAST_GITEA_HTTP_CODE:-000}).${LAST_GITEA_ERROR:+ Provider response: $LAST_GITEA_ERROR}" >&2
        return 1
    fi
    echo "Error: Gitea API rejected the identity-bound token with HTTP 401; refusing cross-principal credential fallback." >&2
    return 1
}

if [[ "$DRY_RUN" == true ]]; then
    if [[ "$PLATFORM" == "gitea" ]]; then
        HOST=$(get_remote_host) || {
            echo "Error: Cannot determine host from origin remote URL" >&2
            exit 1
        }
        if [[ "$CO_AUTHOR_TRAILERS" == true ]]; then
            echo "Dry run: would verify PR commit authors and merge PR #$PR_NUMBER on $HOST with authenticated Gitea API message fields (base=$BASE_BRANCH, method=squash)."
        else
            echo "Dry run: would merge PR #$PR_NUMBER on $HOST with the authenticated exact-head Gitea API path (base=$BASE_BRANCH, method=squash)."
        fi
    else
        echo "Dry run: would merge PR #$PR_NUMBER on $PLATFORM (base=$BASE_BRANCH, method=squash)."
    fi
    exit 0
fi

case "$PLATFORM" in
    github)
        if [[ "$CO_AUTHOR_TRAILERS" == true ]]; then
            echo "Error: --co-author-trailers currently requires the Gitea REST message-field contract." >&2
            exit 1
        fi
        cmd=(gh pr merge "$PR_NUMBER" --squash --match-head-commit "$HEAD_SHA")
        [[ "$DELETE_BRANCH" == true ]] && cmd+=(--delete-branch)
        "${cmd[@]}"
        ;;
    gitea)
        HOST=$(get_remote_host) || {
            echo "Error: Cannot determine host from origin remote URL" >&2
            exit 1
        }
        # Gitea's API head_commit_id is an atomic compare-and-merge precondition.
        # tea cannot express it, so every Gitea merge uses the authenticated API path.
        merge_gitea_with_api "$HOST"
        ;;
    *)
        echo "Error: Could not detect git platform" >&2
        exit 1
        ;;
esac

echo "PR #$PR_NUMBER merged successfully"
