#!/bin/sh
# dpkg - native Terminal package manager (Debian .deb format subset)
#
# Packages must be built for the native Terminal/iOS environment (scripts or
# iOS/Darwin arm64 binaries).  Debian Linux arm64/amd64 packages are rejected
# by default because their ELF binaries cannot run on iOS without an emulator.

ROOT="${TERM_ROOT:-$HOME/Documents/.terminal}"
STATUS="$ROOT/var/lib/dpkg/status"
INFO="$ROOT/var/lib/dpkg/info"
FORCE_ARCH=0

usage() {
    printf '%s\n' "Usage: dpkg <action>"
    printf '%s\n' ""
    printf '%s\n' "  -i, --install <file.deb>    Install a Terminal-compatible .deb"
    printf '%s\n' "  -r, --remove <package>      Remove a package"
    printf '%s\n' "  -P, --purge <package>       Remove a package (same as -r here)"
    printf '%s\n' "  -l, --list                  List installed packages"
    printf '%s\n' "  -s, --status <package>      Show package status"
    printf '%s\n' "  -L, --listfiles <package>   List files installed by a package"
    printf '%s\n' "  -x, --extract <deb> <dir>   Extract a .deb data archive"
    printf '%s\n' "  --contents <deb>            List the contents of a .deb"
    printf '%s\n' "  --print-architecture        Print the native architecture"
    printf '%s\n' "  --force-architecture        Allow non-native architecture packages"
    printf '%s\n' "  --version                   Show version"
}
ensure() {
    mkdir -p "$ROOT" "$INFO" "$ROOT/tmp" "$ROOT/var/lib/dpkg"
    [ -f "$STATUS" ] || : > "$STATUS"
}

field() {
    key="$1"; file="$2"
    awk -v key="$key" 'index($0,key": ")==1 { sub("^" key ": ",""); print; exit }' "$file"
}

stanza_field() {
    key="$1"; stanza="$2"
    printf '%s\n' "$stanza" | awk -v key="$key" 'index($0,key": ")==1 { sub("^" key ": ",""); print; exit }'
}

stanza_for() {
    pkg="$1"
    awk -v pkg="$pkg" 'BEGIN{RS=""; ORS="\n\n"} $0 ~ "(^|\\n)Package: " pkg "(\\n|$)" { print; exit }' "$STATUS"
}

remove_status() {
    pkg="$1"
    TMP="$ROOT/tmp/status.$$"
    awk -v pkg="$pkg" 'BEGIN{RS=""; ORS="\n\n"} $0 !~ "(^|\\n)Package: " pkg "(\\n|$)" { print }' "$STATUS" > "$TMP"
    mv "$TMP" "$STATUS"
}

append_status() {
    {
        echo "Package: $PKG"
        echo "Status: install ok installed"
        echo "Priority: ${PRIORITY:-optional}"
        echo "Section: ${SECTION:-plugins}"
        echo "Installed-Size: ${INSTALLED_SIZE:-0}"
        echo "Maintainer: ${MAINTAINER:-unknown}"
        echo "Architecture: ${ARCH:-iphoneos-arm64}"
        echo "Version: $VERSION"
        echo "Description: $DESCRIPTION"
        echo ""
    } >> "$STATUS"
}

ar_extract() {
    archive="$1"
    dest="$2"
    tar -xf "$archive" -C "$dest" >/dev/null 2>&1
    if [ ! -f "$dest/debian-binary" ]; then
        if python3 -c 'pass' >/dev/null 2>&1 && [ -f "$APPDIR/bin/ardeb" ]; then
            python3 "$APPDIR/bin/ardeb" extract "$archive" "$dest"
        fi
    fi
}

install_deb() {
    DEB="$1"
    if [ ! -f "$DEB" ]; then
        echo "dpkg: cannot access archive '$DEB': No such file" >&2
        return 1
    fi
    if python3 -c 'pass' >/dev/null 2>&1 && [ -f "$APPDIR/bin/pkgtool" ]; then
        python3 "$APPDIR/bin/pkgtool" install "$DEB"
        return $?
    fi
    ensure
    echo "dpkg: unpacking $DEB"
    TMP="$ROOT/tmp/dpkg.$$"
    rm -rf "$TMP"
    mkdir -p "$TMP"
    cp "$DEB" "$TMP/pkg.deb" || { rm -rf "$TMP"; return 1; }
    ar_extract "$TMP/pkg.deb" "$TMP"
    echo "dpkg: ar extracted"
    if [ ! -f "$TMP/debian-binary" ]; then
        echo "dpkg: '$DEB' is not a Debian-format archive" >&2
        rm -rf "$TMP"
        return 1
    fi

    CTRL_ARCHIVE=""
    for f in "$TMP"/control.tar.*; do [ -f "$f" ] && { CTRL_ARCHIVE="$f"; break; }; done
    DATA_ARCHIVE=""
    for f in "$TMP"/data.tar.*; do [ -f "$f" ] && { DATA_ARCHIVE="$f"; break; }; done
    if [ -z "$CTRL_ARCHIVE" ] || [ -z "$DATA_ARCHIVE" ]; then
        echo "dpkg: missing control or data archive" >&2
        rm -rf "$TMP"
        return 1
    fi

    mkdir -p "$TMP/control"
    case "$CTRL_ARCHIVE" in
        *.gz) tar -xzf "$CTRL_ARCHIVE" -C "$TMP/control" 2>/dev/null ;;
        *.xz) tar -xJf "$CTRL_ARCHIVE" -C "$TMP/control" 2>/dev/null ;;
        *)    tar -xf "$CTRL_ARCHIVE" -C "$TMP/control" 2>/dev/null ;;
    esac
    CONTROL=""
    if [ -f "$TMP/control/control" ]; then
        CONTROL="$TMP/control/control"
    elif [ -f "$TMP/control/DEBIAN/control" ]; then
        CONTROL="$TMP/control/DEBIAN/control"
    fi
    if [ -z "$CONTROL" ]; then
        echo "dpkg: no control file in package" >&2
        rm -rf "$TMP"
        return 1
    fi

    PKG=$(field Package "$CONTROL")
    VERSION=$(field Version "$CONTROL")
    ARCH=$(field Architecture "$CONTROL")
    PRIORITY=$(field Priority "$CONTROL")
    SECTION=$(field Section "$CONTROL")
    MAINTAINER=$(field Maintainer "$CONTROL")
    DESCRIPTION=$(field Description "$CONTROL")
    [ -n "$PKG" ] && [ -n "$VERSION" ] || {
        echo "dpkg: invalid control file (Package/Version missing)" >&2
        rm -rf "$TMP"
        return 1
    }
    case "$PKG" in
        *[!A-Za-z0-9+.-]*|"") echo "dpkg: invalid package name: $PKG" >&2; rm -rf "$TMP"; return 1 ;;
    esac

    case "$ARCH" in
        ""|all|iphoneos-arm64|darwin-arm64|a-shell) ;;
        *)
            if [ "$FORCE_ARCH" != "1" ]; then
                echo "dpkg: architecture '$ARCH' is not native to iOS/Terminal." >&2
                echo "      Debian Linux binaries cannot run without an emulator." >&2
                echo "      Use --force-architecture only for script-only packages." >&2
                rm -rf "$TMP"
                return 1
            fi
            echo "dpkg: warning: forcing architecture '$ARCH'" >&2
            ;;
    esac

    echo "Selecting previously unselected package $PKG."
    echo "Unpacking $PKG ($VERSION) ..."

    if [ -f "$TMP/control/preinst" ]; then
        TERM_ROOT="$ROOT" PREFIX="$ROOT" sh "$TMP/control/preinst" install || {
            echo "dpkg: preinst script failed" >&2
            rm -rf "$TMP"
            return 1
        }
    fi

    echo "dpkg: extracting data archive..."
    case "$DATA_ARCHIVE" in
        *.gz) tar -tzf "$DATA_ARCHIVE" 2>/dev/null > "$TMP/filelist" ;;
        *.xz) tar -tJf "$DATA_ARCHIVE" 2>/dev/null > "$TMP/filelist" ;;
        *)    tar -tf "$DATA_ARCHIVE" 2>/dev/null > "$TMP/filelist" ;;
    esac
    awk '{ sub(/^\.\//,""); if ($0 != "") print }' "$TMP/filelist" > "$INFO/$PKG.list"
    DATA_OK=0
    case "$DATA_ARCHIVE" in
        *.gz) tar -xzf "$DATA_ARCHIVE" -C "$ROOT" --no-same-owner 2>/dev/null && DATA_OK=1 ;;
        *.xz) tar -xJf "$DATA_ARCHIVE" -C "$ROOT" --no-same-owner 2>/dev/null && DATA_OK=1 ;;
        *)    tar -xf "$DATA_ARCHIVE" -C "$ROOT" --no-same-owner 2>/dev/null && DATA_OK=1 ;;
    esac
    if [ $DATA_OK -ne 1 ]; then
        case "$DATA_ARCHIVE" in
            *.gz) tar -xzf "$DATA_ARCHIVE" -C "$ROOT" 2>/dev/null && DATA_OK=1 ;;
            *.xz) tar -xJf "$DATA_ARCHIVE" -C "$ROOT" 2>/dev/null && DATA_OK=1 ;;
            *)    tar -xf "$DATA_ARCHIVE" -C "$ROOT" 2>/dev/null && DATA_OK=1 ;;
        esac
    fi
    if [ $DATA_OK -ne 1 ]; then
        echo "dpkg: failed to extract data archive" >&2
        rm -rf "$TMP"
        return 1
    fi

    # Warn about native Mach-O payloads: a-Shell cannot execute them.
    if [ -f "$INFO/$PKG.list" ]; then
        for f in $(cat "$INFO/$PKG.list"); do
            case "$f" in
                bin/*|sbin/*|usr/bin/*|usr/sbin/*|usr/libexec/*)
                    if file "$ROOT/$f" 2>/dev/null | grep -q 'Mach-O'; then
                        echo "dpkg: note: $f is a native Mach-O executable; a-Shell cannot run it." >&2
                    fi
                    ;;
            esac
        done
    fi

    remove_status "$PKG"
    INSTALLED_SIZE=$(du -sk "$ROOT" 2>/dev/null)
    INSTALLED_SIZE=${INSTALLED_SIZE%%[!0-9]*}
    append_status

    for script in preinst postinst prerm postrm; do
        if [ -f "$TMP/control/$script" ]; then
            cp "$TMP/control/$script" "$INFO/$PKG.$script"
            chmod +x "$INFO/$PKG.$script"
        fi
    done

    if [ -f "$TMP/control/postinst" ]; then
        TERM_ROOT="$ROOT" PREFIX="$ROOT" sh "$TMP/control/postinst" configure || {
            echo "dpkg: postinst script failed" >&2
            rm -rf "$TMP"
            return 1
        }
    fi

    echo "Setting up $PKG ($VERSION) ..."
    rm -rf "$TMP"
    return 0
}

remove_pkg() {
    PKG="$1"
    [ -n "$PKG" ] || { usage; return 2; }
    ensure
    STANZA=$(stanza_for "$PKG")
    if [ -z "$STANZA" ]; then
        echo "dpkg: package '$PKG' is not installed" >&2
        return 1
    fi
    if [ -f "$INFO/$PKG.prerm" ]; then
        ( TERM_ROOT="$ROOT" PREFIX="$ROOT" sh "$INFO/$PKG.prerm" remove ) || true
    fi
    if [ -f "$INFO/$PKG.list" ]; then
        for f in $(cat "$INFO/$PKG.list"); do
            case "$f" in
                ""|/*|../*|*/../*) continue ;;
            esac
            [ -d "$ROOT/$f" ] && continue
            rm -f "$ROOT/$f"
        done
    fi
    if [ -f "$INFO/$PKG.postrm" ]; then
        ( TERM_ROOT="$ROOT" PREFIX="$ROOT" sh "$INFO/$PKG.postrm" remove ) || true
    fi
    remove_status "$PKG"
    rm -f "$INFO/$PKG.list" "$INFO/$PKG.preinst" "$INFO/$PKG.postinst" \
          "$INFO/$PKG.prerm" "$INFO/$PKG.postrm"
    echo "Removing $PKG ..."
    echo "Purging configuration files for $PKG ..."
    return 0
}

list_packages() {
    ensure
    echo "Desired=Unknown/Install/Remove/Purge/Hold"
    echo "| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend"
    echo "|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)"
    echo "||/ Name                 Version          Architecture     Description"
    echo "+++-====================-================-================-================================="
    awk 'BEGIN{RS="";FS="\n"} {
        p="";v="";a="";d="";s="";
        for (i=1;i<=NF;i++) {
            if ($i ~ /^Package: /) p=substr($i,10);
            if ($i ~ /^Version: /) v=substr($i,10);
            if ($i ~ /^Architecture: /) a=substr($i,14);
            if ($i ~ /^Description: /) d=substr($i,14);
            if ($i ~ /^Status: /) s=substr($i,9);
        }
        if (s ~ /installed/) printf "ii  %-20s %-16s %-16s %s\n", p, v, a, d;
    }' "$STATUS"
}

status_pkg() {
    PKG="$1"
    STANZA=$(stanza_for "$PKG")
    if [ -z "$STANZA" ]; then
        echo "dpkg-query: package '$PKG' is not installed" >&2
        return 1
    fi
    printf '%s\n' "$STANZA"
}

list_files() {
    PKG="$1"
    if [ ! -f "$INFO/$PKG.list" ]; then
        echo "dpkg-query: package '$PKG' is not installed" >&2
        return 1
    fi
    sed "s#^#/#" "$INFO/$PKG.list"
}

extract_deb() {
    DEB="$1"; DEST="$2"
    [ -f "$DEB" ] || { echo "dpkg: cannot access '$DEB'" >&2; return 1; }
    [ -n "$DEST" ] || { usage; return 2; }
    TMP="$ROOT/tmp/dpkg-x.$$"
    rm -rf "$TMP"; mkdir -p "$TMP" "$DEST"
    cp "$DEB" "$TMP/pkg.deb"
    ar_extract "$TMP/pkg.deb" "$TMP"
    DATA=""
    for f in "$TMP"/data.tar.*; do [ -f "$f" ] && { DATA="$f"; break; }; done
    [ -n "$DATA" ] || { echo "dpkg: no data archive" >&2; rm -rf "$TMP"; return 1; }
    tar -xf "$DATA" -C "$DEST" --no-same-owner 2>/dev/null || tar -xf "$DATA" -C "$DEST" 2>/dev/null
    rm -rf "$TMP"
}

contents_deb() {
    DEB="$1"
    [ -f "$DEB" ] || { echo "dpkg: cannot access '$DEB'" >&2; return 1; }
    TMP="$ROOT/tmp/dpkg-c.$$"
    rm -rf "$TMP"; mkdir -p "$TMP"
    cp "$DEB" "$TMP/pkg.deb"
    ar_extract "$TMP/pkg.deb" "$TMP"
    DATA=""
    for f in "$TMP"/data.tar.*; do [ -f "$f" ] && { DATA="$f"; break; }; done
    [ -n "$DATA" ] || { echo "dpkg: no data archive" >&2; rm -rf "$TMP"; return 1; }
    case "$DATA" in
        *.gz) tar -tzf "$DATA" 2>/dev/null > "$TMP/contents" ;;
        *.xz) tar -tJf "$DATA" 2>/dev/null > "$TMP/contents" ;;
        *)    tar -tf "$DATA" 2>/dev/null > "$TMP/contents" ;;
    esac
    awk '{ sub(/^\.\//,""); if ($0 != "") print }' "$TMP/contents"
    rm -rf "$TMP"
}

case "${1:-}" in
    -i|--install)
        shift
        if [ "${1:-}" = "--force-architecture" ]; then FORCE_ARCH=1; shift; fi
        [ -n "${1:-}" ] || { usage; exit 2; }
        install_deb "$1"
        ;;
    -r|--remove|-P|--purge)
        shift
        remove_pkg "${1:-}"
        ;;
    -l|--list)
        list_packages
        ;;
    -s|--status)
        shift
        status_pkg "${1:-}"
        ;;
    -L|--listfiles)
        shift
        list_files "${1:-}"
        ;;
    -x|--extract)
        shift
        extract_deb "${1:-}" "${2:-}"
        ;;
    --contents)
        shift
        contents_deb "${1:-}"
        ;;
    --print-architecture)
        echo "iphoneos-arm64"
        ;;
    --force-architecture)
        FORCE_ARCH=1
        shift
        [ -n "${1:-}" ] && install_deb "$1"
        ;;
    --version|-V)
        echo "dpkg 1.0 (Terminal native package manager)"
        ;;
    ""|-h|--help|help)
        usage
        ;;
    *)
        usage
        exit 2
        ;;
esac
