#!/bin/sh
# Write a JSON handoff manifest for a completed Clawdie ISO image artifact.
# Intended for the FreeBSD publish/deploy path; safe to run from repo root.

set -eu
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PATH

usage() {
    cat <<'EOF'
Usage: scripts/write-artifact-manifest.sh <image.img|image.img.xz> [--base-url URL]

Writes <image>.manifest.json next to the image artifacts. The manifest is the
handoff contract for Hermes USB/IMG Deployer and other non-git artifact users.

Environment:
  BUILD_COMMAND  Optional build command string to record in the manifest.
  BUILT_BY       Optional builder identity; defaults to "Codex ISO Builder".
EOF
}

if [ "$#" -lt 1 ]; then
    usage >&2
    exit 2
fi

_artifact="$1"
shift
_base_url=""

while [ "$#" -gt 0 ]; do
    case "$1" in
        --base-url)
            shift
            if [ "$#" -eq 0 ]; then
                echo "ERROR: --base-url requires a value" >&2
                exit 2
            fi
            _base_url="${1%/}"
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            echo "ERROR: unknown argument: $1" >&2
            usage >&2
            exit 2
            ;;
    esac
    shift
done

case "${_artifact}" in
    *.img.xz)
        _xz="${_artifact}"
        _raw="${_artifact%.xz}"
        _stem="${_artifact%.img.xz}"
        ;;
    *.img)
        _raw="${_artifact}"
        _xz="${_artifact}.xz"
        _stem="${_artifact%.img}"
        ;;
    *)
        echo "ERROR: artifact must end in .img or .img.xz: ${_artifact}" >&2
        exit 2
        ;;
esac

_sha="${_xz}.sha256"
_manifest="${_stem}.manifest.json"

if [ ! -f "${_xz}" ]; then
    echo "ERROR: compressed image missing: ${_xz}" >&2
    exit 1
fi

if [ ! -f "${_sha}" ]; then
    echo "ERROR: checksum file missing: ${_sha}" >&2
    exit 1
fi

json_escape() {
    printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
}

json_string_or_null() {
    if [ -n "$1" ]; then
        printf '"%s"' "$(json_escape "$1")"
    else
        printf 'null'
    fi
}

file_size() {
    if stat -f '%z' "$1" >/dev/null 2>&1; then
        stat -f '%z' "$1"
    else
        stat -c '%s' "$1"
    fi
}

file_sha256() {
    if command -v sha256 >/dev/null 2>&1; then
        sha256 -q "$1"
    else
        sha256sum "$1" | awk '{print $1}'
    fi
}

sha_from_file() {
    if grep -q '=' "$1" 2>/dev/null; then
        awk -F'= ' 'NF > 1 {print $2; exit}' "$1"
    else
        awk '{print $1; exit}' "$1"
    fi
}

_xz_hash="$(file_sha256 "${_xz}")"
_sha_hash="$(sha_from_file "${_sha}")"
if [ "${_xz_hash}" != "${_sha_hash}" ]; then
    echo "ERROR: checksum file does not match compressed image" >&2
    echo "       computed: ${_xz_hash}" >&2
    echo "       file:     ${_sha_hash}" >&2
    exit 1
fi

_raw_base="$(basename "${_raw}")"
_xz_base="$(basename "${_xz}")"
_sha_base="$(basename "${_sha}")"
_manifest_base="$(basename "${_manifest}")"

if [ -f "${_raw}" ]; then
    _raw_size="$(file_size "${_raw}")"
else
    _raw_size="null"
fi
_xz_size="$(file_size "${_xz}")"

_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)"
_commit="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
_repo_modified="null"
if git diff --quiet 2>/dev/null && git diff --cached --quiet 2>/dev/null; then
    _repo_modified="false"
elif git rev-parse --git-dir >/dev/null 2>&1; then
    _repo_modified="true"
fi
_host="$(hostname 2>/dev/null || echo unknown)"
_written_at="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
_run_stamp="$(date -u '+%Y%m%dT%H%M%SZ')"
_run_id="${_run_stamp}-${_stem##*/}"
_freebsd="$(freebsd-version -kru 2>/dev/null | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
_builder="${BUILT_BY:-Codex ISO Builder}"
_build_command="${BUILD_COMMAND:-unknown}"

if [ -n "${_base_url}" ]; then
    _image_url="${_base_url}/${_xz_base}"
    _sha_url="${_base_url}/${_sha_base}"
    _manifest_url="${_base_url}/${_manifest_base}"
else
    _image_url=""
    _sha_url=""
    _manifest_url=""
fi

_tmp="${_manifest}.tmp.$$"
cat > "${_tmp}" <<EOF
{
  "schema": "clawdie.iso.publish.v1",
  "legacy_schema": "clawdie.iso-artifact.v1",
  "project": "clawdie-iso",
  "workflow": "iso-publish",
  "run_id": "$(json_escape "${_run_id}")",
  "status": "pass",
  "actor": "$(json_escape "${_builder}")",
  "host": "$(json_escape "${_host}")",
  "branch": "$(json_escape "${_branch}")",
  "commit": "$(json_escape "${_commit}")",
  "repo_modified": ${_repo_modified},
  "started_at": null,
  "completed_at": "$(json_escape "${_written_at}")",
  "artifact_type": "operator-usb-image",
  "build_host": "$(json_escape "${_host}")",
  "built_by": "$(json_escape "${_builder}")",
  "image": "$(json_escape "${_raw_base}")",
  "compressed_image": "$(json_escape "${_xz_base}")",
  "sha256_file": "$(json_escape "${_sha_base}")",
  "manifest": "$(json_escape "${_manifest_base}")",
  "sha256": "$(json_escape "${_xz_hash}")",
  "raw_size_bytes": ${_raw_size},
  "compressed_size_bytes": ${_xz_size},
  "manifest_written_at": "$(json_escape "${_written_at}")",
  "freebsd_version": "$(json_escape "${_freebsd}")",
  "build_command": "$(json_escape "${_build_command}")",
  "image_url": $(json_string_or_null "${_image_url}"),
  "sha256_url": $(json_string_or_null "${_sha_url}"),
  "manifest_url": $(json_string_or_null "${_manifest_url}"),
  "inputs": {
    "compressed_image_path": "$(json_escape "${_xz}")",
    "sha256_path": "$(json_escape "${_sha}")"
  },
  "checks": [
    {
      "name": "checksum_file_matches_compressed_image",
      "status": "pass",
      "sha256": "$(json_escape "${_xz_hash}")"
    }
  ],
  "outputs": {
    "manifest_path": "$(json_escape "${_manifest}")",
    "image_url": $(json_string_or_null "${_image_url}"),
    "sha256_url": $(json_string_or_null "${_sha_url}"),
    "manifest_url": $(json_string_or_null "${_manifest_url}")
  },
  "logs": [],
  "summary": "Published artifact manifest is ready for Hermes checksum/xz verification and flashing.",
  "notes": "Built on FreeBSD; ready for Hermes USB/IMG Deployer after checksum and xz verification."
}
EOF

mv "${_tmp}" "${_manifest}"
printf '%s\n' "${_manifest}"
