#!/bin/sh
set -eu

HOST=""
DESTINATION=""
BASE_URL="${BIOSIMULANT_SKILLS_BASE_URL:-https://www.biosimulant.com}"

usage() {
  printf '%s\n' "Usage: install-agent-skills.sh --host codex|claude [--destination PATH] [--base-url URL]"
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --host)
      [ "$#" -ge 2 ] || { usage >&2; exit 2; }
      HOST="$2"
      shift 2
      ;;
    --destination)
      [ "$#" -ge 2 ] || { usage >&2; exit 2; }
      DESTINATION="$2"
      shift 2
      ;;
    --base-url)
      [ "$#" -ge 2 ] || { usage >&2; exit 2; }
      BASE_URL="$2"
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      printf '%s\n' "Unknown argument: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

case "$HOST" in
  codex)
    [ -n "$DESTINATION" ] || DESTINATION="${HOME:?HOME is required}/.agents/skills"
    ;;
  claude)
    [ -n "$DESTINATION" ] || DESTINATION="${HOME:?HOME is required}/.claude/skills"
    ;;
  *)
    usage >&2
    exit 2
    ;;
esac

case "$DESTINATION" in
  ""|/)
    printf '%s\n' "Refusing unsafe destination: $DESTINATION" >&2
    exit 2
    ;;
esac

command -v curl >/dev/null 2>&1 || { printf '%s\n' "curl is required" >&2; exit 1; }
command -v python3 >/dev/null 2>&1 || { printf '%s\n' "python3 is required" >&2; exit 1; }

BASE_URL=${BASE_URL%/}
TMP_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/biosimulant-agent-skills.XXXXXX")
trap 'rm -rf "$TMP_ROOT"' EXIT HUP INT TERM
INDEX_PATH="$TMP_ROOT/index.json"
MANIFEST_PATH="$TMP_ROOT/packages.tsv"
ARCHIVE_DIR="$TMP_ROOT/archives"
STAGE_DIR="$TMP_ROOT/stage"
mkdir -p "$ARCHIVE_DIR" "$STAGE_DIR"

curl -fsSL "$BASE_URL/download/skills/index.json" -o "$INDEX_PATH"

python3 - "$INDEX_PATH" "$MANIFEST_PATH" <<'PY'
import json
import re
import sys

index_path, manifest_path = sys.argv[1:]
with open(index_path, "r", encoding="utf-8") as handle:
    index = json.load(handle)

if index.get("schema_version") != "3":
    raise SystemExit("Unsupported BioSimulant skills index schema")

skills = index.get("skills")
if not isinstance(skills, list) or not skills:
    raise SystemExit("BioSimulant skills index has no packages")

rows = []
seen = set()
for skill in skills:
    name = skill.get("name")
    filename = skill.get("filename")
    checksum = skill.get("sha256")
    if skill.get("format") != "zip":
        raise SystemExit(f"Refusing non-package skill entry: {name!r}")
    if not isinstance(name, str) or not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,63}", name):
        raise SystemExit(f"Invalid package name: {name!r}")
    if filename != f"{name}.zip":
        raise SystemExit(f"Unexpected archive name for {name}")
    if not isinstance(checksum, str) or not re.fullmatch(r"[0-9a-f]{64}", checksum):
        raise SystemExit(f"Invalid SHA-256 for {name}")
    if name in seen:
        raise SystemExit(f"Duplicate package: {name}")
    seen.add(name)
    rows.append((name, filename, checksum))

with open(manifest_path, "w", encoding="utf-8", newline="\n") as handle:
    for row in rows:
        handle.write("\t".join(row) + "\n")
PY

while IFS="$(printf '\t')" read -r name filename checksum; do
  curl -fsSL "$BASE_URL/download/skills/$filename" -o "$ARCHIVE_DIR/$filename"
done < "$MANIFEST_PATH"

python3 - "$MANIFEST_PATH" "$ARCHIVE_DIR" "$STAGE_DIR" <<'PY'
import hashlib
import os
import stat
import sys
import zipfile

manifest_path, archive_dir, stage_dir = sys.argv[1:]
with open(manifest_path, "r", encoding="utf-8") as handle:
    rows = [line.rstrip("\n").split("\t") for line in handle if line.strip()]

for name, filename, expected in rows:
    archive_path = os.path.join(archive_dir, filename)
    with open(archive_path, "rb") as handle:
        actual = hashlib.sha256(handle.read()).hexdigest()
    if actual != expected:
        raise SystemExit(f"Checksum mismatch for {filename}: {actual} != {expected}")

    with zipfile.ZipFile(archive_path) as archive:
        entries = archive.infolist()
        if not entries or len(entries) > 200:
            raise SystemExit(f"Unsafe entry count in {filename}")
        total_size = sum(entry.file_size for entry in entries)
        if total_size > 10 * 1024 * 1024:
            raise SystemExit(f"Uncompressed package is too large: {filename}")
        for entry in entries:
            normalized = entry.filename.replace("\\", "/")
            parts = normalized.split("/")
            if normalized.startswith("/") or not parts or parts[0] != name:
                raise SystemExit(f"Archive entry escapes package root: {entry.filename}")
            if any(part in ("", ".", "..") for part in parts):
                raise SystemExit(f"Unsafe archive path: {entry.filename}")
            mode = entry.external_attr >> 16
            if stat.S_IFMT(mode) == stat.S_IFLNK:
                raise SystemExit(f"Symlink entries are not allowed: {entry.filename}")
        archive.extractall(stage_dir)

    if not os.path.isfile(os.path.join(stage_dir, name, "SKILL.md")):
        raise SystemExit(f"Package {name} does not contain {name}/SKILL.md")
PY

mkdir -p "$DESTINATION"
while IFS="$(printf '\t')" read -r name filename checksum; do
  rm -rf "$DESTINATION/$name"
  mv "$STAGE_DIR/$name" "$DESTINATION/$name"
done < "$MANIFEST_PATH"

PACKAGE_COUNT=$(wc -l < "$MANIFEST_PATH" | tr -d ' ')
printf '%s\n' "Installed $PACKAGE_COUNT verified BioSimulant Agent Skills for $HOST in $DESTINATION"
