#!/usr/bin/env bash

set -Eeuo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VERSION_FILE="$ROOT_DIR/app/build.gradle.kts"
REMOTE_NAME="${GIT_REMOTE:-origin}"
NOTES_FILE=""
SKIP_TESTS=0
GOOGLE_PLAY_MODE="ask"
REUSE_CURRENT=0
VERSION_FILE_CHANGED=0
VERSION_COMMITTED=0
VERSION_BACKUP=""
RELEASE_SUCCEEDED=0
GOOGLE_PLAY_ACCESS_TOKEN=""
GOOGLE_PLAY_EDIT_ID=""
GOOGLE_PLAY_TEMP_DIR=""
GOOGLE_PLAY_CURRENT_VERSION_STATE=""
GOOGLE_PLAY_SUMMARY="pominięto"

usage() {
    cat <<'EOF'
Użycie: scripts/release-android.sh [opcje]

Buduje i publikuje kolejną wersję aplikacji kierowcy:
  - automatycznie zwiększa versionCode o 1 i patch versionName o 1,
  - uruchamia testy jednostkowe,
  - buduje podpisany APK dla Gitea Release,
  - buduje podpisany AAB i publikuje go w teście zamkniętym Google Play,
  - wysyła zmiany Google Play do weryfikacji,
  - zapisuje zmianę wersji w commicie, tworzy tag i wykonuje push,
  - tworzy lub aktualizuje Gitea Release i wysyła APK oraz SHA256SUMS.

Opcje:
  --notes-file PLIK   Treść release notes z podanego pliku.
  --skip-tests        Pomiń testDebugUnitTest.
  --skip-google-play  Nie pytaj i pomiń wysyłanie AAB do Google Play.
  --publish-google-play
                      Nie pytaj i wyślij AAB do Google Play.
  --reuse-current     Nie zwiększaj wersji. Wznów publikację wersji zapisanej
                      obecnie w app/build.gradle.kts.
  -h, --help          Pokaż pomoc.

Zmienne środowiskowe:
  GITEA_TOKEN         Token API Gitea. Jeśli go brak, używany jest git credential helper.
  GITEA_BASE_URL      Nadpisuje adres Gitea, np. https://gitea.example.com.
  GITEA_REPOSITORY    Nadpisuje owner/repository.
  GIT_REMOTE          Remote Git, domyślnie origin.
  GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS
                      Opcjonalne nadpisanie automatycznie znalezionego klucza JSON.
                      Domyślnie skrypt znajduje konto serwisowe w scripts/*.json.
                      Alternatywnie: GOOGLE_APPLICATION_CREDENTIALS.
  GOOGLE_PLAY_PACKAGE_NAME
                      Identyfikator aplikacji, domyślnie pl.firmatpp.kierowca.
  GOOGLE_PLAY_TRACK   Nazwa testu zamkniętego, domyślnie alpha.
  GOOGLE_PLAY_RELEASE_LANGUAGE
                      Język informacji o wersji, domyślnie pl-PL.

Wersja początkowa jest zawsze odczytywana z app/build.gradle.kts.
Google Play otrzymuje AAB przez Android Publisher API. Konto serwisowe musi mieć
uprawnienie „Publikowanie aplikacji na ścieżkach testowych”.
EOF
}

while (($# > 0)); do
    case "$1" in
        --notes-file)
            NOTES_FILE="${2:-}"
            [[ -n "$NOTES_FILE" ]] || { echo "Brak wartości dla --notes-file." >&2; exit 2; }
            shift 2
            ;;
        --skip-tests)
            SKIP_TESTS=1
            shift
            ;;
        --skip-google-play)
            GOOGLE_PLAY_MODE="skip"
            shift
            ;;
        --publish-google-play)
            GOOGLE_PLAY_MODE="publish"
            shift
            ;;
        --reuse-current)
            REUSE_CURRENT=1
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            echo "Nieznana opcja: $1" >&2
            usage >&2
            exit 2
            ;;
    esac
done

cd "$ROOT_DIR"

for command in git curl jq perl sha256sum jarsigner; do
    command -v "$command" >/dev/null 2>&1 || {
        echo "Brak wymaganego polecenia: $command" >&2
        exit 1
    }
done

[[ -x ./gradlew ]] || { echo "Brak wykonywalnego ./gradlew." >&2; exit 1; }
[[ -f keystore.properties ]] || { echo "Brak keystore.properties wymaganego do podpisania release." >&2; exit 1; }
[[ -f "$VERSION_FILE" ]] || { echo "Brak $VERSION_FILE." >&2; exit 1; }
if [[ -n "$NOTES_FILE" && ! -f "$NOTES_FILE" ]]; then
    echo "Nie istnieje plik release notes: $NOTES_FILE" >&2
    exit 1
fi

branch="$(git branch --show-current)"
[[ -n "$branch" ]] || { echo "Release nie może być wykonany z detached HEAD." >&2; exit 1; }

if ! git diff --quiet || ! git diff --cached --quiet; then
    echo "Repozytorium ma niezapisane zmiany śledzonych plików. Najpierw wykonaj commit." >&2
    exit 1
fi

relevant_untracked="$(git status --porcelain --untracked-files=all -- app gradle scripts build.gradle.kts settings.gradle.kts gradle.properties | awk '$1 == "??" { print }')"
if [[ -n "$relevant_untracked" ]]; then
    echo "Repozytorium ma nieśledzone pliki źródłowe. Najpierw zdecyduj, czy mają wejść do release:" >&2
    printf '%s\n' "$relevant_untracked" >&2
    exit 1
fi

git fetch --tags "$REMOTE_NAME"

remote_url="$(git remote get-url "$REMOTE_NAME")"
sanitized_remote="$(printf '%s' "$remote_url" | sed -E 's#^(https?://)[^/@]+@#\1#')"
if [[ "$sanitized_remote" =~ ^(https?)://([^/]+)/(.+)$ ]]; then
    detected_protocol="${BASH_REMATCH[1]}"
    detected_host="${BASH_REMATCH[2]}"
    detected_repository="${BASH_REMATCH[3]%.git}"
else
    detected_protocol="https"
    detected_host=""
    detected_repository=""
fi

gitea_base_url="${GITEA_BASE_URL:-${detected_protocol}://${detected_host}}"
gitea_repository="${GITEA_REPOSITORY:-$detected_repository}"
gitea_base_url="${gitea_base_url%/}"
[[ "$gitea_base_url" =~ ^https?://[^/]+$ ]] || { echo "Ustaw poprawne GITEA_BASE_URL." >&2; exit 1; }
[[ "$gitea_repository" =~ ^[^/]+/[^/]+$ ]] || { echo "Ustaw poprawne GITEA_REPOSITORY=owner/repository." >&2; exit 1; }

auth_args=()
if [[ -n "${GITEA_TOKEN:-}" ]]; then
    auth_args=(-H "Authorization: token $GITEA_TOKEN")
else
    credential="$(printf 'protocol=%s\nhost=%s\n\n' "$detected_protocol" "$detected_host" | git credential fill)"
    gitea_username="$(printf '%s\n' "$credential" | sed -n 's/^username=//p')"
    gitea_password="$(printf '%s\n' "$credential" | sed -n 's/^password=//p')"
    [[ -n "$gitea_username" && -n "$gitea_password" ]] || {
        echo "Brak danych Gitea. Ustaw GITEA_TOKEN albo skonfiguruj git credential helper." >&2
        exit 1
    }
    auth_args=(-u "$gitea_username:$gitea_password")
fi

api_base="$gitea_base_url/api/v1/repos/$gitea_repository"
preflight_status="$(curl -sS "${auth_args[@]}" -o /dev/null -w '%{http_code}' "$api_base")"
[[ "$preflight_status" == "200" ]] || {
    echo "Brak dostępu do repozytorium Gitea $gitea_repository (HTTP $preflight_status)." >&2
    exit 1
}

read_version() {
    local code name
    code="$(sed -nE 's/^[[:space:]]*versionCode = ([0-9]+).*$/\1/p' "$VERSION_FILE" | sed -n '1p')"
    name="$(sed -nE 's/^[[:space:]]*versionName = "([^"]+)".*$/\1/p' "$VERSION_FILE" | sed -n '1p')"
    [[ "$code" =~ ^[0-9]+$ ]] || { echo "Nie można odczytać versionCode." >&2; return 1; }
    [[ "$name" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "versionName musi mieć format major.minor.patch." >&2; return 1; }
    printf '%s %s\n' "$code" "$name"
}

read -r current_code current_name < <(read_version)
if ((REUSE_CURRENT == 1)); then
    next_code="$current_code"
    next_name="$current_name"
else
    IFS=. read -r version_major version_minor version_patch <<<"$current_name"
    next_code=$((current_code + 1))
    next_name="${version_major}.${version_minor}.$((version_patch + 1))"
fi

tag_name="v${next_name}-${next_code}"
artifact_dir="$ROOT_DIR/release-artifacts/$tag_name"
gitea_apk_name="TPP-Kierowca-${next_name}-${next_code}.apk"
play_aab_name="TPP-Kierowca-${next_name}-${next_code}.aab"

cleanup_on_exit() {
    local exit_code=$?
    if ((RELEASE_SUCCEEDED == 0)) && [[ -n "$GOOGLE_PLAY_EDIT_ID" && -n "$GOOGLE_PLAY_ACCESS_TOKEN" ]]; then
        package_encoded="$(urlencode "$google_play_package")"
        edit_encoded="$(urlencode "$GOOGLE_PLAY_EDIT_ID")"
        curl -sS -o /dev/null \
            --connect-timeout 20 \
            --max-time 60 \
            -X DELETE \
            -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
            "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/$package_encoded/edits/$edit_encoded" || true
    fi
    if ((RELEASE_SUCCEEDED == 0 && VERSION_FILE_CHANGED == 1 && VERSION_COMMITTED == 0)) && [[ -n "$VERSION_BACKUP" && -f "$VERSION_BACKUP" ]]; then
        cp "$VERSION_BACKUP" "$VERSION_FILE"
        echo "Przywrócono poprzednią wersję w app/build.gradle.kts." >&2
    fi
    [[ -n "$VERSION_BACKUP" && -f "$VERSION_BACKUP" ]] && rm -f "$VERSION_BACKUP"
    if [[ -n "$GOOGLE_PLAY_TEMP_DIR" && -d "$GOOGLE_PLAY_TEMP_DIR" ]]; then
        rm -rf -- "$GOOGLE_PLAY_TEMP_DIR"
    fi
    if ((RELEASE_SUCCEEDED == 0)); then
        echo "Release nie został ukończony. Po usunięciu przyczyny użyj --reuse-current, jeśli commit wersji już powstał." >&2
    fi
    trap - EXIT INT TERM
    exit "$exit_code"
}
trap cleanup_on_exit EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

urlencode() {
    jq -rn --arg value "$1" '$value | @uri'
}

base64url() {
    openssl base64 -A | tr '+/' '-_' | tr -d '='
}

show_google_play_error() {
    local response_file="$1"
    jq '.error | {code, message, status, details}' "$response_file" >&2 2>/dev/null || true
}

decide_google_play_upload() {
    local answer

    case "$GOOGLE_PLAY_MODE" in
        publish)
            return 0
            ;;
        skip)
            GOOGLE_PLAY_SUMMARY="pominięto (--skip-google-play)"
            return 0
            ;;
        ask)
            if [[ ! -t 0 ]]; then
                echo "Nie można zapytać o publikację Google Play bez interaktywnego terminala." >&2
                echo "Użyj --publish-google-play albo --skip-google-play." >&2
                return 1
            fi
            while true; do
                printf "\nBuild %s (%s) zakończony poprawnie. Czy wysłać aktualizację do Google Play? [t/N] " "$next_name" "$next_code"
                if ! IFS= read -r answer; then
                    echo "Nie udało się odczytać odpowiedzi." >&2
                    return 1
                fi
                case "${answer,,}" in
                    t|tak|y|yes)
                        GOOGLE_PLAY_MODE="publish"
                        break
                        ;;
                    ""|n|nie|no)
                        GOOGLE_PLAY_MODE="skip"
                        GOOGLE_PLAY_SUMMARY="pominięto przez użytkownika"
                        break
                        ;;
                    *)
                        echo "Nie rozpoznano odpowiedzi. Wpisz tak albo nie." >&2
                        ;;
                esac
            done
            ;;
        *)
            echo "Niepoprawny tryb publikacji Google Play: $GOOGLE_PLAY_MODE" >&2
            return 1
            ;;
    esac
}

prepare_google_play() {
    local credentials_path client_email now expires header payload unsigned_jwt signature assertion previous_umask
    local token_response token_status preflight_response preflight_status package_encoded track_encoded
    local candidate
    local -a credentials_candidates=()

    google_play_package="${GOOGLE_PLAY_PACKAGE_NAME:-pl.firmatpp.kierowca}"
    google_play_track="${GOOGLE_PLAY_TRACK:-alpha}"
    google_play_release_language="${GOOGLE_PLAY_RELEASE_LANGUAGE:-pl-PL}"
    credentials_path="${GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS:-${GOOGLE_APPLICATION_CREDENTIALS:-}}"

    if [[ -z "$credentials_path" ]]; then
        while IFS= read -r -d '' candidate; do
            if jq -e '
                .type == "service_account"
                and (.client_email | type == "string" and length > 0)
                and (.private_key | type == "string" and length > 0)
            ' "$candidate" >/dev/null 2>&1; then
                credentials_candidates+=("$candidate")
            fi
        done < <(find "$ROOT_DIR/scripts" -maxdepth 1 -type f -name '*.json' -print0)

        case "${#credentials_candidates[@]}" in
            0)
                echo "Nie znaleziono klucza konta serwisowego w $ROOT_DIR/scripts/*.json." >&2
                echo "Umieść dokładnie jeden poprawny plik JSON konta serwisowego w folderze scripts." >&2
                return 1
                ;;
            1)
                credentials_path="${credentials_candidates[0]}"
                ;;
            *)
                echo "Znaleziono więcej niż jeden klucz konta serwisowego w folderze scripts:" >&2
                for candidate in "${credentials_candidates[@]}"; do
                    printf '  - %s\n' "$(basename "$candidate")" >&2
                done
                echo "Pozostaw jeden plik albo ustaw GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS." >&2
                return 1
                ;;
        esac
    fi

    command -v openssl >/dev/null 2>&1 || {
        echo "Brak wymaganego polecenia: openssl" >&2
        return 1
    }

    [[ "$google_play_package" =~ ^[A-Za-z0-9._]+$ ]] || {
        echo "Niepoprawne GOOGLE_PLAY_PACKAGE_NAME: $google_play_package" >&2
        return 1
    }
    [[ -n "$google_play_track" ]] || { echo "GOOGLE_PLAY_TRACK nie może być puste." >&2; return 1; }
    [[ "$google_play_release_language" =~ ^[A-Za-z0-9-]+$ ]] || {
        echo "Niepoprawne GOOGLE_PLAY_RELEASE_LANGUAGE: $google_play_release_language" >&2
        return 1
    }
    [[ -n "$credentials_path" ]] || {
        echo "Nie wskazano ani nie znaleziono klucza konta serwisowego Google Play." >&2
        return 1
    }
    [[ -r "$credentials_path" ]] || {
        echo "Nie można odczytać klucza konta serwisowego: $credentials_path" >&2
        return 1
    }

    GOOGLE_PLAY_TEMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/tpp-google-play.XXXXXX")"
    chmod 700 "$GOOGLE_PLAY_TEMP_DIR"
    client_email="$(jq -er '.client_email | select(type == "string" and length > 0)' "$credentials_path")" || {
        echo "Klucz Google Play nie zawiera client_email." >&2
        return 1
    }
    previous_umask="$(umask)"
    umask 077
    if ! jq -er '.private_key | select(type == "string" and length > 0)' "$credentials_path" > "$GOOGLE_PLAY_TEMP_DIR/private-key.pem"; then
        umask "$previous_umask"
        echo "Klucz Google Play nie zawiera private_key." >&2
        return 1
    fi
    umask "$previous_umask"

    now="$(date +%s)"
    expires=$((now + 3600))
    header="$(printf '%s' '{"alg":"RS256","typ":"JWT"}' | base64url)"
    payload="$(jq -cn \
        --arg iss "$client_email" \
        --arg scope 'https://www.googleapis.com/auth/androidpublisher' \
        --arg aud 'https://oauth2.googleapis.com/token' \
        --argjson iat "$now" \
        --argjson exp "$expires" \
        '{iss:$iss,scope:$scope,aud:$aud,iat:$iat,exp:$exp}' | base64url)"
    unsigned_jwt="$header.$payload"
    signature="$(printf '%s' "$unsigned_jwt" \
        | openssl dgst -sha256 -sign "$GOOGLE_PLAY_TEMP_DIR/private-key.pem" \
        | base64url)"
    assertion="$unsigned_jwt.$signature"
    printf '%s' "$assertion" > "$GOOGLE_PLAY_TEMP_DIR/assertion.txt"
    chmod 600 "$GOOGLE_PLAY_TEMP_DIR/assertion.txt"

    token_response="$GOOGLE_PLAY_TEMP_DIR/token.json"
    token_status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 60 \
        -o "$token_response" \
        -w '%{http_code}' \
        -X POST \
        -H 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \
        --data-urlencode "assertion@$GOOGLE_PLAY_TEMP_DIR/assertion.txt" \
        'https://oauth2.googleapis.com/token')"
    [[ "$token_status" == "200" ]] || {
        echo "Nie udało się pobrać tokenu Google Play (HTTP $token_status)." >&2
        jq '{error, error_description}' "$token_response" >&2 2>/dev/null || true
        return 1
    }
    GOOGLE_PLAY_ACCESS_TOKEN="$(jq -er '.access_token | select(type == "string" and length > 0)' "$token_response")" || {
        echo "Google OAuth nie zwrócił access_token." >&2
        return 1
    }
    printf 'Authorization: Bearer %s\n' "$GOOGLE_PLAY_ACCESS_TOKEN" > "$GOOGLE_PLAY_TEMP_DIR/auth-header.txt"
    chmod 600 "$GOOGLE_PLAY_TEMP_DIR/auth-header.txt"
    rm -f "$GOOGLE_PLAY_TEMP_DIR/private-key.pem" "$GOOGLE_PLAY_TEMP_DIR/assertion.txt" "$token_response"

    package_encoded="$(urlencode "$google_play_package")"
    track_encoded="$(urlencode "$google_play_track")"
    preflight_response="$GOOGLE_PLAY_TEMP_DIR/preflight.json"
    preflight_status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 60 \
        -o "$preflight_response" \
        -w '%{http_code}' \
        -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
        "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/$package_encoded/tracks/$track_encoded/releases")"
    [[ "$preflight_status" == "200" ]] || {
        echo "Brak dostępu do ścieżki Google Play $google_play_track dla $google_play_package (HTTP $preflight_status)." >&2
        show_google_play_error "$preflight_response"
        return 1
    }

    GOOGLE_PLAY_CURRENT_VERSION_STATE="$(jq -r --argjson code "$next_code" '
        .releases[]?
        | select(any(.activeArtifacts[]?; .versionCode == $code))
        | .releaseLifecycleState // empty
    ' "$preflight_response" | sed -n '1p')"
    if [[ -n "$GOOGLE_PLAY_CURRENT_VERSION_STATE" && "$REUSE_CURRENT" == "0" ]]; then
        echo "Google Play ma już versionCode $next_code na ścieżce $google_play_track ($GOOGLE_PLAY_CURRENT_VERSION_STATE)." >&2
        echo "Zwiększ wersję lokalną albo świadomie użyj --reuse-current." >&2
        return 1
    fi
    if [[ "$GOOGLE_PLAY_CURRENT_VERSION_STATE" == "RELEASE_LIFECYCLE_STATE_NOT_APPROVED" ]]; then
        echo "Wersja $next_code została odrzucona w Google Play. Zwiększ versionCode i utwórz nowe wydanie." >&2
        return 1
    fi

    echo "Google Play: używam klucza $(basename "$credentials_path")."
    echo "Google Play: zweryfikowano dostęp do $google_play_package, ścieżka $google_play_track."
}

publish_to_google_play() {
    local release_notes="$1"
    local package_encoded track_encoded edit_encoded api_base
    local response status bundle_version_code existing_sha256 local_sha256
    local play_notes track_payload release_name

    case "$GOOGLE_PLAY_CURRENT_VERSION_STATE" in
        RELEASE_LIFECYCLE_STATE_IN_REVIEW|RELEASE_LIFECYCLE_STATE_APPROVED_NOT_PUBLISHED|RELEASE_LIFECYCLE_STATE_PUBLISHED)
            GOOGLE_PLAY_SUMMARY="już istnieje: $GOOGLE_PLAY_CURRENT_VERSION_STATE"
            echo "Google Play: wersja $next_name ($next_code) ma już stan $GOOGLE_PLAY_CURRENT_VERSION_STATE; pomijam ponowną publikację."
            return 0
            ;;
    esac

    package_encoded="$(urlencode "$google_play_package")"
    track_encoded="$(urlencode "$google_play_track")"
    api_base="https://androidpublisher.googleapis.com/androidpublisher/v3/applications/$package_encoded"

    response="$GOOGLE_PLAY_TEMP_DIR/edit-create.json"
    status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 60 \
        -o "$response" \
        -w '%{http_code}' \
        -X POST \
        -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
        -H 'Content-Type: application/json' \
        -d '{}' \
        "$api_base/edits")"
    [[ "$status" == "200" ]] || {
        echo "Nie udało się utworzyć edycji Google Play (HTTP $status)." >&2
        show_google_play_error "$response"
        return 1
    }
    GOOGLE_PLAY_EDIT_ID="$(jq -er '.id | select(type == "string" and length > 0)' "$response")" || {
        echo "Google Play nie zwrócił identyfikatora edycji." >&2
        return 1
    }
    edit_encoded="$(urlencode "$GOOGLE_PLAY_EDIT_ID")"

    response="$GOOGLE_PLAY_TEMP_DIR/track-get.json"
    status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 60 \
        -o "$response" \
        -w '%{http_code}' \
        -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
        "$api_base/edits/$edit_encoded/tracks/$track_encoded")"
    [[ "$status" == "200" ]] || {
        echo "Nie znaleziono ścieżki Google Play $google_play_track (HTTP $status)." >&2
        show_google_play_error "$response"
        return 1
    }

    response="$GOOGLE_PLAY_TEMP_DIR/bundles.json"
    status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 60 \
        -o "$response" \
        -w '%{http_code}' \
        -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
        "$api_base/edits/$edit_encoded/bundles")"
    [[ "$status" == "200" ]] || {
        echo "Nie udało się sprawdzić pakietów Google Play (HTTP $status)." >&2
        show_google_play_error "$response"
        return 1
    }

    existing_sha256="$(jq -r --argjson code "$next_code" '.bundles[]? | select(.versionCode == $code) | .sha256 // empty' "$response" | sed -n '1p')"
    local_sha256="$(sha256sum "$artifact_dir/google-play/$play_aab_name" | awk '{print $1}')"
    if [[ -n "$existing_sha256" ]]; then
        if [[ "${existing_sha256,,}" != "${local_sha256,,}" && "$REUSE_CURRENT" == "0" ]]; then
            echo "Google Play ma już versionCode $next_code z inną sumą SHA-256." >&2
            return 1
        fi
        if [[ "${existing_sha256,,}" != "${local_sha256,,}" ]]; then
            echo "Uwaga: lokalny AAB ma inną sumę niż istniejący versionCode $next_code; --reuse-current zachowuje pakiet już wysłany do Google Play." >&2
        fi
        bundle_version_code="$next_code"
        echo "Google Play ma już AAB $next_name ($next_code); pomijam ponowny upload."
    else
        response="$GOOGLE_PLAY_TEMP_DIR/bundle-upload.json"
        status="$(curl -sS \
            --connect-timeout 20 \
            --max-time 600 \
            -o "$response" \
            -w '%{http_code}' \
            -X POST \
            -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
            -H 'Content-Type: application/octet-stream' \
            --data-binary "@$artifact_dir/google-play/$play_aab_name" \
            "https://androidpublisher.googleapis.com/upload/androidpublisher/v3/applications/$package_encoded/edits/$edit_encoded/bundles?uploadType=media")"
        [[ "$status" == "200" ]] || {
            echo "Nie udało się wysłać AAB do Google Play (HTTP $status)." >&2
            show_google_play_error "$response"
            return 1
        }
        bundle_version_code="$(jq -er '.versionCode' "$response")" || {
            echo "Google Play nie zwrócił versionCode przesłanego AAB." >&2
            return 1
        }
        [[ "$bundle_version_code" == "$next_code" ]] || {
            echo "Google Play odczytał versionCode $bundle_version_code zamiast $next_code." >&2
            return 1
        }
    fi

    play_notes="$(jq -rn --arg notes "$release_notes" '$notes | if length > 500 then .[0:497] + "..." else . end')"
    release_name="TPP Kierowca $next_name ($next_code)"
    track_payload="$(jq -n \
        --arg track "$google_play_track" \
        --arg release_name "$release_name" \
        --arg version_code "$bundle_version_code" \
        --arg language "$google_play_release_language" \
        --arg notes "$play_notes" \
        '{track:$track,releases:[{name:$release_name,versionCodes:[$version_code],status:"completed",releaseNotes:[{language:$language,text:$notes}]}]}')"

    response="$GOOGLE_PLAY_TEMP_DIR/track-update.json"
    status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 60 \
        -o "$response" \
        -w '%{http_code}' \
        -X PUT \
        -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
        -H 'Content-Type: application/json' \
        -d "$track_payload" \
        "$api_base/edits/$edit_encoded/tracks/$track_encoded")"
    [[ "$status" == "200" ]] || {
        echo "Nie udało się przypisać AAB do ścieżki $google_play_track (HTTP $status)." >&2
        show_google_play_error "$response"
        return 1
    }

    response="$GOOGLE_PLAY_TEMP_DIR/edit-validate.json"
    status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 120 \
        -o "$response" \
        -w '%{http_code}' \
        -X POST \
        -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
        "$api_base/edits/$edit_encoded:validate")"
    [[ "$status" == "200" ]] || {
        echo "Walidacja edycji Google Play nie powiodła się (HTTP $status)." >&2
        show_google_play_error "$response"
        return 1
    }

    response="$GOOGLE_PLAY_TEMP_DIR/edit-commit.json"
    status="$(curl -sS \
        --connect-timeout 20 \
        --max-time 120 \
        -o "$response" \
        -w '%{http_code}' \
        -X POST \
        -H "@$GOOGLE_PLAY_TEMP_DIR/auth-header.txt" \
        "$api_base/edits/$edit_encoded:commit?changesNotSentForReview=false&changesInReviewBehavior=ERROR_IF_IN_REVIEW")"
    [[ "$status" == "200" ]] || {
        echo "Nie udało się zatwierdzić i wysłać zmian Google Play do weryfikacji (HTTP $status)." >&2
        show_google_play_error "$response"
        return 1
    }

    GOOGLE_PLAY_EDIT_ID=""
    GOOGLE_PLAY_SUMMARY="wysłano do weryfikacji"
    echo "Google Play: $release_name wysłano na ścieżkę $google_play_track i przekazano do weryfikacji."
}

if ((REUSE_CURRENT == 0)); then
    if git show-ref --verify --quiet "refs/tags/$tag_name" || git ls-remote --exit-code --tags "$REMOTE_NAME" "refs/tags/$tag_name" >/dev/null 2>&1; then
        echo "Tag $tag_name już istnieje. Użyj --reuse-current albo zwiększ wersję ręcznie." >&2
        exit 1
    fi

    VERSION_BACKUP="$(mktemp)"
    cp "$VERSION_FILE" "$VERSION_BACKUP"
    perl -0pi -e "s/versionCode = \\d+/versionCode = $next_code/" "$VERSION_FILE"
    perl -0pi -e "s/versionName = \"[^\"]+\"/versionName = \"$next_name\"/" "$VERSION_FILE"
    VERSION_FILE_CHANGED=1

    read -r written_code written_name < <(read_version)
    [[ "$written_code" == "$next_code" && "$written_name" == "$next_name" ]] || {
        echo "Nie udało się jednoznacznie zapisać nowej wersji." >&2
        exit 1
    }
fi

echo "Buduję TPP Kierowca $next_name ($next_code)..."
gradle_tasks=(assembleRelease bundleRelease)
if ((SKIP_TESTS == 0)); then
    gradle_tasks=(testDebugUnitTest "${gradle_tasks[@]}")
fi
./gradlew "${gradle_tasks[@]}"

apk_source="$(find app/build/outputs/apk/release -maxdepth 1 -type f -name '*.apk' -print -quit)"
aab_source="$(find app/build/outputs/bundle/release -maxdepth 1 -type f -name '*.aab' -print -quit)"
[[ -n "$apk_source" && -f "$apk_source" ]] || { echo "Nie znaleziono release APK." >&2; exit 1; }
[[ -n "$aab_source" && -f "$aab_source" ]] || { echo "Nie znaleziono release AAB." >&2; exit 1; }

sdk_dir="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
if [[ -z "$sdk_dir" && -f local.properties ]]; then
    sdk_dir="$(sed -n 's/^sdk.dir=//p' local.properties | sed -n '1p')"
fi
apksigner="$(find "$sdk_dir/build-tools" -type f -name apksigner 2>/dev/null | sort -V | tail -n 1)"
aapt="$(find "$sdk_dir/build-tools" -type f -name aapt 2>/dev/null | sort -V | tail -n 1)"
[[ -x "$apksigner" && -x "$aapt" ]] || { echo "Nie znaleziono apksigner/aapt w Android SDK." >&2; exit 1; }
"$apksigner" verify --verbose "$apk_source"
apk_badging="$("$aapt" dump badging "$apk_source" | sed -n '1p')"
[[ "$apk_badging" == *"versionCode='$next_code'"* && "$apk_badging" == *"versionName='$next_name'"* ]] || {
    echo "APK ma inny numer wersji niż oczekiwany $next_name ($next_code)." >&2
    exit 1
}
jarsigner -verify "$aab_source" >/dev/null

rm -rf "$artifact_dir"
mkdir -p "$artifact_dir/gitea" "$artifact_dir/google-play"
cp "$apk_source" "$artifact_dir/gitea/$gitea_apk_name"
cp "$aab_source" "$artifact_dir/google-play/$play_aab_name"
(
    cd "$artifact_dir"
    sha256sum "gitea/$gitea_apk_name" "google-play/$play_aab_name" > SHA256SUMS.txt
)

decide_google_play_upload
if [[ "$GOOGLE_PLAY_MODE" == "publish" ]]; then
    prepare_google_play
fi

if ((REUSE_CURRENT == 0)); then
    git add "$VERSION_FILE"
    git diff --cached --check
    git commit -m "Wydaj aplikację kierowcy $next_name ($next_code)"
    VERSION_COMMITTED=1
fi

if ! git show-ref --verify --quiet "refs/tags/$tag_name"; then
    git tag -a "$tag_name" -m "TPP Kierowca $next_name ($next_code)"
fi
tag_commit="$(git rev-list -n 1 "$tag_name")"
head_commit="$(git rev-parse HEAD)"
[[ "$tag_commit" == "$head_commit" ]] || {
    echo "Tag $tag_name nie wskazuje aktualnego commita ($head_commit)." >&2
    exit 1
}

if [[ -n "$NOTES_FILE" ]]; then
    release_notes="$(<"$NOTES_FILE")"
else
    previous_tag="$(git tag --sort=-version:refname | grep -Fxv "$tag_name" | sed -n '1p' || true)"
    if [[ -n "$previous_tag" ]]; then
        changes="$(git log --format='- %s (%h)' "$previous_tag..HEAD")"
    else
        changes="$(git log -20 --format='- %s (%h)')"
    fi
release_notes="TPP Kierowca $next_name ($next_code)

Zmiany:
${changes:-'- Wydanie techniczne.'}

Artefakt Google Play Console (AAB) znajduje się lokalnie w:
release-artifacts/$tag_name/google-play/$play_aab_name"
fi

if [[ "$GOOGLE_PLAY_MODE" == "publish" ]]; then
    publish_to_google_play "$release_notes"
    GOOGLE_PLAY_ACCESS_TOKEN=""
    rm -f "$GOOGLE_PLAY_TEMP_DIR/auth-header.txt"
fi

git push "$REMOTE_NAME" "$branch"
git push "$REMOTE_NAME" "$tag_name"

release_response="$(mktemp)"
release_status="$(curl -sS "${auth_args[@]}" -o "$release_response" -w '%{http_code}' "$api_base/releases/tags/$tag_name")"
if [[ "$release_status" == "200" ]]; then
    release_id="$(jq -r '.id' "$release_response")"
elif [[ "$release_status" == "404" ]]; then
    release_payload="$(jq -n \
        --arg tag "$tag_name" \
        --arg target "$branch" \
        --arg name "TPP Kierowca $next_name ($next_code)" \
        --arg body "$release_notes" \
        '{tag_name:$tag,target_commitish:$target,name:$name,body:$body,draft:false,prerelease:false}')"
    release_status="$(curl -sS "${auth_args[@]}" \
        -H 'Content-Type: application/json' \
        -o "$release_response" \
        -w '%{http_code}' \
        -X POST \
        -d "$release_payload" \
        "$api_base/releases")"
    [[ "$release_status" == "201" ]] || {
        echo "Nie udało się utworzyć Gitea Release (HTTP $release_status)." >&2
        jq '{message,errors}' "$release_response" >&2 || true
        exit 1
    }
    release_id="$(jq -r '.id' "$release_response")"
else
    echo "Nie udało się sprawdzić Gitea Release (HTTP $release_status)." >&2
    exit 1
fi

upload_asset() {
    local file_path="$1"
    local asset_name="$2"
    local mime_type="$3"
    local assets_response existing_asset_id upload_response upload_status
    assets_response="$(mktemp)"
    curl -fsS "${auth_args[@]}" -o "$assets_response" "$api_base/releases/$release_id/assets"
    existing_asset_id="$(jq -r --arg name "$asset_name" '.[] | select(.name == $name) | .id' "$assets_response" | sed -n '1p')"
    if [[ -n "$existing_asset_id" ]]; then
        curl -fsS "${auth_args[@]}" -X DELETE "$api_base/releases/$release_id/assets/$existing_asset_id" >/dev/null
    fi
    upload_response="$(mktemp)"
    upload_status="$(curl -sS "${auth_args[@]}" \
        -o "$upload_response" \
        -w '%{http_code}' \
        -X POST \
        -F "attachment=@$file_path;type=$mime_type" \
        "$api_base/releases/$release_id/assets?name=$asset_name")"
    [[ "$upload_status" == "201" ]] || {
        echo "Nie udało się wysłać $asset_name (HTTP $upload_status)." >&2
        jq '{message,errors}' "$upload_response" >&2 || true
        return 1
    }
}

upload_asset "$artifact_dir/gitea/$gitea_apk_name" "$gitea_apk_name" "application/vnd.android.package-archive"
upload_asset "$artifact_dir/SHA256SUMS.txt" "SHA256SUMS.txt" "text/plain"

assets_response="$(mktemp)"
curl -fsS "${auth_args[@]}" -o "$assets_response" "$api_base/releases/$release_id/assets"
published_apk_url="$(jq -r --arg name "$gitea_apk_name" '.[] | select(.name == $name) | .browser_download_url' "$assets_response" | sed -n '1p')"
[[ -n "$published_apk_url" && "$published_apk_url" != "null" ]] || {
    echo "Nie znaleziono opublikowanego APK w Gitea Release." >&2
    exit 1
}
downloaded_apk="$(mktemp)"
curl -fsSL "${auth_args[@]}" -o "$downloaded_apk" "$published_apk_url"
local_apk_hash="$(sha256sum "$artifact_dir/gitea/$gitea_apk_name" | awk '{print $1}')"
published_apk_hash="$(sha256sum "$downloaded_apk" | awk '{print $1}')"
[[ "$local_apk_hash" == "$published_apk_hash" ]] || {
    echo "Checksum opublikowanego APK nie zgadza się z lokalnym plikiem." >&2
    exit 1
}

release_url="$gitea_base_url/$gitea_repository/releases/tag/$tag_name"
RELEASE_SUCCEEDED=1
[[ -n "$VERSION_BACKUP" ]] && rm -f "$VERSION_BACKUP"
if [[ -n "$GOOGLE_PLAY_TEMP_DIR" && -d "$GOOGLE_PLAY_TEMP_DIR" ]]; then
    rm -rf -- "$GOOGLE_PLAY_TEMP_DIR"
    GOOGLE_PLAY_TEMP_DIR=""
fi
trap - EXIT INT TERM

cat <<EOF

Release zakończony:
  Wersja:       $next_name ($next_code)
  Tag:          $tag_name
  Gitea:        $release_url
  APK:          $artifact_dir/gitea/$gitea_apk_name
  AAB:          $artifact_dir/google-play/$play_aab_name
  Google Play:  $GOOGLE_PLAY_SUMMARY
  Play track:   ${google_play_track:-pominięto}
  Checksumy:    $artifact_dir/SHA256SUMS.txt
EOF
