#!/bin/bash
# pr-create.sh - Create pull requests on Gitea or GitHub
# Usage: pr-create.sh -t "Title" [-b "Body"] [-B base] [-H head] [-l "labels"] [-m "milestone"]

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/detect-platform.sh"

# Default values
TITLE=""
BODY=""
BASE_BRANCH=""
HEAD_BRANCH=""
LABELS=""
MILESTONE=""
DRAFT=false
ISSUE=""

# get_remote_host, get_gitea_token, get_repo_info, and get_gitea_repo_args are provided by detect-platform.sh

gitea_default_branch() {
    # Forge default branch for the current repo (T51-P2 WP5a / spec E4): the
    # API fallback must not guess a base. Empty output or any lookup failure
    # returns nonzero so the caller fails loud instead of mistargeting a PR.
    local host repo token url body branch
    host=$(get_remote_host) || return 1
    repo=$(get_repo_info) || return 1
    token=$(get_gitea_token "$host") || return 1
    url="https://${host}/api/v1/repos/${repo}"
    # Fetch and parse as separate steps (T51P2WP5AR B2): a piped
    # `curl | python` reports only python's status, so an HTTP failure that
    # still emits parseable JSON would masquerade as success. curl's own
    # exit status is authoritative here.
    if ! body=$(curl -fsS \
        -H "User-Agent: curl/8" \
        -H "Authorization: token ${token}" \
        "$url" 2>/dev/null); then
        return 1
    fi
    # A valid base is a NONBLANK JSON STRING (T51P2WP5AR B3): null, numbers,
    # and whitespace-only values are failed resolution, never a POSTed base.
    branch=$(printf '%s' "$body" | python3 -c '
import json, sys
try:
    value = json.load(sys.stdin).get("default_branch")
except Exception:
    sys.exit(1)
if not isinstance(value, str) or not value.strip():
    sys.exit(1)
print(value.strip())
' 2>/dev/null) || return 1
    [[ -n "$branch" ]] || return 1
    printf '%s' "$branch"
}

gitea_pr_create_api() {
    local host repo token url payload
    host=$(get_remote_host) || {
        echo "Error: could not determine remote host for API fallback" >&2
        return 1
    }
    repo=$(get_repo_info) || {
        echo "Error: could not determine repo owner/name for API fallback" >&2
        return 1
    }
    token=$(get_gitea_token "$host") || {
        echo "Error: Gitea token not found for API fallback (set GITEA_TOKEN or configure ~/.git-credentials)" >&2
        return 1
    }

    if [[ -n "$LABELS" || -n "$MILESTONE" || "$DRAFT" == true ]]; then
        echo "Warning: API fallback applies title/body/head/base only; labels/milestone/draft require authenticated tea setup." >&2
    fi

    # Base resolution (spec E4): an explicit -B always wins; with none, the
    # forge default branch is resolved from the provider API -- never the
    # historical "main" literal, which mistargeted every fallback PR on
    # repos whose trunk is not main (e.g. mosaicstack/stack -> next).
    local api_base=""
    if [[ -n "$EFFECTIVE_BASE" ]]; then
        api_base="$EFFECTIVE_BASE"
    else
        api_base=$(gitea_default_branch) || {
            echo "Error: could not resolve the forge default branch for the API-fallback base; pass -B <branch> explicitly" >&2
            return 1
        }
    fi

    payload=$(TITLE="$TITLE" BODY="$BODY" HEAD_BRANCH="$HEAD_BRANCH" API_BASE="$api_base" python3 - <<'PY'
import json
import os

payload = {
    "title": os.environ["TITLE"],
    "head": os.environ["HEAD_BRANCH"],
    "base": os.environ["API_BASE"],
}
body = os.environ.get("BODY", "")
if body:
    payload["body"] = body
print(json.dumps(payload))
PY
)

    url="https://${host}/api/v1/repos/${repo}/pulls"
    curl -fsS -X POST \
      -H "User-Agent: curl/8" \
      -H "Authorization: token ${token}" \
      -H "Content-Type: application/json" \
      -d "$payload" \
      "$url"
}

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

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

Options:
    -t, --title TITLE       PR title (required, or use --issue)
    -b, --body BODY         PR description/body
    -B, --base BRANCH       Base branch to merge into (default: the forge repository's default branch)
    -H, --head BRANCH       Head branch with changes (default: current branch)
    -l, --labels LABELS     Comma-separated labels
    -m, --milestone NAME    Milestone name
    -i, --issue NUMBER      Link to issue (auto-generates title if not provided)
    -d, --draft             Create as draft PR
    -h, --help              Show this help message

Examples:
    $(basename "$0") -t "Add login feature" -b "Implements user authentication"
    $(basename "$0") -t "Fix bug" -B main -H feature/fix-123
    $(basename "$0") -i 42 -b "Implements the feature described in #42"
    $(basename "$0") -t "WIP: New feature" --draft
EOF
    exit "${1:-1}"
}

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -t|--title)
            TITLE="$2"
            shift 2
            ;;
        -b|--body)
            BODY="$2"
            shift 2
            ;;
        -B|--base)
            BASE_BRANCH="$2"
            shift 2
            ;;
        -H|--head)
            HEAD_BRANCH="$2"
            shift 2
            ;;
        -l|--labels)
            LABELS="$2"
            shift 2
            ;;
        -m|--milestone)
            MILESTONE="$2"
            shift 2
            ;;
        -i|--issue)
            ISSUE="$2"
            shift 2
            ;;
        -d|--draft)
            DRAFT=true
            shift
            ;;
        -h|--help)
            usage 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            usage
            ;;
    esac
done

# If no title but issue provided, generate title
if [[ -z "$TITLE" ]] && [[ -n "$ISSUE" ]]; then
    TITLE="Fixes #$ISSUE"
fi

if [[ -z "$TITLE" ]]; then
    echo "Error: Title is required (-t) or provide an issue (-i)" >&2
    usage
fi

# Default head branch to current branch
if [[ -z "$HEAD_BRANCH" ]]; then
    HEAD_BRANCH=$(git branch --show-current)
fi

# T51 WP5b: declaration-driven base resolution (spec 4.1). Precedence:
#   explicit -B  -> validated as an ALLOWED transition (4.2: a flag is input,
#                    not authority) when a consumable declaration exists
#   declared trunk (v2 declarations only) -> used directly
#   legacy        -> WP5a forge-default floor (unmanaged/absent/v1, 4.3)
# shellcheck source=packages/mosaic/framework/tools/git/repo-decl.sh
if [ -f "$SCRIPT_DIR/repo-decl.sh" ]; then
    source "$SCRIPT_DIR/repo-decl.sh"
    repo_decl_load
else
    DECL_STATE=absent; DECL_SCHEMA=""
    repo_decl_warn() { printf 'repo-decl: %s\n' "$*" >&2; }
    repo_decl_report_invalid() { :; }
    repo_decl_warn_absent_reversible() { :; }
    repo_decl_warn_absent_irreversible() { :; }
    repo_decl_remote_matches() { return 0; }
    repo_decl_check_transition() { return 2; }
fi
EFFECTIVE_BASE="$BASE_BRANCH"
case "$DECL_STATE" in
    invalid) repo_decl_report_invalid ;;
esac
if [[ "$DECL_STATE" == valid && "$DECL_SCHEMA" != 2 ]]; then
    repo_decl_warn "declaration is v$DECL_SCHEMA — carries no consumable flow/trunk fields; legacy behavior"
fi
if [[ "$DECL_STATE" == valid && "$DECL_SCHEMA" == 2 ]]; then
    # Write path: a normalized-remote mismatch refuses (spec 5.3).
    if ! repo_decl_remote_matches; then
        echo "Error: origin remote does not match the declared canonical_remote (spec 5.3, write path) — refusing to create a PR against the wrong forge. Fix the origin remote or the declaration." >&2
        exit 1
    fi
    if [[ -n "$BASE_BRANCH" ]]; then
        trc=0
        repo_decl_check_transition "$HEAD_BRANCH" "$BASE_BRANCH" || trc=$?
        if [[ "$trc" == 1 ]]; then
            echo "Error: -B '$BASE_BRANCH' is not an allowed transition for head '$HEAD_BRANCH' under the declared flow (spec 4.2). The declaration governs; supply an allowed base." >&2
            exit 1
        fi
        # trc 2 cannot happen here (state=valid): 0 = allowed
    else
        EFFECTIVE_BASE="$DECL_TRUNK"
    fi
elif [[ -z "$BASE_BRANCH" ]]; then
    repo_decl_warn_absent_reversible "pr-create"
fi

# Add issue reference to body if provided
if [[ -n "$ISSUE" ]]; then
    if [[ -n "$BODY" ]]; then
        BODY="$BODY

Fixes #$ISSUE"
    else
        BODY="Fixes #$ISSUE"
    fi
fi

PLATFORM=$(detect_platform)

case "$PLATFORM" in
    github)
        CMD=(gh pr create --title "$TITLE")
        [[ -n "$BODY" ]] && CMD+=(--body "$BODY")
        [[ -n "$EFFECTIVE_BASE" ]] && CMD+=(--base "$EFFECTIVE_BASE")
        [[ -n "$HEAD_BRANCH" ]] && CMD+=(--head "$HEAD_BRANCH")
        [[ -n "$LABELS" ]] && CMD+=(--label "$LABELS")
        [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE")
        [[ "$DRAFT" == true ]] && CMD+=(--draft)
        "${CMD[@]}"
        ;;
    gitea)
        # tea pull create syntax. Always pass --repo because tea repo inference
        # is unreliable in Mosaic worktrees/profile shells. Use arrays instead
        # of eval so markdown backticks/body content are not shell-executed.
        REPO_SLUG=$(get_repo_slug)
        GITEA_LOGIN_NAME=$(get_gitea_login) || {
            echo "Warning: could not resolve Gitea login for tea; trying Gitea API fallback..." >&2
            gitea_pr_create_api
            exit $?
        }
        if ! get_gitea_authenticated_user "$GITEA_LOGIN_NAME" >/dev/null; then
            echo "Warning: Tea authenticated-user validation failed (possible stale user/login); trying Gitea API fallback..." >&2
            gitea_pr_create_api
            exit $?
        fi
        REPO_ARGS=(--repo "$REPO_SLUG" --login "$GITEA_LOGIN_NAME")
        CMD=(tea pr create "${REPO_ARGS[@]}" --title "$TITLE")
        [[ -n "$BODY" ]] && CMD+=(--description "$BODY")
        [[ -n "$EFFECTIVE_BASE" ]] && CMD+=(--base "$EFFECTIVE_BASE")
        [[ -n "$HEAD_BRANCH" ]] && CMD+=(--head "$HEAD_BRANCH")

        # Handle labels for tea
        if [[ -n "$LABELS" ]]; then
            # tea may use --labels flag
            CMD+=(--labels "$LABELS")
        fi

        # Handle milestone for tea
        if [[ -n "$MILESTONE" ]]; then
            MILESTONE_ID=$(tea milestones list "${REPO_ARGS[@]}" 2>/dev/null | grep -E "^\s*[0-9]+" | grep "$MILESTONE" | awk '{print $1}' | head -1)
            if [[ -n "$MILESTONE_ID" ]]; then
                CMD+=(--milestone "$MILESTONE_ID")
            else
                echo "Warning: Could not find milestone '$MILESTONE', creating without milestone" >&2
            fi
        fi

        # Note: tea may not support --draft flag in all versions
        if [[ "$DRAFT" == true ]]; then
            echo "Note: Draft PR may not be supported by your tea version" >&2
        fi

        if "${CMD[@]}"; then
            exit 0
        fi
        echo "Warning: tea pr create failed, trying Gitea API fallback..." >&2
        { declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true
        gitea_pr_create_api
        ;;
    *)
        echo "Error: Could not detect git platform" >&2
        exit 1
        ;;
esac
