#!/usr/bin/env sh
set -eu

MODE=""
CID=""
CURRENT_VERSION=""
INFO_HEX=""
FIRMWARE_PATH=""
RESULT_PATH=""
BASE_URL="${BMCLI_FIRMWARE_BASE_URL:-https://download.busmust.com/files}"
CGI_URL="${BMCLI_FIRMWARE_CGI_URL:-https://download.busmust.com/cgi/DownloadFirmware.cgi}"

while [ "$#" -gt 0 ]; do
    case "$1" in
        --mode) MODE="$2"; shift 2 ;;
        --cid) CID="$2"; shift 2 ;;
        --current-version) CURRENT_VERSION="$2"; shift 2 ;;
        --info-hex) INFO_HEX="$2"; shift 2 ;;
        --firmware-path) FIRMWARE_PATH="$2"; shift 2 ;;
        --result-path) RESULT_PATH="$2"; shift 2 ;;
        --base-url) BASE_URL="$2"; shift 2 ;;
        --cgi-url) CGI_URL="$2"; shift 2 ;;
        *) echo "Unknown option: $1" >&2; exit 1 ;;
    esac
done

[ -n "$RESULT_PATH" ] || { echo "--result-path is required" >&2; exit 1; }
command -v curl >/dev/null 2>&1 || { echo "curl is required" >&2; exit 1; }
if command -v python3 >/dev/null 2>&1; then PYTHON=python3
elif command -v python >/dev/null 2>&1; then PYTHON=python
else echo "python3 is required for firmware release handling" >&2; exit 1
fi

TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT HUP INT TERM

case "$MODE" in
    check)
        printf '%s\n' "$CID" | grep -Eq '^[0-9A-Fa-f]{4}$' || {
            echo "Invalid firmware CID" >&2; exit 1;
        }
        CHANGELOG="$TMP/changelist.txt"
        CHANGELOG_URL="${BASE_URL%/}/Firmware_$(printf '%s' "$CID" | tr '[:lower:]' '[:upper:]')_Changelist.txt"
        curl -fsSL "$CHANGELOG_URL" -o "$CHANGELOG"
        "$PYTHON" - "$CHANGELOG" "$CURRENT_VERSION" "$RESULT_PATH" "$CHANGELOG_URL" "$CID" <<'PY'
import json, pathlib, re, sys
changelog_path, current, result_path, url, cid = sys.argv[1:]
pattern = re.compile(r"^(\d+)\.(\d+)\.(\d+)\.(\d+)$")
def version(value):
    match = pattern.fullmatch(value)
    if not match:
        raise SystemExit("invalid firmware version: " + value)
    parts = tuple(int(item) for item in match.groups())
    if any(item > 255 for item in parts):
        raise SystemExit("firmware version component exceeds 255")
    return parts
text = pathlib.Path(changelog_path).read_text(encoding="utf-8-sig")
lines = text.splitlines()
first = next((line.strip() for line in lines if line.strip()), "")
match = re.match(r"^(\d+\.\d+\.\d+\.\d+)(?:\s|$)", first)
if not match:
    raise SystemExit("firmware changelist first line does not contain a version")
latest = match.group(1)
current_tuple, latest_tuple = version(current), version(latest)
state = "update_available" if current_tuple < latest_tuple else (
    "up_to_date" if current_tuple == latest_tuple else "local_newer")
relevant = []
if current_tuple < latest_tuple:
    for line in lines:
        if re.match(r"^" + re.escape(current) + r"(?:\s|$)", line):
            break
        relevant.append(line)
result = {
    "status": "ok", "action": "device_update_check", "cid": cid.upper(),
    "current_version": current, "latest_version": latest,
    "update_available": current_tuple < latest_tuple, "state": state,
    "changelist_url": url, "release_notes": "\n".join(relevant).strip(),
}
pathlib.Path(result_path).write_text(json.dumps(result, ensure_ascii=False,
    separators=(",", ":")) + "\n", encoding="utf-8")
PY
        ;;
    download)
        printf '%s\n' "$INFO_HEX" | grep -Eq '^[0-9A-Fa-f]{216}$' || {
            echo "Invalid firmware info payload" >&2; exit 1;
        }
        [ -n "$FIRMWARE_PATH" ] || { echo "--firmware-path is required" >&2; exit 1; }
        curl -fsSL "$CGI_URL?info=$INFO_HEX" -o "$FIRMWARE_PATH"
        "$PYTHON" - "$FIRMWARE_PATH" "$RESULT_PATH" <<'PY'
import hashlib, json, pathlib, sys
firmware, result = map(pathlib.Path, sys.argv[1:])
payload = firmware.read_bytes()
if len(payload) < 1024:
    raise SystemExit("downloaded firmware is unexpectedly small")
if payload[:32].lstrip().lower().startswith((b"<!doctype", b"<html")):
    raise SystemExit("firmware server returned HTML")
result.write_text(json.dumps({"status":"ok","action":"device_firmware_download",
    "bytes":len(payload),"sha256":hashlib.sha256(payload).hexdigest()},
    separators=(",", ":")) + "\n", encoding="utf-8")
PY
        ;;
    *) echo "--mode must be check or download" >&2; exit 1 ;;
esac
