#!/usr/bin/env bash
set -euo pipefail

# Assemble the MoqFFI Swift Package (raw UniFFI bindings): bundle per-target
# static libs into an XCFramework, copy the uniffi-generated Swift source,
# rewrite the Package.swift binary URL+checksum from ffi/Package.swift.template,
# and tar the result. This package is published to moq-dev/moq-swift-ffi,
# lockstep with the moq-ffi crate. The ergonomic wrapper (package.sh) depends
# on it.
#
# Designed to run after rs/moq-ffi/build.sh produces per-target outputs.
# Only macOS hosts can run this (xcodebuild is required).
#
# Usage:
#   swift/scripts/package-ffi.sh --version 0.0.0-dev --lib-dir dist --output dist
#
#   --version       Version baked into Package.swift (the moq-ffi crate version).
#   --lib-dir       Directory containing per-target moq-ffi outputs.
#   --output        Destination directory for the .tar.gz + xcframework.zip.
#   --bindings-dir  Directory with uniffi-bindgen swift output (defaults to
#                   "$LIB_DIR/bindings").
#   --release-url   Release URL prefix used as the XCFramework download
#                   target. Defaults to the upstream GitHub Releases URL;
#                   override when publishing from a fork.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SWIFT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
WORKSPACE_DIR="$(cd "$SWIFT_DIR/.." && pwd)"

VERSION=""
LIB_DIR=""
OUTPUT_DIR=""
BINDINGS_DIR=""
RELEASE_URL_BASE="https://github.com/moq-dev/moq/releases/download"

while [[ $# -gt 0 ]]; do
    case $1 in
        --version)
            VERSION="$2"
            shift 2
            ;;
        --lib-dir)
            LIB_DIR="$2"
            shift 2
            ;;
        --output)
            OUTPUT_DIR="$2"
            shift 2
            ;;
        --bindings-dir)
            BINDINGS_DIR="$2"
            shift 2
            ;;
        --release-url)
            RELEASE_URL_BASE="$2"
            shift 2
            ;;
        -h | --help)
            grep '^#' "$0" | sed 's/^# \{0,1\}//'
            exit 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
    esac
done

[[ -z "$VERSION" ]] && {
    echo "Error: --version is required" >&2
    exit 1
}
[[ -z "$LIB_DIR" ]] && {
    echo "Error: --lib-dir is required" >&2
    exit 1
}
[[ -z "$OUTPUT_DIR" ]] && OUTPUT_DIR="dist"
[[ -z "$BINDINGS_DIR" ]] && BINDINGS_DIR="$LIB_DIR/bindings"

[[ "$(uname)" == "Darwin" ]] || {
    echo "Error: package-ffi.sh requires macOS (xcodebuild)" >&2
    exit 1
}
command -v xcodebuild >/dev/null || {
    echo "Error: xcodebuild not found" >&2
    exit 1
}
command -v swift >/dev/null || {
    echo "Error: swift not found" >&2
    exit 1
}

# `nix develop` on Darwin exports SDKROOT/DEVELOPER_DIR pointing at the
# nixpkgs-bundled apple-sdk, which the Xcode swift/xcodebuild in PATH refuse to
# load. Drop those so they fall back to `xcrun --show-sdk-path`. No-op in CI
# (macOS runners don't set a nix SDKROOT). Mirrors check.sh.
xcode_sdk_env=()
if [[ "${SDKROOT:-}" == /nix/store/* ]]; then
    xcode_sdk_env+=(-u SDKROOT)
fi
if [[ "${DEVELOPER_DIR:-}" == /nix/store/* ]]; then
    xcode_sdk_env+=(-u DEVELOPER_DIR)
fi

mkdir -p "$OUTPUT_DIR"
# Normalize to an absolute path: later steps (zip, swift package
# compute-checksum) run from cd'd subshells, so a relative OUTPUT_DIR
# would resolve against the wrong cwd.
OUTPUT_DIR="$(cd "$OUTPUT_DIR" && pwd)"

STAGING=$(mktemp -d)
trap 'rm -rf "$STAGING"' EXIT

# --- Headers (modulemap + .h) shared by all slices ---
HEADERS_DIR="$STAGING/headers"
mkdir -p "$HEADERS_DIR"
[[ -f "$BINDINGS_DIR/moqFFI.h" ]] || {
    echo "Error: missing $BINDINGS_DIR/moqFFI.h" >&2
    exit 1
}
[[ -f "$BINDINGS_DIR/moqFFI.modulemap" ]] || {
    echo "Error: missing $BINDINGS_DIR/moqFFI.modulemap" >&2
    exit 1
}
[[ -f "$BINDINGS_DIR/moq.swift" ]] || {
    echo "Error: missing $BINDINGS_DIR/moq.swift" >&2
    exit 1
}
cp "$BINDINGS_DIR/moqFFI.h" "$HEADERS_DIR/"
cp "$BINDINGS_DIR/moqFFI.modulemap" "$HEADERS_DIR/module.modulemap"

# --- Per-slice library prep ---
lib_for() {
    echo "$LIB_DIR/$1/libmoq_ffi.a"
}

ensure_lib() {
    local path
    path=$(lib_for "$1")
    [[ -f "$path" ]] || {
        echo "Error: missing static lib for $1 at $path" >&2
        exit 1
    }
    echo "$path"
}

IOS_DEVICE_LIB=$(ensure_lib "aarch64-apple-ios")
IOS_SIM_ARM64=$(ensure_lib "aarch64-apple-ios-sim")
MAC_ARM64=$(ensure_lib "aarch64-apple-darwin")

# --- Build XCFramework ---
XCF="$STAGING/MoqFFI.xcframework"
env ${xcode_sdk_env[@]+"${xcode_sdk_env[@]}"} xcodebuild -create-xcframework \
    -library "$IOS_DEVICE_LIB" -headers "$HEADERS_DIR" \
    -library "$IOS_SIM_ARM64" -headers "$HEADERS_DIR" \
    -library "$MAC_ARM64" -headers "$HEADERS_DIR" \
    -output "$XCF"

# --- Zip and checksum the XCFramework ---
XCF_ZIP="$OUTPUT_DIR/MoqFFI.xcframework.zip"
rm -f "$XCF_ZIP"
(cd "$STAGING" && zip -r -q "$XCF_ZIP" "$(basename "$XCF")")

# Move/copy to absolute path before computing checksum (swift requires
# it to live in a package).
CHECKSUM=$(cd "$SWIFT_DIR" && env ${xcode_sdk_env[@]+"${xcode_sdk_env[@]}"} swift package compute-checksum "$XCF_ZIP")
echo "XCFramework checksum: $CHECKSUM"

# --- Assemble Swift package staging dir ---
PKG_NAME="moq-ffi-${VERSION}-swift-ffi"
PKG_STAGE="$STAGING/$PKG_NAME"
mkdir -p "$PKG_STAGE/Sources/MoqFFI"

cp "$BINDINGS_DIR/moq.swift" "$PKG_STAGE/Sources/MoqFFI/Generated.swift"

# Dual-license files lifted from the workspace root so the mirror isn't
# licenseless. Both files are required by the MIT OR Apache-2.0 grant.
for license in LICENSE-MIT LICENSE-APACHE; do
    [[ -f "$WORKSPACE_DIR/$license" ]] || {
        echo "Error: missing $WORKSPACE_DIR/$license" >&2
        exit 1
    }
    cp "$WORKSPACE_DIR/$license" "$PKG_STAGE/$license"
done

# Minimal consumer-facing README. The full developer README lives in the
# monorepo; this one just orients a visitor to moq-dev/moq-swift-ffi.
cat >"$PKG_STAGE/README.md" <<EOF
# MoqFFI (Swift Package)

Raw UniFFI bindings for [Media over QUIC](https://github.com/moq-dev/moq). Most
callers want the ergonomic \`Moq\` wrapper at [moq-dev/moq-swift](https://github.com/moq-dev/moq-swift)
instead, which depends on this package.

Auto-generated mirror; source, issues, and pull requests live in
[moq-dev/moq](https://github.com/moq-dev/moq). This repo only carries tagged
Swift Package Manager releases, lockstep with the \`moq-ffi\` crate.

## Install

\`\`\`swift
.package(url: "https://github.com/moq-dev/moq-swift-ffi", from: "${VERSION}"),
\`\`\`

The package depends on a prebuilt \`MoqFFI.xcframework\` attached to the matching
[moq-ffi-v${VERSION}](https://github.com/moq-dev/moq/releases/tag/moq-ffi-v${VERSION})
release on the source repo.

Licensed under MIT OR Apache-2.0.
EOF

# Generate Package.swift with the final URL+checksum from the release
# template. The working swift/Package.swift is intentionally the monolithic
# local-dev (path-based) form and is not used here.
TEMPLATE="$SWIFT_DIR/ffi/Package.swift.template"
[[ -f "$TEMPLATE" ]] || {
    echo "Error: missing $TEMPLATE" >&2
    exit 1
}
URL="${RELEASE_URL_BASE}/moq-ffi-v${VERSION}/MoqFFI.xcframework.zip"
# Token-based substitution: the template carries REPLACE_URL / REPLACE_CHECKSUM
# placeholders, so editing the upstream URL in the template (or passing
# --release-url) never goes silently unsubstituted.
sed -e "s|REPLACE_URL|${URL}|g" \
    -e "s|REPLACE_CHECKSUM|${CHECKSUM}|g" \
    "$TEMPLATE" >"$PKG_STAGE/Package.swift"

# Fail loudly if any placeholder survived (e.g. someone renamed a token
# in the template without updating this script). Catching it here keeps
# a broken manifest from reaching the mirror.
if grep -q 'REPLACE_URL\|REPLACE_CHECKSUM' "$PKG_STAGE/Package.swift"; then
    echo "Error: unresolved REPLACE_* tokens in generated Package.swift" >&2
    grep -n 'REPLACE_URL\|REPLACE_CHECKSUM' "$PKG_STAGE/Package.swift" >&2
    exit 1
fi

# Cheap manifest sanity check: parse the generated Package.swift via the
# Swift toolchain. This runs even on PR dry-runs (where the live release
# asset doesn't exist yet) and catches syntax / API breakage in the
# template before it can reach the mirror.
(cd "$PKG_STAGE" && env ${xcode_sdk_env[@]+"${xcode_sdk_env[@]}"} swift package dump-package >/dev/null)

# --- Archive ---
ARCHIVE="$OUTPUT_DIR/${PKG_NAME}.tar.gz"
tar -czf "$ARCHIVE" -C "$STAGING" "$PKG_NAME"
echo "Created: $ARCHIVE"
echo "Created: $XCF_ZIP"
