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

DEFAULT_REPO="https://github.com/IO-ZetZor/Visor-BootManager.git"

PKG_LIB_DIR="${VISOR_LIB_DIR:-/usr/lib/visor}"
PKG_DATA_DIR="${VISOR_DATA_DIR:-/usr/share/visor}"

real_home() {
    local user="${SUDO_USER:-${PKEXEC_UID:-}}"
    if [ -n "$user" ]; then
        case "$user" in
            *[!A-Za-z0-9_.-]*)
                printf '%s' "${HOME:-/tmp}"
                return 0
                ;;
        esac
        if command -v getent >/dev/null 2>&1; then
            local h
            h="$(getent passwd "$user" 2>/dev/null | cut -d: -f6)"
            [ -n "$h" ] && [ -d "$h" ] && { printf '%s' "$h"; return 0; }
        fi
        eval "printf '%s'" "~$user" 2>/dev/null && return 0
        printf '%s' "${HOME:-/tmp}"
    else
        printf '%s' "${HOME:-/tmp}"
    fi
}

host_efi_name() {
    case "$(uname -m 2>/dev/null)" in
        aarch64) printf 'visor_aa64.efi\n' ;;
        *)       printf 'visor_x64.efi\n' ;;
    esac
}

packaged_available() {
    [ -f "$PKG_LIB_DIR/$(host_efi_name)" ] && [ -f "$PKG_DATA_DIR/boot.conf.example" ]
}

source_dir_default() {
    printf '%s\n' "${VISOR_SOURCE_DIR:-${XDG_CACHE_HOME:-$(real_home)/.cache}/visor/source}"
}

say() { printf '\033[1;34m::\033[0m %s\n' "$*"; }
ok()  { printf '\033[1;32mok\033[0m  %s\n' "$*"; }
warn() { printf '\033[1;33m!!\033[0m %s\n' "$*" >&2; }
die() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; }

usage() {
    cat <<'EOF'
Usage:
  visor <command> [options]

Commands:
  build [make-args...]          Build visor_x64.efi
  install [install-args...]     Install to the ESP. Uses a source checkout when
                                available, otherwise installs the packaged files
                                from /usr/lib/visor (--packaged forces this)
  update [options]              Pull latest GitHub version, build, and install
  sign [options]                Sign installed EFI binaries with sbctl
  drivers [options]             Install an EfiFs filesystem driver for /boot
  encrypt <kernel> [initrd]     Encrypt kernel (and initrd) with one password,
                                install to the ESP, and set up the boot entry
  convert <video> [options]     Convert video to animated background (MJPEG or
                                compact VBG via --vbg, 10–20× smaller)
  features [options]            Show the feature set compiled into a binary
  uninstall [options]           Remove installed Visor files
  status [options]              Show install/source status
  config validate [options]      Validate a config file
  clean [--all]                 Remove build outputs (--all also removes the
                                ~/.cache/visor source checkout, with confirmation)
  studio [options]              Fetch and run the Visor Studio configurator locally
  doctor                        Check build/install dependencies
  help                          Show this help

Update options:
  --esp PATH         ESP mount point to pass to install.sh
  --profile NAME     feature profile to pass to install.sh (default: reuse the
                     last installed profile, or prompt)
  --features LIST    feature tweaks to pass to install.sh (see install --features)
  --repo URL         Git repository URL
  --branch NAME      Git branch to track
  --source-dir PATH  Local checkout path (default: ~/.cache/visor/source)
  --boot-entry       Ensure a Visor UEFI boot entry exists
  --sign             Sign the updated EFI binary with sbctl
  --force-config     Replace boot.conf with the repo example

Sign options:
  --esp PATH         ESP mount point
  --source-dir PATH  Local checkout path
  --no-drivers      Do not sign EFI files in \EFI\visor\drivers

Drivers options:
  --esp PATH         ESP mount point
  --fs TYPE          filesystem type (default: auto-detect from /boot)
  --sign             sign the installed driver with sbctl

Encrypt options:
  --esp PATH         ESP mount point (install the result into \EFI\visor)
  --out PATH         Write the encrypted file here instead of the ESP (single input)
  --name NAME        Output filename under \EFI\visor (default: <input>.enc)
  --title TEXT       Menu entry name (default: "Encrypted Linux")
  --iterations N     PBKDF2 iterations (default 600000)
  --initrd           The single input is an initrd, not a kernel
  --no-config        Only print the boot.conf entry, never offer to append it
  --no-luks          Do not add luks=1 even if the running system uses LUKS

Uninstall options:
  --esp PATH         ESP mount point
  --cli-dir PATH     Directory to remove the host-side command from
  --keep-entry       Keep the UEFI boot entry

Status options:
  --esp PATH         ESP mount point
  --source-dir PATH  Local checkout path

Features options:
  --esp PATH         ESP mount point
  --binary PATH      EFI binary to inspect (default: ESP install, then packaged)
  --json             Machine-readable JSON on stdout

Doctor options:
  --esp PATH         ESP mount point
  --source-dir PATH  Source tree to check
  --file PATH        Config file to validate
  --build            Run a build as part of diagnostics

Config validate options:
  --file PATH        Config file to validate
  --esp PATH         ESP mount point

Studio options:
  --update           Pull the latest Visor Studio before running
  --port PORT        Backend port (default 8000)
  --esp PATH         ESP mount point for local config access
  --docker           Require the Docker runtime (otherwise Docker is used when
                     available, falling back to python3+node)
  --native           Run with local python3+npm even when Docker is available
  --detach           Run in the background and return immediately
EOF
}

need_cmd() {
    command -v "$1" >/dev/null 2>&1 || die "$1 is required"
}

need_value() {
    local opt="$1"
    local val="${2:-}"
    [ -n "$val" ] || die "$opt needs a value"
    case "$val" in
        --*) die "$opt needs a value" ;;
    esac
    printf '%s\n' "$val"
}

script_dir() {
    local src="${BASH_SOURCE[0]}"
    while [ -L "$src" ]; do
        local dir
        dir="$(cd -P "$(dirname "$src")" && pwd)"
        src="$(readlink "$src")"
        [[ "$src" != /* ]] && src="$dir/$src"
    done
    cd -P "$(dirname "$src")" && pwd
}

source_dir_candidates() {
    local home
    home="$(real_home)"
    printf '%s\n' "$(script_dir)"
    printf '%s\n' "$(source_dir_default)"
    printf '%s\n' "${XDG_CACHE_HOME:-$home/.cache}/visor-src"
    printf '%s\n' "$PKG_DATA_DIR"
}

is_source_tree() {
    [ -f "$1/Makefile" ] && [ -f "$1/install.sh" ]
}

has_installer() {
    [ -f "$1/install.sh" ]
}

project_dir() {
    local d
    while IFS= read -r d; do
        [ -n "$d" ] || continue
        is_source_tree "$d" && { printf '%s\n' "$d"; return 0; }
    done <<EOF
$(source_dir_candidates)
EOF
    source_dir_default
}

installer_dir() {
    local d
    while IFS= read -r d; do
        [ -n "$d" ] || continue
        has_installer "$d" && { printf '%s\n' "$d"; return 0; }
    done <<EOF
$(source_dir_candidates)
EOF
    return 1
}

no_source_hint() {
    warn "No Visor source tree found. Looked in:"
    local d
    while IFS= read -r d; do
        [ -n "$d" ] || continue
        printf '      %s\n' "$d" >&2
    done <<EOF
$(source_dir_candidates)
EOF
    warn "Run 'visor update' to fetch and build the latest source,"
    warn "or set VISOR_SOURCE_DIR / pass --source-dir PATH."
}

run_root() {
    if [ "$(id -u)" -eq 0 ]; then
        "$@"
    elif command -v sudo >/dev/null 2>&1; then
        sudo "$@"
    else
        "$@"
    fi
}

run_install() {
    local script="$1"
    shift
    if [ "$(id -u)" -eq 0 ]; then
        bash "$script" "$@"
    elif command -v sudo >/dev/null 2>&1; then
        say "Installing with sudo"
        sudo bash "$script" "$@"
    else
        bash "$script" "$@"
    fi
}

detect_esp() {
    if command -v bootctl >/dev/null 2>&1; then
        local p
        p="$(bootctl --print-esp-path 2>/dev/null || true)"
        [ -n "$p" ] && { printf '%s\n' "$p"; return 0; }
    fi

    local m
    for m in /boot/efi /efi /boot; do
        if mountpoint -q "$m" 2>/dev/null && [ "$(findmnt -Uno FSTYPE "$m" 2>/dev/null)" = vfat ]; then
            printf '%s\n' "$m"
            return 0
        fi
    done

    if command -v lsblk >/dev/null 2>&1; then
        lsblk -o MOUNTPOINT,PARTTYPENAME -rn 2>/dev/null | awk -F' ' '/EFI System/ && $1!="" {print $1; exit}'
    fi
}

resolve_esp() {
    local esp="${1:-}"
    if [ -n "$esp" ]; then
        printf '%s\n' "$esp"
        return 0
    fi
    detect_esp
}

valid_visor_entries() {
    efibootmgr 2>/dev/null | awk '
        /^[Bb]oot[0-9A-Fa-f]{4}[^0-9A-Fa-f]/ && /[Vv]isor/ { print; found=1 }
        END { exit !found }
    '
}

corrupted_visor_entries() {
    efibootmgr 2>/dev/null | awk '
        /^[Bb]oot[0-9A-Fa-f]{5,}[^0-9A-Fa-f]/ && /[Vv]isor/ { print }
    '
}

ensure_visor_boot_entry() {
    local esp="$1"
    command -v efibootmgr >/dev/null 2>&1 || { warn "efibootmgr not installed; skipping boot entry."; return 0; }
    if valid_visor_entries >/dev/null; then
        say "UEFI boot entry 'Visor' already exists; left untouched."
        return 0
    fi
    if corrupted_visor_entries >/dev/null; then
        warn "Malformed UEFI boot entry 'Visor' detected; creating a fresh valid one."
        warn "Old unusable entries may need manual cleanup (see '#32')."
    fi
    local src disk partnum
    src="$(findmnt -Uno SOURCE "$esp")" || { warn "Cannot resolve ESP device; skipping boot entry."; return 0; }
    disk="/dev/$(lsblk -no PKNAME "$src")"
    partnum="$(lsblk -no PARTN "$src" 2>/dev/null || echo "$src" | grep -o '[0-9]*$')"
    if [ ! -b "$disk" ]; then
        warn "Could not determine ESP disk (got '$disk'); skipping boot entry."
        return 0
    fi
    efibootmgr --create --disk "$disk" --part "$partnum" \
               --label "Visor" --loader "\\EFI\\visor\\$(host_efi_name)" >/dev/null
    say "UEFI boot entry 'Visor' -> $disk part $partnum"
}

packaged_install() {
    local esp=""
    local boot_entry=1
    local force_config=0
    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --boot-entry) boot_entry=1; shift ;;
            --no-boot-entry) boot_entry=0; shift ;;
            --force-config) force_config=1; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown install option for packaged mode: $1" ;;
        esac
    done

    local efi_name
    efi_name="$(host_efi_name)"
    local efi_src="$PKG_LIB_DIR/$efi_name"
    [ -f "$efi_src" ] || die "packaged EFI missing: $efi_src"

    [ -z "$esp" ] && esp="$(detect_esp || true)"
    [ -n "$esp" ] || die "Could not find the ESP. Re-run with: --esp /your/esp/mount"
    [ -d "$esp" ] || die "ESP path does not exist: $esp"
    [ -w "$esp" ] || die "No write permission on $esp. Re-run with sudo."

    local dest="$esp/EFI/visor"
    say "Installing from packaged files into $dest"
    mkdir -p "$dest/icons" "$dest/backgrounds" "$dest/drivers" 2>/dev/null || die "cannot create $dest"

    if [ -f "$dest/$efi_name" ]; then
        cp -f "$dest/$efi_name" "$dest/$efi_name.bak"
        say "Previous binary kept as $efi_name.bak"
    fi
    install -m 0644 "$efi_src" "$dest/$efi_name"
    ok "Loader: $dest/$efi_name"

    if [ -d "$PKG_DATA_DIR/icons" ]; then
        cp -f "$PKG_DATA_DIR"/icons/*.png "$dest/icons/" 2>/dev/null || true
    fi
    if [ -d "$PKG_DATA_DIR/backgrounds" ]; then
        cp -f "$PKG_DATA_DIR"/backgrounds/*.png "$dest/backgrounds/" 2>/dev/null || true
    fi
    if [ -f "$PKG_DATA_DIR/logo.png" ]; then
        install -m 0644 "$PKG_DATA_DIR/logo.png" "$dest/logo.png" 2>/dev/null || true
    fi
    ok "Assets: icons, backgrounds, logo"

    local conf="$dest/boot.conf"
    if [ -f "$conf" ] && [ "$force_config" -eq 0 ]; then
        ok "Config: kept existing $conf"
    else
        install -m 0644 "$PKG_DATA_DIR/boot.conf.example" "$conf"
        ok "Config: wrote default $conf"
    fi

    if [ ! -e "$dest/boot.log" ]; then
        install -m 0644 /dev/null "$dest/boot.log"
        say "Created boot log: $dest/boot.log"
    fi

    if [ "$boot_entry" -eq 1 ]; then
        ensure_visor_boot_entry "$esp"
    fi
    say "Done. Reboot to try Visor."
}

project_or_die() {
    local dir="${1:-}"
    if [ -n "$dir" ]; then
        is_source_tree "$dir" || { no_source_hint; die "not a Visor source tree: $dir"; }
        printf '%s\n' "$dir"
        return 0
    fi
    dir="$(project_dir)"
    is_source_tree "$dir" || { no_source_hint; die "no Visor source tree available"; }
    printf '%s\n' "$dir"
}

find_encrypt_tool() {
    local c
    for c in "$(script_dir)/tools/visor_encrypt.py" \
             "$(project_dir)/tools/visor_encrypt.py" \
             "$(source_dir_default)/tools/visor_encrypt.py" \
             "$PKG_DATA_DIR/tools/visor_encrypt.py"; do
        [ -f "$c" ] && { printf '%s\n' "$c"; return 0; }
    done
    return 1
}

find_vbg_tool() {
    local c
    local env="${VISOR_VBG_TOOL:-}"
    if [ -n "$env" ] && [ -f "$env" ]; then printf '%s\n' "$env"; return 0; fi
    for c in "$(script_dir)/tools/vbg_encode.py" \
             "$(project_dir)/tools/vbg_encode.py" \
             "$(source_dir_default)/tools/vbg_encode.py" \
             "$PKG_DATA_DIR/tools/vbg_encode.py"; do
        [ -f "$c" ] && { printf '%s\n' "$c"; return 0; }
    done
    return 1
}

cmd_convert() {
    local input="" out="" esp="" scale="" quality="" threshold="" qshift="" tile="" keyint="" mvrange="" denoise="" fps="" vbg=0 force=0
    while [ $# -gt 0 ]; do
        case "$1" in
            --out) out="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --scale) scale="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --quality) quality="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --threshold) threshold="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --qshift) qshift="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --tile) tile="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --keyint) keyint="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --mv-range) mvrange="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --denoise) denoise="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --fps) fps="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --vbg) vbg=1; shift ;;
            --no-vbg) vbg=0; shift ;;
            --force) force=1; shift ;;
            -h|--help) cat <<'EOF'
visor convert <video> [options]
  Convert any video into an animated Visor background.
  Without --vbg: MJPEG MP4 at the source frame rate.
  With    --vbg: compact VBG (tiled delta + motion, 10–20× smaller).

  --vbg              use VBG instead of MJPEG
  --out PATH         output file (default: <input>.mp4 or .vbg with --vbg)
  --esp PATH         install directly into \EFI\visor\backgrounds on the ESP
  --scale WxH|H      scale, e.g. 1920x1080 or 1080 (default: keep original, match screen)
  --quality N        MJPEG/VBG keyframe quality 2–31 (default 3, lower is better)
  --force            overwrite existing output
VBG only:
  --fps N            output fps, 12 is plenty for ambient motion
  --keyint N         frames between keyframes (default: single keyframe)
  --no-motion|--mv-range 0  disable motion compensation
  --mv-range N       motion search radius 0–127 (default 16)
  --denoise SPEC|none temporal denoise (default mild)
  --threshold T      per-channel dead zone
  --qshift S         residual quantizer 0–7
  --tile N           tile size power-of-two 4–64
  --denoise SPEC     ffmpeg denoise filter
EOF
                exit 0 ;;
            --*) die "unknown convert option: $1" ;;
            *)
                if [ -z "$input" ]; then input="$1"; shift
                else die "unexpected argument: $1"
                fi
                ;;
        esac
    done
    [ -n "$input" ] || die "usage: visor convert <video> [--vbg] [--out PATH|--esp PATH] [options]"
    [ -f "$input" ] || die "input not found: $input"
    if [ -n "$out" ] && [ -n "$esp" ]; then die "--out and --esp are mutually exclusive"; fi
    if [ -n "$esp" ]; then
        esp="$(resolve_esp "$esp")"
        [ -n "$esp" ] || die "could not find ESP (pass --esp PATH)"
        [ -d "$esp/EFI/visor" ] || die "Visor not installed at $esp/EFI/visor — run 'visor install' first"
        require_feature anim_mp4 "install an animated background for this loader" "$esp"
        local base
        base="$(basename "$input")"
        base="${base%.*}"
        if [ "$vbg" -eq 1 ]; then base="$base.vbg"; else base="$base.mp4"; fi
        out="$esp/EFI/visor/backgrounds/$base"
        say "Will install to $out"
    fi
    if [ -z "$out" ]; then
        if [ "$vbg" -eq 1 ]; then out="${input%.*}.vbg"; else out="${input%.*}.mp4"; fi
    fi
    if [ -f "$out" ] && [ "$force" -eq 0 ]; then die "output exists: $out (pass --force to overwrite)"; fi
    mkdir -p "$(dirname "$out")"
    if [ "$vbg" -eq 1 ]; then
        local tool
        tool="$(find_vbg_tool)" || die "vbg_encode.py not found (run from source tree or 'visor update')"
        need_cmd python3
        local pyargs=("$tool" "$input" "$out")
        [ -n "$scale" ] && pyargs+=(--scale "$scale")
        [ -n "$quality" ] && pyargs+=(--quality "$quality")
        [ -n "$threshold" ] && pyargs+=(--threshold "$threshold")
        [ -n "$qshift" ] && pyargs+=(--qshift "$qshift")
        [ -n "$tile" ] && pyargs+=(--tile "$tile")
        [ -n "$keyint" ] && pyargs+=(--keyint "$keyint")
        [ -n "$mvrange" ] && pyargs+=(--mv-range "$mvrange")
        [ -n "$denoise" ] && pyargs+=(--denoise "$denoise")
        [ -n "$fps" ] && pyargs+=(--fps "$fps")
        say "Encoding VBG: $input → $out"
        python3 "${pyargs[@]}" || die "vbg_encode failed"
        ok "VBG ready: $out ($(du -h "$out" | cut -f1))"
        if [ -n "$esp" ]; then say "Installed: $out  — set background=$out in boot.conf (\\EFI\\visor\\backgrounds\\$(basename "$out"))"; fi
    else
        need_cmd ffmpeg
        local vf=""
        if [ -n "$scale" ]; then
            case "$scale" in
                *x*) vf="scale=$scale:flags=lanczos" ;;
                *) vf="scale=-2:$scale:flags=lanczos" ;;
            esac
        fi
        local q="${quality:-3}"
        say "Encoding MJPEG: $input → $out"
        if [ -n "$vf" ]; then
            ffmpeg -y -i "$input" -vf "$vf" -c:v mjpeg -q:v "$q" -an "$out" || die "ffmpeg failed"
        else
            ffmpeg -y -i "$input" -c:v mjpeg -q:v "$q" -an "$out" || die "ffmpeg failed"
        fi
        ok "MJPEG ready: $out ($(du -h "$out" | cut -f1))"
        if [ -n "$esp" ]; then say "Installed: $out"; fi
    fi
}

cmd_build() {
    local dir
    dir="$(project_or_die)"
    say "Building Visor in $dir"
    make --no-print-directory -C "$dir" "$@"
}

run_packaged_install() {
    if [ "$(id -u)" -ne 0 ] && command -v sudo >/dev/null 2>&1; then
        say "Installing from the packaged files (re-running with sudo)"
        exec sudo bash "$0" install --packaged "$@"
    fi
    packaged_install "$@"
}

cmd_install() {
    local dir=""
    local pass=()
    while [ $# -gt 0 ]; do
        case "$1" in
            --source-dir) dir="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --packaged) dir="@packaged@"; shift ;;
            *) pass+=("$1"); shift ;;
        esac
    done

    if [ "$dir" = "@packaged@" ]; then
        run_packaged_install "${pass[@]+"${pass[@]}"}"
        return 0
    fi

    if [ -z "$dir" ]; then
        dir="$(installer_dir || true)"
        if [ -z "$dir" ]; then
            if packaged_available; then
                say "No source tree found - installing from the packaged files"
                run_packaged_install "${pass[@]+"${pass[@]}"}"
                return 0
            fi
            no_source_hint
            die "no install.sh found - run 'visor update' to fetch the source first"
        fi
    fi
    [ -f "$dir/install.sh" ] || { no_source_hint; die "install.sh not found in $dir"; }
    say "Installing Visor from $dir"
    run_install "$dir/install.sh" "${pass[@]+"${pass[@]}"}"
}

cmd_clean() {
    local dir
    local all=0
    case "${1:-}" in
        --all) all=1 ;;
        -h|--help) usage; exit 0 ;;
        --*) die "unknown clean option: $1" ;;
    esac

    dir="$(project_or_die)"
    say "Cleaning build outputs in $dir"
    make --no-print-directory -C "$dir" clean

    if [ "$all" -eq 1 ]; then
        local home cache
        home="$(real_home)"
        cache="${XDG_CACHE_HOME:-$home/.cache}"
        local default_v="${VISOR_SOURCE_DIR:-$cache/visor/source}"
        local legacy="$cache/visor-src"
        local removed=0
        local d
        for d in "$default_v" "$legacy"; do
            [ -e "$d" ] || continue
            local ans=""
            printf 'Remove source checkout %s? [y/N] ' "$d"
            read -r ans || ans=""
            case "${ans,,}" in
                y|yes)
                    rm -rf -- "$d" && removed=1 || warn "could not remove $d"
                    ;;
                *) say "Keeping $d" ;;
            esac
        done
        if [ "$removed" -eq 1 ]; then
            say "Re-run 'visor update' to re-clone the source."
        fi
    fi
    return 0
}

cmd_doctor() {
    local esp=""
    local source_dir=""
    local config_file=""
    local check_build=0
    local source_explicit=0

    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --source-dir) source_dir="$(need_value "$1" "${2:-}")"; source_explicit=1; shift 2 ;;
            --file) config_file="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --build) check_build=1; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown doctor option: $1" ;;
        esac
    done

    local ok=1
    local c
    local dir
    dir="$(project_dir)"
    [ -z "$source_dir" ] && source_dir="$dir"

    printf 'Source: %s\n' "$source_dir"
    for c in git make objcopy objdump awk sed od; do
        if command -v "$c" >/dev/null 2>&1; then
            printf 'ok  %s\n' "$c"
        else
            printf 'miss %s\n' "$c"
            ok=0
        fi
    done

    local host_arch crt0_name cc_cands cc_found=""
    case "$(uname -m 2>/dev/null)" in
        aarch64) host_arch=aarch64; crt0_name=crt0-efi-aarch64.o
                 cc_cands="aarch64-linux-gnu-gcc" ;;
        *)       host_arch=x86_64;  crt0_name=crt0-efi-x86_64.o
                 cc_cands="x86_64-linux-gnu-gcc gcc cc" ;;
    esac
    for c in $cc_cands; do
        if command -v "$c" >/dev/null 2>&1; then cc_found="$c"; break; fi
    done
    if [ -n "$cc_found" ]; then
        printf 'ok  %s\n' "$cc_found"
    else
        printf 'miss C compiler for %s (want: %s)\n' "$host_arch" "${cc_cands%% *}"
        ok=0
    fi

    if command -v python3 >/dev/null 2>&1; then
        printf 'ok  python3\n'
    else
        printf 'warn python3 missing; encryption/font tools unavailable\n'
    fi

    if [ -d /usr/include/efi ] || [ -d /usr/local/include/efi ] || [ -d /usr/include/gnuefi/efi ]; then
        printf 'ok  gnu-efi headers\n'
    else
        printf 'miss gnu-efi headers\n'
        ok=0
    fi

    if [ -f "/usr/lib/$crt0_name" ] ||
       [ -f "/usr/lib64/gnuefi/$crt0_name" ] ||
       [ -f "/usr/lib/gnuefi/$crt0_name" ] ||
       [ -f "/usr/lib/$host_arch-linux-gnu/$crt0_name" ] ||
       [ -f "/usr/lib/$host_arch-linux-gnu/gnuefi/$crt0_name" ] ||
       [ -f "$source_dir/gnu-efi/$host_arch/gnuefi/$crt0_name" ]; then
        printf 'ok  gnu-efi crt0 (%s)\n' "$crt0_name"
    else
        printf 'miss gnu-efi crt0 (%s)\n' "$crt0_name"
        ok=0
    fi

    if [ -f "$source_dir/Makefile" ] && [ -f "$source_dir/install.sh" ]; then
        printf 'ok  source tree\n'
    elif [ "$source_explicit" -eq 0 ] && packaged_available; then
        printf 'ok  packaged install: %s + %s\n' "$PKG_LIB_DIR" "$PKG_DATA_DIR"
    else
        printf 'miss source tree files\n'
        ok=0
    fi

    if [ -f "$source_dir/$(host_efi_name)" ]; then
        printf 'ok  built EFI: %s\n' "$source_dir/$(host_efi_name)"
    elif [ "$source_explicit" -eq 0 ] && [ -f "$PKG_LIB_DIR/$(host_efi_name)" ]; then
        printf 'ok  packaged EFI: %s\n' "$PKG_LIB_DIR/$(host_efi_name)"
    else
        printf 'warn built EFI missing: run visor build\n'
    fi

    if [ "$check_build" -eq 1 ] && [ -f "$source_dir/Makefile" ]; then
        if make --no-print-directory -C "$source_dir" >/dev/null; then
            printf 'ok  build\n'
        else
            printf 'fail build\n'
            ok=0
        fi
    fi

    for c in assets/icons/linux.png assets/icons/windows.png assets/icons/unknown.png assets/backgrounds/default.png; do
        if [ -f "$source_dir/$c" ]; then
            printf 'ok  %s\n' "$c"
        elif [ "$source_explicit" -eq 0 ] && [ -f "$PKG_DATA_DIR/${c#assets/}" ]; then
            printf 'ok  packaged %s\n' "${c#assets/}"
        else
            printf 'miss %s\n' "$c"
            ok=0
        fi
    done

    esp="$(resolve_esp "$esp" || true)"
    if [ -n "$esp" ]; then
        local root="$esp/EFI/visor"
        printf 'ESP: %s\n' "$esp"
        [ -d "$root" ] && printf 'ok  install dir\n' || { printf 'miss install dir: %s\n' "$root"; ok=0; }
        [ -f "$root/$(host_efi_name)" ] && printf 'ok  installed EFI\n' || { printf 'miss installed EFI\n'; ok=0; }
        if [ -f "$root/boot.conf" ]; then
            if validate_config_file "$root/boot.conf"; then
                printf 'ok  installed config\n'
            else
                printf 'fail installed config\n'
                ok=0
            fi
            [ -z "$config_file" ] && config_file="$root/boot.conf"
        elif [ -f "$source_dir/boot.conf.example" ] || [ -f "$PKG_DATA_DIR/boot.conf.example" ]; then
            printf 'warn installed config missing; example exists\n'
        else
            printf 'miss installed config\n'
            ok=0
        fi
        [ -f "$root/boot.log" ] && printf 'ok  boot log\n' || printf 'warn boot log missing; it appears after first boot\n'
        [ -d "$root/icons" ] && printf 'ok  icons dir\n' || printf 'warn icons dir missing\n'
        [ -d "$root/backgrounds" ] && printf 'ok  backgrounds dir\n' || printf 'warn backgrounds dir missing\n'
        if [ -d "$root/drivers" ]; then
            local driver_count
            driver_count="$(find "$root/drivers" -maxdepth 1 \( -iname '*.efi' -o -iname '*.EFI' \) 2>/dev/null | wc -l | tr -d ' ')"
            printf 'ok  drivers dir (%s EFI files)\n' "$driver_count"
        else
            printf 'ok  drivers dir absent\n'
        fi

        local venc_count=0
        local old_venc=0
        if command -v find >/dev/null 2>&1; then
            while IFS= read -r f; do
                [ -n "$f" ] || continue
                venc_count=$((venc_count + 1))
                if command -v od >/dev/null 2>&1; then
                    local hdr magic version
                    hdr="$(od -An -tx1 -N12 "$f" 2>/dev/null | tr -d ' \n')"
                    magic="${hdr:0:16}"
                    version="${hdr:16:8}"
                    if [ "$magic" = "5649534f52454e43" ] && [ "$version" = "02000000" ]; then
                        printf 'ok  encrypted artifact v2: %s\n' "$f"
                    else
                        printf 'warn encrypted artifact not VISORENC v2: %s\n' "$f"
                        old_venc=$((old_venc + 1))
                    fi
                fi
            done <<EOF
$(find "$root" -type f -name '*.venc' 2>/dev/null)
EOF
        fi
        [ "$venc_count" -eq 0 ] && printf 'ok  encrypted artifacts: none found\n'
        [ "$old_venc" -eq 0 ] || ok=0
    else
        printf 'warn ESP not detected; pass --esp PATH for install checks\n'
    fi

    [ -z "$config_file" ] && [ -f ./boot.conf ] && config_file=./boot.conf
    [ -z "$config_file" ] && [ -f "$source_dir/boot.conf.example" ] && config_file="$source_dir/boot.conf.example"
    [ -z "$config_file" ] && [ -f "$PKG_DATA_DIR/boot.conf.example" ] && config_file="$PKG_DATA_DIR/boot.conf.example"
    if [ -n "$config_file" ] && [ -f "$config_file" ]; then
        if validate_config_file "$config_file"; then
            printf 'ok  config valid: %s\n' "$config_file"
        else
            printf 'fail config invalid: %s\n' "$config_file"
            ok=0
        fi
        if grep -Eiq '^[[:space:]]*quiet[[:space:]]*=[[:space:]]*(1|true|yes)' "$config_file"; then
            printf 'ok  quiet boot enabled; recovery will show captured logs after failure\n'
        fi
        if grep -Eiq '^[[:space:]]*luks[[:space:]]*=[[:space:]]*(1|true|yes)' "$config_file"; then
            if ! grep -Eiq '^[[:space:]]*luks_(cmdline|preset)[[:space:]]*=' "$config_file"; then
                printf 'warn luks=1 without luks_cmdline or luks_preset may leave initramfs prompting\n'
            fi
            if ! grep -Eiq '^[[:space:]]*initrd[[:space:]]*=' "$config_file"; then
                printf 'warn luks=1 without initrd= only works for EFI-stub/UKI paths, not raw kernels\n'
            fi
        fi
    else
        printf 'warn config file unavailable\n'
    fi

    local sb_enabled=0
    if command -v mokutil >/dev/null 2>&1 && mokutil --sb-state 2>/dev/null | grep -qi enabled; then
        printf 'ok  secure boot enabled\n'
        sb_enabled=1
    elif command -v bootctl >/dev/null 2>&1; then
        if bootctl status 2>/dev/null | grep -qi 'Secure Boot:.*enabled'; then
            printf 'ok  secure boot enabled\n'
            sb_enabled=1
        elif bootctl status 2>/dev/null | grep -qi 'Secure Boot:'; then
            printf 'warn secure boot not enabled\n'
        fi
    elif [ -d /sys/firmware/efi/efivars ]; then
        printf 'warn secure boot state unavailable; bootctl missing\n'
    fi

    if [ "$sb_enabled" -eq 1 ] && [ -n "$esp" ] && [ -d "$esp/EFI/visor/drivers" ]; then
        printf 'warn secure boot enabled with filesystem drivers present; firmware or shim must trust them\n'
    fi

    if command -v efibootmgr >/dev/null 2>&1; then
        if valid_visor_entries >/dev/null; then
            printf 'ok  UEFI boot entry\n'
        elif corrupted_visor_entries >/dev/null; then
            printf 'warn UEFI boot entry corrupted (run `visor update --boot-entry` to fix)\n'
        else
            printf 'warn UEFI boot entry missing\n'
        fi
    else
        printf 'warn efibootmgr missing; boot entry not checked\n'
    fi

    [ "$ok" -eq 1 ] || return 1
}

git_default_branch() {
    local dir="$1"
    local ref
    ref="$(git -C "$dir" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)"
    ref="${ref#origin/}"
    [ -n "$ref" ] && printf '%s\n' "$ref"
}

ensure_source_checkout() {
    local repo="$1"
    local branch="$2"
    local dir="$3"

    need_cmd git

    if [ ! -d "$dir/.git" ]; then
        local legacy="${XDG_CACHE_HOME:-$(real_home)/.cache}/visor-src"
        if [ -d "$legacy/.git" ]; then
            say "Moving legacy source from $legacy to $dir"
            mkdir -p "$(dirname "$dir")"
            mv "$legacy" "$dir" || warn "could not move $legacy (continuing)"
        fi
    fi

    mkdir -p "$(dirname "$dir")"

    if [ -d "$dir/.git" ]; then
        if git -C "$dir" remote get-url origin >/dev/null 2>&1; then
            git -C "$dir" remote set-url origin "$repo"
        else
            git -C "$dir" remote add origin "$repo"
        fi
        say "Pulling latest Visor source in $dir"
        git -C "$dir" fetch --prune origin
        if [ -n "$branch" ]; then
            git -C "$dir" checkout "$branch" 2>/dev/null || git -C "$dir" checkout -B "$branch" "origin/$branch"
            git -C "$dir" pull --ff-only origin "$branch"
        else
            branch="$(git_default_branch "$dir")"
            if [ -n "$branch" ]; then
                git -C "$dir" checkout "$branch" 2>/dev/null || git -C "$dir" checkout -B "$branch" "origin/$branch"
                git -C "$dir" pull --ff-only origin "$branch"
            else
                git -C "$dir" pull --ff-only
            fi
        fi
    else
        say "Cloning Visor source into $dir"
        local clone_args=(clone)
        [ -n "$branch" ] && clone_args+=(--branch "$branch")
        git "${clone_args[@]}" "$repo" "$dir"
    fi
}

validate_config_file() {
    local file="$1"
    awk '
    function fail(msg) {
        print msg > "/dev/stderr"
        exit 1
    }
    function is_space(c) {
        return c == " " || c == "\t" || c == "\r" || c == "\n"
    }
    function strip_comment(s,    i,c,in_quote,first,eq,after_eq,prev) {
        first = 1
        while (first <= length(s) && is_space(substr(s, first, 1))) first++

        eq = index(s, "=")
        after_eq = 0
        if (eq > 0) {
            after_eq = eq + 1
            while (after_eq <= length(s) && is_space(substr(s, after_eq, 1))) after_eq++
        }

        in_quote = 0
        for (i = 1; i <= length(s); i++) {
            c = substr(s, i, 1)
            if (c == "\"") {
                in_quote = !in_quote
                continue
            }
            if (!in_quote && c == "#" && i != first && i != after_eq) {
                prev = (i > 1) ? substr(s, i - 1, 1) : ""
                if (is_space(prev)) return substr(s, 1, i - 1)
            }
        }
        return s
    }
    function trim(s) {
        sub(/^[ \t]+/, "", s)
        sub(/[ \t\r]+$/, "", s)
        return s
    }
    BEGIN { in_entry=0; have_name=0; have_kernel=0 }
    {
        sub(/\r$/, "")
        $0 = strip_comment($0)
        line = trim($0)
        if (line == "" || substr(line,1,1) == "#") next
        if (in_entry) {
            if (line == "}") {
                if (!have_name) fail(FILENAME ": entry missing name")
                if (!have_kernel) fail(FILENAME ": entry missing kernel")
                in_entry=0
                have_name=0
                have_kernel=0
                next
            }
            if (index(line, "=") > 1) {
                split(line, kv, "=")
                key = trim(kv[1])
                if (key == "name") have_name=1
                if (key == "kernel") have_kernel=1
                next
            }
            fail(FILENAME ": invalid entry line: " line)
        }
        if (line == "}" || line == "{") next
        if (line ~ /^(entry|linux|windows)([ \t]*\{[ \t]*)?$/) {
            in_entry=1
            have_name=0
            have_kernel=0
            next
        }
        if (index(line, "=") > 1) next
        fail(FILENAME ": invalid top-level line: " line)
    }
    END {
        if (in_entry) fail(FILENAME ": unterminated entry block")
    }' "$file"
}

cmd_config_validate() {
    local file=""
    local esp=""

    while [ $# -gt 0 ]; do
        case "$1" in
            --file) file="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown config option: $1" ;;
        esac
    done

    if [ -z "$file" ]; then
        if [ -n "$esp" ]; then
            esp="$(resolve_esp "$esp")"
            [ -n "$esp" ] || die "could not find the ESP"
            file="$esp/EFI/visor/boot.conf"
        elif [ -f ./boot.conf ]; then
            file=./boot.conf
        else
            esp="$(resolve_esp "$esp")"
            [ -n "$esp" ] || die "could not find the ESP"
            file="$esp/EFI/visor/boot.conf"
        fi
    fi

    [ -f "$file" ] || die "config file not found: $file"
    validate_config_file "$file"
    say "Config is valid: $file"
}

VISOR_VAR_GUID="4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"

visor_var_path() {
    printf '%s\n' "/sys/firmware/efi/efivars/VisorBuild-$VISOR_VAR_GUID"
}

manifest_blob() {
    local efi="${1:-}"
    [ -n "$efi" ] && [ -r "$efi" ] || return 0
    grep -ao 'VISORFT1[^C]*VISORFTEND' "$efi" 2>/dev/null | head -1 || true
}

manifest_fields() {
    local blob="${1:-}"
    [ -n "$blob" ] || return 0
    printf '%s\n' "$blob" | python3 -c '
import json, sys
b = sys.stdin.read()
m = json.loads(b[b.index("{"): b.rindex("}") + 1])
f = m.get("feat", []) or []
o = m.get("opts", {}) or {}
print("%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s" %
      (m.get("profile", "?"), m.get("ver", "?"), m.get("arch", "?"),
       m.get("schema", "?"), m.get("reg", "?"), len(f),
       ",".join(f), o.get("font_px", "-")))' 2>/dev/null || true
}

runtime_manifest() {
    local p="$(visor_var_path)"
    [ -r "$p" ] || return 0
    dd if="$p" bs=1 skip=4 2>/dev/null | tr -d '\000' || true
}

sidecar_path() {
    local binary="${1:-}" esp="${2:-}" cand
    if [ -n "$binary" ]; then
        cand="$(dirname "$binary")/build.json"
        [ -r "$cand" ] && { printf '%s\n' "$cand"; return 0; }
    fi
    if [ -n "$esp" ]; then
        cand="$(resolve_esp "$esp" || true)/EFI/visor/build.json"
        [ -r "$cand" ] && { printf '%s\n' "$cand"; return 0; }
    fi
    return 0
}

sidecar_fields() {
    local path="${1:-}"
    [ -n "$path" ] && [ -r "$path" ] || return 0
    python3 - "$path" <<'PY' 2>/dev/null || true
import json, sys
try:
    with open(sys.argv[1], encoding="utf-8") as fh:
        d = json.load(fh)
    f = d.get("features") or []
    print("%s\t%s\t%d\t%s" % (d.get("profile", "?"), d.get("arch", "?"),
                              len(f), ",".join(f)))
except Exception:
    pass
PY
}

find_visor_binary() {
    local esp="${1:-}" candidate=""
    if [ -n "$esp" ]; then
        esp="$(resolve_esp "$esp" || true)"
        for candidate in visor_x64.efi visor_aa64.efi; do
            if [ -f "$esp/EFI/visor/$candidate" ]; then
                printf '%s/EFI/visor/%s\n' "$esp" "$candidate"; return 0
            fi
        done
    fi
    if packaged_available; then
        printf '%s\n' "$PKG_LIB_DIR/$(host_efi_name)"; return 0
    fi
    candidate="$(host_efi_name)"
    if [ -f "$candidate" ]; then
        printf '%s\n' "$candidate"; return 0
    fi
    return 0
}

build_has_feature() {
    local efi="$1" feature="$2" blob fields
    blob="$(manifest_blob "$efi")"
    [ -n "$blob" ] || return 2
    fields="$(manifest_fields "$blob")"
    [ -n "$fields" ] || return 2
    local nfeats feats
    nfeats="$(printf '%s' "$fields" | cut -f6)"
    feats="$(printf '%s' "$fields" | cut -f7)"
    [ "$nfeats" -gt 0 ] || return 2
    case ",$feats," in
        *",$feature,"*) return 0 ;;
        *) return 1 ;;
    esac
}

require_feature() {
    local feature="$1" action="$2" esp="${3:-}" binary=""
    binary="$(find_visor_binary "$esp")"
    if [ -z "$binary" ] || ! [ -r "$binary" ]; then
        return 0
    fi
    case "$(build_has_feature "$binary" "$feature"; echo $?)" in
        0) return 0 ;;
        2) return 0 ;;
    esac
    die "refusing to $action: the installed Visor ($binary, $(printf '%s' "$binary" | sed 's/.*\///')) does not have the '$feature' feature. \
This profile was built without it; pick a profile that includes $feature, or use the external tool and a matching loader."
}

cmd_features() {
    local esp="" binary="" json=0
    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --binary) binary="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --json) json=1; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown features option: $1" ;;
        esac
    done

    if [ -z "$binary" ]; then
        binary="$(find_visor_binary "$esp")"
        [ -n "$esp" ] || esp=""
    else
        [ -e "$binary" ] || die "no such file: $binary"
        [ -r "$binary" ] || die "cannot read $binary (try sudo)"
    fi

    local blob="" fields="" profile="" ver="" arch="" schema="" reg=""
    local nfeats="" feats="" font_px=""

    if [ -n "$binary" ] && [ -r "$binary" ]; then
        blob="$(manifest_blob "$binary")"
        if [ -n "$blob" ]; then
            fields="$(manifest_fields "$blob")"
            if [ -z "$fields" ]; then
                warn "Embedded manifest present but python3 could not parse it - treating as legacy."
                blob=""
            else
                profile="$(printf '%s' "$fields" | cut -f1)"
                ver="$(printf '%s' "$fields" | cut -f2)"
                arch="$(printf '%s' "$fields" | cut -f3)"
                schema="$(printf '%s' "$fields" | cut -f4)"
                reg="$(printf '%s' "$fields" | cut -f5)"
                nfeats="$(printf '%s' "$fields" | cut -f6)"
                feats="$(printf '%s' "$fields" | cut -f7)"
                font_px="$(printf '%s' "$fields" | cut -f8)"
            fi
        fi
    else
        binary=""
    fi

    local runtime="unknown" rt_blob="" rt_fields="" rt_reg="" rt_ver=""
    local rt_profile="" rt_feats=""
    rt_blob="$(runtime_manifest)"
    if [ -n "$rt_blob" ]; then
        rt_fields="$(manifest_fields "$rt_blob")"
        rt_profile="$(printf '%s' "$rt_fields" | cut -f1)"
        rt_ver="$(printf '%s' "$rt_fields" | cut -f2)"
        rt_reg="$(printf '%s' "$rt_fields" | cut -f5)"
        rt_feats="$(printf '%s' "$rt_fields" | cut -f7)"
        if [ -z "$reg" ]; then
            runtime="unknown"
        elif [ "$reg" = "$rt_reg" ] && [ "$ver" = "$rt_ver" ] && \
             [ "$profile" = "$rt_profile" ] && [ "$feats" = "$rt_feats" ]; then
            runtime="match"
        else
            runtime="mismatch"
        fi
    elif [ -e "$(visor_var_path)" ]; then
        runtime="unreadable"
    fi

    local side="" side_profile="" side_arch="" side_feats="" side_n=""
    if [ -z "$blob" ]; then
        side="$(sidecar_path "$binary" "$esp")"
        if [ -n "$side" ]; then
            local sf
            sf="$(sidecar_fields "$side")"
            if [ -n "$sf" ]; then
                side_profile="$(printf '%s' "$sf" | cut -f1)"
                side_arch="$(printf '%s' "$sf" | cut -f2)"
                side_n="$(printf '%s' "$sf" | cut -f3)"
                side_feats="$(printf '%s' "$sf" | cut -f4)"
            fi
        fi
    fi

    if [ "$json" -eq 1 ]; then
        if [ -n "$binary" ] && [ -n "$blob" ]; then
            python3 - "$binary" "$runtime" "$fields" <<'PY'
import json, sys
bin_path, runtime, fields = sys.argv[1:4]
profile, ver, arch, schema, reg, n, feats, font_px = fields.split("\t")
m = {
    "profile": profile,
    "version": ver, "arch": arch, "schema": schema, "register": reg,
    "features": feats.split(",") if feats else [],
    "options": {"font_px": int(font_px) if font_px not in ("", "-") else None},
    "source": bin_path, "runtime": runtime, "from": "manifest", "legacy": False,
}
print(json.dumps(m, indent=2))
PY
        else
            if [ -n "$side_feats" ]; then
                python3 - "$side" "$runtime" "$side_profile" "$side_arch" "$side_feats" "${binary:-}" <<'PY'
import json, sys
side, runtime, profile, arch, feats, bin_path = sys.argv[1:7]
import os.path
same = bool(bin_path) and os.path.dirname(bin_path) == os.path.dirname(side)
print(json.dumps({
    "profile": profile, "arch": arch,
    "features": feats.split(",") if feats else [],
    "source": bin_path if same else None,
    "other_binary": None if same else (bin_path or None),
    "sidecar": side, "from": "sidecar",
    "legacy": False, "runtime": runtime,
}, indent=2))
PY
            elif [ -n "$binary" ]; then
                python3 - "$binary" "$runtime" <<'PY'
import json, sys
bin_path, runtime = sys.argv[1:3]
print(json.dumps({
    "profile": "legacy", "features": None, "legacy": True,
    "source": bin_path, "runtime": runtime, "from": "legacy",
}, indent=2))
PY
            else
                printf '{ "source": null, "runtime": "%s", "features": null, "profile": "unknown", "legacy": false, "from": "none" }\n' "$runtime"
            fi
        fi
        return 0
    fi

    if [ -z "$binary" ] && [ -z "$side_feats" ]; then
        say "No Visor binary found (pass --esp or --binary)"
        return 0
    fi
    if [ -z "$blob" ] && [ -n "$side_feats" ]; then
        if [ -n "$binary" ] && [ "$(dirname "$binary")" = "$(dirname "$side")" ]; then
            printf 'Binary:  %s\n' "$binary"
        fi
        printf 'Install: %s\n' "$(dirname "$side")"
        printf 'Profile: %s   Arch: %s\n' "$side_profile" "$side_arch"
        printf 'Features (%s):\n' "$side_n"
        IFS=',' read -ra _sl <<< "$side_feats"
        for f in "${_sl[@]}"; do printf '    %s\n' "$f"; done
        say "Read from $side - no readable binary manifest at that location."
        if [ -n "$binary" ] && [ "$(dirname "$binary")" != "$(dirname "$side")" ]; then
            say "A separate Visor binary exists at $binary; pass --binary to inspect it."
        fi
        return 0
    fi
    if [ -n "$binary" ]; then
        printf 'Binary:  %s\n' "$binary"
    fi
    if [ -z "$blob" ]; then
        warn "No embedded manifest - legacy build. Assuming every feature is present."
        return 0
    fi
    printf 'Profile: %s\n' "$profile"
    printf 'Version: %s   Arch: %s   Schema: %s\n' "$ver" "$arch" "$schema"
    printf 'Register id: %s   font_px: %s\n' "$reg" "$font_px"
    printf 'Features (%s):\n' "$nfeats"
    IFS=',' read -ra _fl <<< "$feats"
    for f in "${_fl[@]}"; do printf '    %s\n' "$f"; done
    case "$runtime" in
        match) ok "Runtime: last boot used this build" ;;
        mismatch)
            warn "Runtime: the machine last booted a different Visor than $binary"
            printf '    last boot: profile %s, v%s, %s features\n' \
                "$rt_profile" "$rt_ver" "$(printf '%s' "$rt_feats" | tr ',' '\n' | grep -c . || true)"
            printf '    this file: profile %s, v%s, %s features\n' "$profile" "$ver" "$nfeats"
            ;;
        unreadable) say "Runtime variable: present but unreadable (re-run with sudo)" ;;
        *) say "Runtime variable: not set this boot" ;;
    esac
}

cmd_status() {
    local source_dir=""
    local esp=""

    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --source-dir) source_dir="$(need_value "$1" "${2:-}")"; shift 2 ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown status option: $1" ;;
        esac
    done

    local found=0
    if [ -n "$source_dir" ]; then
        is_source_tree "$source_dir" && found=1
    else
        source_dir="$(project_dir)"
        is_source_tree "$source_dir" && found=1
    fi

    esp="$(resolve_esp "$esp" || true)"
    printf 'CLI path: %s\n' "$(script_dir)/$(basename "$0")"
    if [ "$found" -eq 1 ]; then
        printf 'Source dir: %s\n' "$source_dir"
    else
        printf 'Source dir: %s (not found)\n' "$source_dir"
        if packaged_available; then
            printf 'Packaged install: %s + %s\n' "$PKG_LIB_DIR" "$PKG_DATA_DIR"
        fi
    fi
    if [ -d "$source_dir/.git" ]; then
        printf 'Source commit: %s\n' "$(git -C "$source_dir" rev-parse --short HEAD 2>/dev/null || printf '?')"
        if [ -n "$(git -C "$source_dir" status --short 2>/dev/null || true)" ]; then
            printf 'Source dirty: yes\n'
        else
            printf 'Source dirty: no\n'
        fi
    else
        printf 'Source commit: unavailable\n'
    fi

    if [ -n "$esp" ]; then
        local root="$esp/EFI/visor"
        printf 'ESP: %s\n' "$esp"
        if [ ! -r "$esp" ] || [ ! -x "$esp" ]; then
            printf 'Installed binary: unreadable\n'
            printf 'Config: unreadable\n'
            printf 'Boot log: unreadable\n'
            printf 'UEFI entry: unknown (cannot read NVRAM)\n'
            warn "cannot read ESP $esp (permission denied) - re-run with sudo"
            return 1
        fi
        local installed_bin=""
        for candidate in "$(host_efi_name)" visor_x64.efi visor_aa64.efi; do
            if [ -f "$root/$candidate" ]; then installed_bin="$candidate"; break; fi
        done
        if [ -n "$installed_bin" ]; then
            printf 'Installed binary: %s\n' "$root/$installed_bin"
            if command -v sha256sum >/dev/null 2>&1; then
                local s
                s="$(sha256sum "$root/$installed_bin" 2>/dev/null | awk '{print $1}')"
                [ -n "$s" ] && printf 'Installed sha256: %s\n' "$s"
            fi
        else
            printf 'Installed binary: missing\n'
        fi
        if [ -n "$installed_bin" ]; then
            local mblob mfields mprofile mnfeats mfeats mver
            mblob="$(manifest_blob "$root/$installed_bin")"
            if [ -n "$mblob" ]; then
                mfields="$(manifest_fields "$mblob")"
                mprofile="$(printf '%s' "$mfields" | cut -f1)"
                mver="$(printf '%s' "$mfields" | cut -f2)"
                mnfeats="$(printf '%s' "$mfields" | cut -f6)"
                mfeats="$(printf '%s' "$mfields" | cut -f7)"
                printf 'Profile:   %s (v%s, %s features)\n' "$mprofile" "$mver" "$mnfeats"
                printf 'Features:  %s\n' "$mfeats"
            else
                printf 'Profile:   legacy build (no embedded manifest - all features assumed)\n'
            fi
        fi
        [ -f "$root/boot.conf" ] && printf 'Config: %s\n' "$root/boot.conf" || printf 'Config: missing\n'
        [ -f "$root/boot.log" ] && printf 'Boot log: %s\n' "$root/boot.log" || printf 'Boot log: missing\n'
        if command -v efibootmgr >/dev/null 2>&1; then
            if valid_visor_entries >/dev/null; then
                printf 'UEFI entry: present\n'
            elif corrupted_visor_entries >/dev/null; then
                printf 'UEFI entry: present (corrupted — run `visor update --boot-entry` to fix)\n'
            elif efibootmgr >/dev/null 2>&1; then
                printf 'UEFI entry: missing\n'
            else
                printf 'UEFI entry: unknown (cannot read NVRAM)\n'
            fi
        else
            printf 'UEFI entry: unknown (efibootmgr not installed)\n'
        fi
    else
        printf 'ESP: unavailable\n'
    fi
}

cmd_sign() {
    local esp=""
    local source_dir="${VISOR_SOURCE_DIR:-$(source_dir_default)}"
    local sign_drivers=1

    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --source-dir) source_dir="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --no-drivers) sign_drivers=0; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown sign option: $1" ;;
        esac
    done

    need_cmd sbctl

    local efi_name
    efi_name="$(host_efi_name)"

    esp="$(resolve_esp "$esp" || true)"
    local installed=""
    if [ -n "$esp" ]; then
        local candidate
        for candidate in "$efi_name" visor_x64.efi visor_aa64.efi; do
            if [ -f "$esp/EFI/visor/$candidate" ]; then
                installed="$esp/EFI/visor/$candidate"
                break
            fi
        done
    fi
    if [ -n "$installed" ]; then
        say "Signing $installed"
        run_root sbctl sign -s "$installed"
        if [ "$sign_drivers" -eq 1 ] && [ -d "$esp/EFI/visor/drivers" ]; then
            for f in "$esp"/EFI/visor/drivers/*.efi "$esp"/EFI/visor/drivers/*.EFI; do
                [ -e "$f" ] || continue
                say "Signing $f"
                run_root sbctl sign -s "$f"
            done
        fi
        return 0
    fi

    local built
    for built in "$source_dir/$efi_name" "$source_dir/visor_x64.efi" "$source_dir/visor_aa64.efi"; do
        if [ -f "$built" ]; then
            say "Signing $built"
            run_root sbctl sign -s "$built"
            return 0
        fi
    done

    die "could not find an EFI binary to sign"
}

boot_fs_type() {
    local t=""
    if mountpoint -q /boot 2>/dev/null; then
        t="$(findmnt -Uno FSTYPE /boot 2>/dev/null || true)"
    fi
    [ -z "$t" ] && t="$(findmnt -Uno FSTYPE / 2>/dev/null || true)"
    printf '%s\n' "$t"
}

EFIFS_VERSION="${EFIFS_VERSION:-v1.12}"

efifs_sha256_for() {
    case "$1" in
        btrfs_aa64.efi) echo 92dae5f1d0f6055afb6fd851a438e6173e7a452582f9ff13038ad4f2bf5088b9 ;;
        btrfs_x64.efi) echo 8ae24aa9f38f71a1e347fb6d0646b4678e04466aadab9919fb2ad133d5ee879c ;;
        exfat_aa64.efi) echo 629e567847ba028cb6ba1f75af12b1ace2094a6b1e70cddbfe1a99a82cdd0511 ;;
        exfat_x64.efi) echo 21a5969dcd7b6c149b1dc9408c591749ba9c62fb264e2852cc70061fe3defff6 ;;
        ext2_aa64.efi) echo a472ec2641475dfcc2dff290472186c9d2424ae005a1a3c46809aac2785d146b ;;
        ext2_x64.efi) echo e009f02f25b9c5ad3beaf0d3a04f89042985eea1d90187b888130a708c35ca61 ;;
        f2fs_aa64.efi) echo df07c2bc9f485e8b01e707552852a6ee129e74e7085bd5547b29734ae4848beb ;;
        f2fs_x64.efi) echo 74490317fbbb4c3f37072c1c1ab93557d1c8834c533690970c41b4310b4527cb ;;
        hfsplus_aa64.efi) echo fa23cc880464ec5daeb669b7a3a373e524136e87459a5585c2ba23e59ddfe1fb ;;
        hfsplus_x64.efi) echo 894d5b2985808d92ae8a5476fd942d39075f4730ac86d9c182e421208af5fbaf ;;
        jfs_aa64.efi) echo 8f8d8388f34342eca7cf566f2b5bdcadd44bc6862d0ffb9cde3d3535e8d58a51 ;;
        jfs_x64.efi) echo 716d6328ba85d29faa7de377dc61e5a154f3455b3680037b06ec2b025e8eba82 ;;
        nilfs2_aa64.efi) echo 33a74897a89828fb5ad9f311b0942716438fadd2ba19cf642b692bcec30d970b ;;
        nilfs2_x64.efi) echo 2c43afe61c5d1fa309cadf79b39a789eca7424b9ed0363efbb246664d6b51a4e ;;
        ntfs_aa64.efi) echo 5eb1827942bdc8006a714d719b9c80268bb57095d7e484e9529f47781d68c672 ;;
        ntfs_x64.efi) echo 59c37d5026ca14553a158939e3f2cf20286b6135a713a62c08b569ac9caedcb7 ;;
        reiserfs_aa64.efi) echo 5cb5300186487fbb497497889674c585a6e93c9a189a3fcdaf9ec411e3c85439 ;;
        reiserfs_x64.efi) echo d14fd72d34cd04163cd810d465394c6664f30b8c4b45e96fa8b1b974b2933ac0 ;;
        ufs2_aa64.efi) echo aba8cc7949b77986071c37a9e49502b884a09700d4006913fe39c4840aefc0e1 ;;
        ufs2_x64.efi) echo 5f916e9263fc32bccd4f1e82b62d4fb72bc4b99b19010b4d4962a87d30854157 ;;
        xfs_aa64.efi) echo 82af528c35f464f106af40c39336e18ec6a8972bab16054d7e4b4959d11d80f7 ;;
        xfs_x64.efi) echo f75d595d8037d0f5612b4eaec2d6f4a581353530a792d904c2c6988d358e07f6 ;;
        zfs_aa64.efi) echo 8c710d400bb4d57129cc96363d21e42ea2ab1835ca52182840ef3ebcdf6c0b53 ;;
        zfs_x64.efi) echo e61dae69979979977f2ffa30283b86526e54fed8ad240ee4c0529690b1e5342d ;;
        *) echo "" ;;
    esac
}

efifs_verify_sha256() {
    local want got
    want="$(efifs_sha256_for "$2")"
    if [ -z "$want" ]; then
        warn "no pinned SHA-256 for $2 - skipping checksum verification"
        return 0
    fi
    if command -v sha256sum >/dev/null 2>&1; then
        got="$(sha256sum "$1" | cut -d" " -f1)"
    elif command -v shasum >/dev/null 2>&1; then
        got="$(shasum -a 256 "$1" | cut -d" " -f1)"
    else
        warn "sha256sum not available - skipping checksum verification"
        return 0
    fi
    [ "$got" = "$want" ]
}

efifs_name_for() {
    case "$1" in
        ext2|ext3|ext4) echo ext2 ;;
        btrfs)          echo btrfs ;;
        xfs)            echo xfs ;;
        f2fs)           echo f2fs ;;
        zfs)            echo zfs ;;
        ntfs|ntfs3)     echo ntfs ;;
        reiserfs)       echo reiserfs ;;
        nilfs2)         echo nilfs2 ;;
        jfs)            echo jfs ;;
        exfat)          echo exfat ;;
        hfsplus)        echo hfsplus ;;
        ufs2)           echo ufs2 ;;
        *)              echo "" ;;
    esac
}

cmd_drivers() {
    local esp=""
    local fstype=""
    local sign=0
    local efifs_url="${EFIFS_URL:-https://github.com/pbatard/efifs/releases/download/$EFIFS_VERSION}"

    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --fs) fstype="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --sign) sign=1; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown drivers option: $1" ;;
        esac
    done

    esp="$(resolve_esp "$esp")"
    [ -n "$esp" ] || die "could not find the ESP (pass --esp PATH)"
    [ -d "$esp/EFI/visor" ] || die "Visor is not installed at $esp/EFI/visor - run 'visor install' first"

    if [ -z "$fstype" ]; then
        fstype="$(boot_fs_type)"
        [ -n "$fstype" ] || die "could not detect the /boot filesystem (pass --fs TYPE)"
        say "Detected kernel filesystem: $fstype"
    fi
    case "$fstype" in
        vfat|msdos)
            say "FAT is readable by the firmware already - no driver needed."
            return 0 ;;
    esac

    local name arch driver tmp
    name="$(efifs_name_for "$fstype")"
    [ -n "$name" ] || die "no EfiFs driver known for filesystem '$fstype'"
    case "$(uname -m)" in
        aarch64) arch=aa64 ;;
        *)       arch=x64 ;;
    esac
    driver="${name}_${arch}.efi"

    tmp="$(mktemp)"
    say "Downloading EfiFs driver $driver ..."
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL --connect-timeout 15 -o "$tmp" "$efifs_url/$driver" || { rm -f "$tmp"; die "download failed: $efifs_url/$driver"; }
    elif command -v wget >/dev/null 2>&1; then
        wget -q -T 15 -O "$tmp" "$efifs_url/$driver" || { rm -f "$tmp"; die "download failed: $efifs_url/$driver"; }
    else
        rm -f "$tmp"
        die "curl or wget is required to download the driver"
    fi
    [ "$(head -c2 "$tmp" 2>/dev/null)" = "MZ" ] || { rm -f "$tmp"; die "downloaded file is not an EFI binary"; }
    if [ -z "${EFIFS_URL:-}" ] && [ "$EFIFS_VERSION" = "v1.12" ]; then
        efifs_verify_sha256 "$tmp" "$driver" || { rm -f "$tmp"; die "SHA-256 mismatch for $driver - refusing to install"; }
        say "Driver checksum verified (EfiFs $EFIFS_VERSION)"
    else
        warn "EFIFS_URL/EFIFS_VERSION overridden - checksum table does not apply"
    fi

    run_root mkdir -p "$esp/EFI/visor/drivers"
    run_root install -m 0644 "$tmp" "$esp/EFI/visor/drivers/$driver"
    rm -f "$tmp"
    say "Installed filesystem driver: $esp/EFI/visor/drivers/$driver"

    if [ "$sign" -eq 1 ]; then
        need_cmd sbctl
        say "Signing $esp/EFI/visor/drivers/$driver"
        run_root sbctl sign -s "$esp/EFI/visor/drivers/$driver"
    elif command -v mokutil >/dev/null 2>&1 && mokutil --sb-state 2>/dev/null | grep -qi enabled; then
        warn "Secure Boot is enabled - sign the driver ('visor drivers --sign' or 'visor sign') or it will not load."
    fi
    say "Visor loads drivers from \\EFI\\visor\\drivers automatically at boot."
}

remove_boot_entry() {
    if ! command -v efibootmgr >/dev/null 2>&1; then
        warn "efibootmgr not installed; skipping boot entry removal."
        return 0
    fi

    local line num
    while IFS= read -r line; do
        [ -n "$line" ] || continue
        num="$(printf '%s\n' "$line" | sed -n 's/^Boot\([0-9A-Fa-f]\{4,\}\).*/\1/p')"
        [ -n "$num" ] || continue
        say "Removing UEFI boot entry Boot${num}"
        run_root efibootmgr -b "$num" -B
    done < <(efibootmgr -v 2>/dev/null | \
        awk 'BEGIN{IGNORECASE=1} /^Boot[0-9A-Fa-f]{4,}[^0-9A-Fa-f]/ && /Visor/ {print}')
}

cmd_uninstall() {
    local esp=""
    local cli_dir="${CLI_DIR:-/usr/local/bin}"
    local keep_entry=0

    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --cli-dir) cli_dir="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --keep-entry) keep_entry=1; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown uninstall option: $1" ;;
        esac
    done

    esp="$(resolve_esp "$esp" || true)"
    if [ -n "$esp" ] && [ -d "$esp/EFI/visor" ]; then
        say "Removing $esp/EFI/visor"
        run_root rm -rf "$esp/EFI/visor"
    fi
    if [ -n "$cli_dir" ] && [ -f "$cli_dir/visor" ]; then
        say "Removing $cli_dir/visor"
        run_root rm -f "$cli_dir/visor"
    fi
    if [ "$keep_entry" -eq 0 ]; then
        remove_boot_entry
    fi
}

ensure_source_for_update() {
    local repo="$1"
    local branch="$2"
    local dir="$3"
    ensure_source_checkout "$repo" "$branch" "$dir"
}

cmd_update() {
    local repo="${VISOR_REPO:-$DEFAULT_REPO}"
    local branch="${VISOR_BRANCH:-}"
    local source_dir="${VISOR_SOURCE_DIR:-$(source_dir_default)}"
    local esp=""
    local profile="" features=""
    local boot_entry=0
    local sign=0
    local force_config=0

    while [ $# -gt 0 ]; do
        case "$1" in
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --profile) profile="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --features) features="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --repo) repo="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --branch) branch="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --source-dir) source_dir="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --boot-entry) boot_entry=1; shift ;;
            --sign) sign=1; shift ;;
            --force-config) force_config=1; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown update option: $1" ;;
        esac
    done

    [ -n "$repo" ] || die "repo URL is empty"
    [ -n "$source_dir" ] || die "source directory is empty"

    ensure_source_for_update "$repo" "$branch" "$source_dir"

    local install_args=(--no-boot-entry --no-sign --no-fs-drivers)
    [ -n "$esp" ] && install_args+=(--esp "$esp")
    [ -n "$profile" ] && install_args+=(--profile "$profile")
    [ -n "$features" ] && install_args+=(--features "$features")
    [ "$boot_entry" -eq 1 ] && install_args+=(--boot-entry)
    [ "$sign" -eq 1 ] && install_args+=(--sign)
    [ "$force_config" -eq 1 ] && install_args+=(--force-config)

    say "Building and installing the updated Visor"
    run_install "$source_dir/install.sh" "${install_args[@]}"
    say "Visor is up to date"
}

cmd_encrypt() {
    local input="" initrd_input="" out="" esp="" name="" title="" iterations=""
    local kind="kernel" no_config=0 no_luks=0
    while [ $# -gt 0 ]; do
        case "$1" in
            --out) out="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --name) name="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --title) title="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --iterations) iterations="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --initrd) kind="initrd"; shift ;;
            --no-config) no_config=1; shift ;;
            --no-luks) no_luks=1; shift ;;
            -h|--help) usage; exit 0 ;;
            --*) die "unknown encrypt option: $1" ;;
            *)
                if [ -z "$input" ]; then input="$1"
                elif [ -z "$initrd_input" ]; then initrd_input="$1"
                else die "unexpected argument: $1"; fi
                shift ;;
        esac
    done

    [ -n "$input" ] || die "usage: visor encrypt <kernel> [initrd] [--esp PATH | --out PATH] [--title TEXT] [--iterations N]"
    [ -f "$input" ] || die "input file not found: $input"
    [ -z "$initrd_input" ] || [ -f "$initrd_input" ] || die "input file not found: $initrd_input"
    [ -z "$initrd_input" ] || [ "$kind" = "kernel" ] || die "--initrd only applies to a single input"
    [ -z "$initrd_input" ] || [ -z "$out" ] || die "--out only works with a single input"
    need_cmd python3

    local tool
    tool="$(find_encrypt_tool)" || die "visor_encrypt.py not found (run from the Visor source tree, or 'visor update' first)"

    [ -n "$name" ] || name="$(basename "$input").enc"
    case "$name" in */*|'') die "--name must be a bare filename" ;; esac
    local initrd_name=""
    [ -z "$initrd_input" ] || initrd_name="$(basename "$initrd_input").enc"

    require_feature crypto "encrypt a kernel/initrd for this loader" "$esp"

    local install_to_esp=0 target initrd_target=""
    if [ -n "$out" ]; then
        target="$out"
    else
        esp="$(resolve_esp "$esp")"
        [ -n "$esp" ] || die "could not find the ESP (pass --esp PATH or --out PATH)"
        [ -d "$esp/EFI/visor" ] || die "Visor is not installed at $esp/EFI/visor - run 'visor install' first"
        target="$(mktemp)"
        [ -z "$initrd_input" ] || initrd_target="$(mktemp)"
        install_to_esp=1
    fi

    local pyargs=("$tool")
    [ -n "$iterations" ] && pyargs+=(--iterations "$iterations")
    pyargs+=("$input" "$target")
    [ -z "$initrd_input" ] || pyargs+=("$initrd_input" "$initrd_target")

    if [ -n "$initrd_input" ]; then
        say "Encrypting kernel and initrd (one password covers both) ..."
    else
        say "Encrypting $input (you will be prompted for a password) ..."
    fi
    if ! python3 "${pyargs[@]}"; then
        if [ "$install_to_esp" -eq 1 ]; then
            rm -f "$target"
            [ -z "$initrd_target" ] || rm -f "$initrd_target"
        fi
        die "encryption failed"
    fi

    local kpath ipath=""
    if [ "$install_to_esp" -eq 1 ]; then
        run_root install -m 0644 "$target" "$esp/EFI/visor/$name"
        rm -f "$target"
        say "Installed: $esp/EFI/visor/$name"
        kpath="\\EFI\\visor\\$name"
        if [ -n "$initrd_input" ]; then
            run_root install -m 0644 "$initrd_target" "$esp/EFI/visor/$initrd_name"
            rm -f "$initrd_target"
            say "Installed: $esp/EFI/visor/$initrd_name"
            ipath="\\EFI\\visor\\$initrd_name"
        fi
    else
        say "Wrote encrypted artifact: $target"
        kpath="\\path\\to\\$(basename "$target")"
    fi

    [ -n "$title" ] || title="Encrypted Linux"
    local cmdline="" w
    if [ -r /proc/cmdline ]; then
        for w in $(cat /proc/cmdline); do
            case "$w" in BOOT_IMAGE=*|initrd=*) ;; *) cmdline="$cmdline $w" ;; esac
        done
        cmdline="${cmdline# }"
    fi
    [ -n "$cmdline" ] || cmdline="root=... rw"

    local luks_lines="" luks_root=0 luks_dev=""
    if [ "$no_luks" -eq 0 ]; then
        case " $cmdline " in
            *" cryptdevice="*|*" rd.luks.name="*|*" rd.luks.uuid="*) luks_root=1 ;;
        esac
    fi
    if [ "$luks_root" -eq 1 ]; then
        local preset=""
        if command -v mkinitcpio >/dev/null 2>&1; then preset="mkinitcpio"
        elif command -v dracut >/dev/null 2>&1; then preset="dracut"
        elif [ -d /usr/lib/dracut ]; then preset="dracut"
        fi
        if [ -n "$preset" ]; then
            luks_lines="$(printf '    luks        = 1\n    luks_preset = %s' "$preset")"
        else
            luks_lines="$(printf '    luks        = 1\n    # luks_preset = mkinitcpio | dracut (set to match your initramfs)')"
        fi
        for w in $cmdline; do
            case "$w" in
                cryptdevice=*) luks_dev="${w#cryptdevice=}"; luks_dev="${luks_dev%%:*}" ;;
                rd.luks.uuid=*) luks_dev="UUID=${w#rd.luks.uuid=}" ;;
                rd.luks.name=*) [ -n "$luks_dev" ] || { luks_dev="${w#rd.luks.name=}"; luks_dev="UUID=${luks_dev%%=*}"; } ;;
            esac
        done
    fi

    local entry_text
    if [ -n "$initrd_input" ]; then
        entry_text="$(cat <<EOF
entry {
    name      = "$title"
    kernel    = $kpath
    initrd    = $ipath
    encrypted = 1
${luks_lines:+$luks_lines
}    cmdline   = "$cmdline"
}
EOF
)"
    elif [ "$kind" = "initrd" ]; then
        entry_text="$(cat <<EOF
entry {
    name    = "$title"
    kernel  = \\path\\to\\vmlinuz
    initrd  = $kpath
    initrd_encrypted = 1
${luks_lines:+$luks_lines
}    cmdline = "$cmdline"
}
EOF
)"
    else
        entry_text="$(cat <<EOF
entry {
    name    = "$title"
    kernel  = $kpath
    kernel_encrypted = 1
    cmdline = "$cmdline"
}
EOF
)"
    fi

    printf '\n%s\n\n' "$entry_text"

    local conf="$esp/EFI/visor/boot.conf" appended=0
    if [ "$install_to_esp" -eq 1 ] && [ "$no_config" -eq 0 ] && [ -f "$conf" ] && [ -t 0 ]; then
        printf 'Append this entry to boot.conf now? [y/N] '
        local ans=""
        read -r ans || ans=""
        case "$ans" in
            y|Y|yes|YES)
                printf '\n%s\n' "$entry_text" | run_root tee -a "$conf" >/dev/null
                say "Entry appended to $conf"
                appended=1
                ;;
        esac
    fi
    if [ "$appended" -eq 0 ]; then
        say "Add the entry above to \\EFI\\visor\\boot.conf (shown with your current cmdline)."
    fi
    say "At boot, Visor asks for the password and decrypts in memory."
    if [ "$luks_root" -eq 1 ]; then
        say "LUKS root detected: the entry unlocks it with the same password."
        if [ -n "$luks_dev" ]; then
            case "$luks_dev" in
                UUID=*) say "Enroll that password as a LUKS key first: sudo cryptsetup luksAddKey /dev/disk/by-uuid/${luks_dev#UUID=}" ;;
                *)      say "Enroll that password as a LUKS key first: sudo cryptsetup luksAddKey $luks_dev" ;;
            esac
        else
            say "Enroll that password as a LUKS key first: sudo cryptsetup luksAddKey <your-luks-device>"
        fi
    fi
}

cmd_config() {
    local sub="${1:-help}"
    case "$sub" in
        validate) shift; cmd_config_validate "$@" ;;
        help|-h|--help|"") usage ;;
        *) die "unknown config command: $sub" ;;
    esac
}

STUDIO_REPO="https://github.com/Versedcamel153/visor-studio.git"

studio_dir() {
    printf '%s\n' "${VISOR_STUDIO_DIR:-${XDG_CACHE_HOME:-$(real_home)/.cache}/visor-studio}"
}

cmd_studio() {
    local port=8000
    local esp=""
    local force_docker=0
    local force_native=0
    local detach=0
    local update=0
    while [ $# -gt 0 ]; do
        case "$1" in
            --update) update=1; shift ;;
            --port) port="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --esp) esp="$(need_value "$1" "${2:-}")"; shift 2 ;;
            --docker) force_docker=1; shift ;;
            --native) force_native=1; shift ;;
            --detach) detach=1; shift ;;
            -h|--help) usage; exit 0 ;;
            *) die "unknown studio option: $1" ;;
        esac
    done
    [ "$force_docker" -eq 1 ] && [ "$force_native" -eq 1 ] && die "--docker and --native are mutually exclusive"

    need_cmd git
    command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 || die "curl or wget is required"

    local dir
    dir="$(studio_dir)"
    if [ ! -d "$dir/.git" ]; then
        say "Cloning Visor Studio into $dir"
        mkdir -p "$(dirname "$dir")"
        git clone --depth 1 "$STUDIO_REPO" "$dir"
    elif [ "$update" -eq 1 ]; then
        say "Updating Visor Studio in $dir"
        git -C "$dir" pull --ff-only
    fi

    local visor_dir_host="${VISOR_DIR:-}"
    local visor_dir_explicit=0
    local boot_dir_host="${VISOR_BOOT_DIR:-/boot}"
    [ -n "$visor_dir_host" ] && visor_dir_explicit=1
    if [ -n "$visor_dir_host" ]; then
        [ -d "$visor_dir_host" ] || die "VISOR_DIR is not a directory: $visor_dir_host"
        [ -r "$visor_dir_host" ] || die "VISOR_DIR is not readable: $visor_dir_host"
    else
        local esp_path
        esp_path="$(resolve_esp "$esp" || true)"
        if [ -n "$esp_path" ] && [ -d "$esp_path/EFI/visor" ]; then
            visor_dir_host="$esp_path/EFI/visor"
        fi
    fi
    if [ -n "$visor_dir_host" ] && [ ! -f "$visor_dir_host/boot.conf" ]; then
        if [ "$visor_dir_explicit" -eq 1 ]; then
            die "VISOR_DIR does not contain boot.conf: $visor_dir_host"
        fi
        warn "Found $visor_dir_host, but boot.conf is missing; Studio will use hosted/export mode."
        visor_dir_host=""
    fi
    if [ -n "$boot_dir_host" ] && [ ! -d "$boot_dir_host" ]; then
        warn "VISOR_BOOT_DIR is not a directory: $boot_dir_host"
        boot_dir_host=""
    fi
    if [ -n "$visor_dir_host" ]; then
        say "Local Visor install: $visor_dir_host"
    else
        warn "Could not find an installed Visor directory; Studio will use hosted/export mode."
    fi

    if [ "$force_native" -eq 0 ] && { [ "$force_docker" -eq 1 ] || command -v docker >/dev/null 2>&1; }; then
        if command -v docker >/dev/null 2>&1; then
            local docker_args=(-p "$port:8000")
            if [ -n "$visor_dir_host" ]; then
                docker_args+=(-e VISOR_DIR=/mnt/visor -v "$visor_dir_host:/mnt/visor")
            fi
            if [ -n "$boot_dir_host" ] && [ -d "$boot_dir_host" ]; then
                docker_args+=(-e VISOR_BOOT_DIR=/mnt/boot -v "$boot_dir_host:/mnt/boot:ro")
            fi
            say "Building Visor Studio image (docker)"
            docker build -t visor-studio "$dir" || die "docker build failed"
            if [ "$detach" -eq 1 ]; then
                docker rm -f visor-studio >/dev/null 2>&1 || true
                docker run -d --name visor-studio "${docker_args[@]}" visor-studio >/dev/null
                say "Visor Studio running at http://127.0.0.1:$port"
                say "Stop it with: docker rm -f visor-studio"
                return 0
            fi
            say "Starting Visor Studio at http://127.0.0.1:$port (Ctrl-C to stop)"
            exec docker run --rm "${docker_args[@]}" visor-studio
        elif [ "$force_docker" -eq 1 ]; then
            die "docker is not installed (install docker or drop --docker)"
        fi
    fi

    if ! command -v python3 >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
        die "need python3 and npm to run Studio locally - install them, or install docker and re-run"
    fi

    say "Preparing Python environment"
    python3 -m venv "$dir/.venv" 2>/dev/null || true
    # shellcheck disable=SC1091
    . "$dir/.venv/bin/activate"
    pip install -q -e "$dir[test]" || { deactivate 2>/dev/null || true; die "failed to install Python deps for Studio"; }

    say "Installing and starting frontend"
    (cd "$dir/frontend" && npm install --silent) || die "npm install failed in $dir/frontend"

    local studio_env=()
    [ -n "$visor_dir_host" ] && studio_env+=(VISOR_DIR="$visor_dir_host")
    [ -n "$boot_dir_host" ] && [ -d "$boot_dir_host" ] && studio_env+=(VISOR_BOOT_DIR="$boot_dir_host")

    if [ "$detach" -eq 1 ]; then
        (cd "$dir/frontend" && nohup npm run dev >/tmp/visor-studio-frontend.log 2>&1 &) || true
        (cd "$dir" && nohup env "${studio_env[@]}" uvicorn main:app --port "$port" >/tmp/visor-studio.log 2>&1 &) || true
        say "Backend:  http://127.0.0.1:$port"
        say "Frontend: http://127.0.0.1:5173"
        say "Logs: /tmp/visor-studio.log, /tmp/visor-studio-frontend.log"
        return 0
    fi

    (cd "$dir/frontend" && npm run dev >/tmp/visor-studio-frontend.log 2>&1 &) || true
    say "Starting Visor Studio backend at http://127.0.0.1:$port"
    say "Open http://127.0.0.1:5173 in your browser (Ctrl-C to stop)"
    (cd "$dir" && exec env "${studio_env[@]}" uvicorn main:app --port "$port")
}

main() {
    local cmd="${1:-help}"
    case "$cmd" in
        build) shift; cmd_build "$@" ;;
        install) shift; cmd_install "$@" ;;
        update) shift; cmd_update "$@" ;;
        sign) shift; cmd_sign "$@" ;;
        drivers) shift; cmd_drivers "$@" ;;
        encrypt) shift; cmd_encrypt "$@" ;;
        convert) shift; cmd_convert "$@" ;;
        features) shift; cmd_features "$@" ;;
        uninstall) shift; cmd_uninstall "$@" ;;
        status) shift; cmd_status "$@" ;;
        config) shift; cmd_config "$@" ;;
        clean) shift; cmd_clean "$@" ;;
        studio) shift; cmd_studio "$@" ;;
        doctor) shift; cmd_doctor "$@" ;;
        help|-h|--help|"") usage ;;
        *) die "unknown command: $cmd" ;;
    esac
}

main "$@"
