#!/usr/bin/env bash
# build-keychain.sh: ephemeral Developer ID build keychain for headless signing.
#
# Why this exists: over SSH the Mac's login.keychain is locked
# ("User interaction is not allowed"), so `security find-identity` reports
# "0 valid identities found" and codesign silently produces an unsigned bundle.
# This creates a throwaway keychain, imports a password-protected .p12, and runs
# `set-key-partition-list` so codesign can use the key WITHOUT the GUI auth
# prompt that otherwise hangs a non-interactive session forever.
#
# Team-agnostic on purpose: it signs with whatever identity the .p12 contains.
# That means remote signing works BEFORE the Apple Developer org transfer to the
# LLC is finished; the team only matters at the notarization step, which is a
# separate concern (see release-macos.sh --sign-only).
#
# This is the first concrete piece of Bento (the app-release orchestrator).
# Bento's macOS recipe will `source` this; for now release-macos.sh does.
#
# --- Usage (sourced, the normal path) ---
#   source "$(dirname "$0")/build-keychain.sh"
#   bk_setup                      # create + import + unlock + put on search list
#   trap bk_teardown EXIT         # ALWAYS tear down, even on failure
#   id="$(bk_identity_name)"      # the "Developer ID Application: ... (TEAM)" string
#   ... codesign -s "$id" ...  /  ... APPLE_SIGNING_IDENTITY="$id" cargo tauri build ...
#   # teardown runs on exit
#
# --- Usage (standalone, for inspection/debugging) ---
#   # setup + teardown run as SEPARATE processes, so pin the keychain path so
#   # both agree (the default is unique per process, see BUILD_KEYCHAIN below):
#   export BUILD_KEYCHAIN="${TMPDIR:-/tmp}/bento-build.debug.keychain-db"
#   ./dist/build-keychain.sh setup       # leaves the keychain in place
#   ./dist/build-keychain.sh identity    # print the Developer ID identity it holds
#   ./dist/build-keychain.sh teardown    # remove it + restore the search list
#
# --- Required env (live in ~/.tauri/passwords.env on mbp; never in git) ---
#   BUILD_P12_PATH           path to the exported Developer ID Application .p12
#   BUILD_P12_PASSWORD       password protecting that .p12
#   BUILD_KEYCHAIN_PASSWORD  password for the temp keychain (ephemeral; any value)
#
# One-time prerequisite (cannot be done over SSH, needs a GUI Terminal on the
# Mac with login.keychain unlocked): export the cert + private key to a .p12:
#   security find-identity -v -p codesigning | grep "Developer ID Application"
#   # then, in Keychain Access: right-click the "Developer ID Application" identity
#   # -> Export -> .p12 -> set a password -> save to ~/Code/_private/developer-id.p12
#   # (or CLI: security export -t identities -f pkcs12 \
#   #          -P "<p12pw>" -o ~/Code/_private/developer-id.p12 )
#   # NOTE: omit -k. On modern macOS the Developer ID key lives in the
#   # data-protection keychain, not login.keychain-db; passing
#   # `-k login.keychain` finds nothing. Default search list works.
# Then add BUILD_P12_PATH / BUILD_P12_PASSWORD / BUILD_KEYCHAIN_PASSWORD to
# ~/.tauri/passwords.env.

set -euo pipefail

# A FULL PATH, not a bare name. Over SSH `security create-keychain` with a bare
# name tries ~/Library/Keychains/ and fails "SecKeychainCreate ... Permission
# denied"; a full path in $TMPDIR works headlessly (the CI-standard approach).
#
# UNIQUE PER RUN. A fixed path (`bento-build.keychain-db`) let two macOS builds
# on the same Mac collide: the second's `security delete-keychain` (bk_setup
# below) wiped the first's keychain mid-codesign, and both stomped one shared
# search-list state file, so teardown restored a polluted list. `mktemp -u`
# mints an unused name once, at source time, so it is stable across this
# process's bk_setup + bk_teardown yet distinct from any concurrent run.
# Override with BUILD_KEYCHAIN to pin a location (e.g. to pair a standalone
# `setup` and `teardown` that run as separate processes).
: "${BUILD_KEYCHAIN:=$(mktemp -u "${TMPDIR:-/tmp}/bento-build.XXXXXX").keychain-db}"

# The original keychain search list is stashed here across setup/teardown.
# Derived from BUILD_KEYCHAIN so it is unique-and-paired with it: a concurrent
# run saves/restores its own list rather than sharing one file.
_BK_STATE_FILE="${BUILD_KEYCHAIN%.keychain-db}.searchlist"

_bk_require_env() {
    local missing=0
    for v in BUILD_P12_PATH BUILD_P12_PASSWORD BUILD_KEYCHAIN_PASSWORD; do
        if [ -z "${!v:-}" ]; then
            echo "build-keychain: required env $v is unset (source ~/.tauri/passwords.env)" >&2
            missing=1
        fi
    done
    if [ -n "${BUILD_P12_PATH:-}" ] && [ ! -f "$BUILD_P12_PATH" ]; then
        echo "build-keychain: BUILD_P12_PATH not found: $BUILD_P12_PATH" >&2
        echo "  Run the one-time GUI .p12 export first (see header of this script)." >&2
        missing=1
    fi
    [ "$missing" -eq 0 ] || return 1
}

bk_setup() {
    _bk_require_env || return 1

    # If a stale keychain from a crashed run exists, remove it first.
    security delete-keychain "$BUILD_KEYCHAIN" 2>/dev/null || true

    echo "build-keychain: creating $BUILD_KEYCHAIN" >&2
    if ! security create-keychain -p "$BUILD_KEYCHAIN_PASSWORD" "$BUILD_KEYCHAIN"; then
        echo "build-keychain: create-keychain FAILED. Over SSH this needs a full" >&2
        echo "  keychain PATH (BUILD_KEYCHAIN=$BUILD_KEYCHAIN) and an active console" >&2
        echo "  login session on the Mac." >&2
        return 1
    fi
    # No idle auto-lock during a long build; lock-on-sleep stays on.
    security set-keychain-settings -lut 21600 "$BUILD_KEYCHAIN"
    security unlock-keychain -p "$BUILD_KEYCHAIN_PASSWORD" "$BUILD_KEYCHAIN"

    echo "build-keychain: importing $BUILD_P12_PATH" >&2
    security import "$BUILD_P12_PATH" -k "$BUILD_KEYCHAIN" -P "$BUILD_P12_PASSWORD" \
        -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productsign

    # Import the Apple intermediate(s). An isolated keychain holds only the leaf
    # cert; codesign rejects an identity whose chain it can't build ("no identity
    # found"). Match the leaf's issuer: this team's Developer ID Application cert is
    # issued by the ORIGINAL "Developer ID Certification Authority"
    # (OU=Apple Certification Authority) -> DeveloperIDCA.cer; G2 (OU=G2) is for
    # newer certs and will NOT chain it. Both are public; we fetch (cached) and
    # import both so either generation works. Apple Root CA is in the System trust
    # store, so System.keychain must stay on the search list (below).
    _bk_import_apple_intermediates

    # Pre-authorize the tools to use the imported key so macOS doesn't pop a GUI
    # prompt (apple-tool:/apple: is the Apple-standard partition set).
    security set-key-partition-list -S apple-tool:,apple: \
        -s -k "$BUILD_KEYCHAIN_PASSWORD" "$BUILD_KEYCHAIN" >/dev/null

    # Prepend our keychain to the user search list, KEEPING the existing ones
    # (login + System). Dropping System breaks root anchoring/lookups.
    local orig
    orig="$(security list-keychains -d user | sed -e 's/^[[:space:]]*//' -e 's/"//g')"
    printf '%s\n' "$orig" > "$_BK_STATE_FILE"
    # shellcheck disable=SC2086 # word-splitting the list is intentional
    security list-keychains -d user -s "$BUILD_KEYCHAIN" $orig

    echo "build-keychain: ready; identity: $(bk_identity_name || echo '??')" >&2
    # NOTE: keychain setup is now correct, but codesign STILL needs to run in the
    # console GUI (Aqua) security session to USE the private key. A pure SSH session
    # can enumerate the identity but not sign with it ("no identity found"). Drive
    # the build from a GUI Terminal, a user LaunchAgent, or `launchctl asuser <uid>`
    # (needs root). See bento/design.md "THE WALL" for the full analysis.
}

# Fetch (cached under the keychain dir) + import the public Apple Developer ID
# intermediates so codesign can build the cert chain.
_bk_import_apple_intermediates() {
    local dir cer url
    dir="$(dirname "$BUILD_KEYCHAIN")"
    for cer in DeveloperIDCA.cer DeveloperIDG2CA.cer; do
        url="https://www.apple.com/certificateauthority/$cer"
        if [ ! -f "$dir/$cer" ]; then
            curl -fsS "$url" -o "$dir/$cer" 2>/dev/null \
                || { echo "build-keychain: warn: could not fetch $cer" >&2; continue; }
        fi
        security import "$dir/$cer" -k "$BUILD_KEYCHAIN" >/dev/null 2>&1 || true
    done
}

bk_identity_name() {
    security find-identity -v -p codesigning "$BUILD_KEYCHAIN" \
        | sed -n 's/.*"\(Developer ID Application:[^"]*\)".*/\1/p' | head -1
}

bk_identity_hash() {
    security find-identity -v -p codesigning "$BUILD_KEYCHAIN" \
        | awk '/Developer ID Application/ {print $2; exit}'
}

bk_teardown() {
    # Restore the original search list if we saved one.
    if [ -f "$_BK_STATE_FILE" ]; then
        local orig
        orig="$(cat "$_BK_STATE_FILE")"
        if [ -n "$orig" ]; then
            # shellcheck disable=SC2086
            security list-keychains -d user -s $orig 2>/dev/null || true
        fi
        rm -f "$_BK_STATE_FILE"
    fi
    security delete-keychain "$BUILD_KEYCHAIN" 2>/dev/null || true
    echo "build-keychain: torn down $BUILD_KEYCHAIN" >&2
}

# When run directly (not sourced), act as a small CLI.
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
    case "${1:-}" in
        setup)    bk_setup ;;
        identity) bk_identity_name ;;
        hash)     bk_identity_hash ;;
        teardown) bk_teardown ;;
        *) echo "usage: $0 {setup|identity|hash|teardown}" >&2; exit 2 ;;
    esac
fi
