#!/bin/sh
# ============================================================
# init -- PID 1 for the hiperiso hypervisor host initramfs
#
# Boots a selected ISO as a KVM guest with full bootlogging.
# Kernel cmdline parameters (see INTERFACES.sh):
#   hiperiso_iso="/ISOs/ubuntu.iso"
#   hiperiso_log="/hiperiso/logs/ubuntu/"
#   hiperiso_trace_level="standard|detailed|full|none"
#   hiperiso_ram=2048  hiperiso_cpus=2
#   hiperiso_display="none|gtk|vnc"
#   hiperiso_vga="std|none|virtio"
#   hiperiso_fallback="1"
# ============================================================

PATH="/usr/bin:/bin:/usr/sbin:/sbin"
export PATH

# ── Step 1: Mount essential virtual filesystems ──────────────
mkdir -p /proc /sys /dev /tmp /run

mount -t proc     proc     /proc  2>/dev/null
mount -t sysfs    sysfs    /sys   2>/dev/null
mount -t devtmpfs devtmpfs /dev   2>/dev/null || mount -t tmpfs tmpfs /dev 2>/dev/null
mount -t tmpfs    tmpfs    /tmp   2>/dev/null
mount -t tmpfs    tmpfs    /run   2>/dev/null

. /hiperiso-lib.sh

_INIT_UPTIME=$(cut -d' ' -f1 /proc/uptime 2>/dev/null)

printf '\n'
printf '======================================================\n'
printf '  hiperiso hypervisor host -- KVM ISO boot logger     \n'
printf '======================================================\n'
printf '\n'

# ── Step 2: Parse kernel command line ────────────────────────
hiperiso_parse_cmdline

if [ -z "$HIPERISO_ISO_PATH" ]; then
    printf '[hiperiso] FATAL: hiperiso_iso= not set on kernel command line\n'
    printf '[hiperiso] Dropping to recovery shell\n'
    exec /bin/sh
fi

hiperiso_log "ISO path: $HIPERISO_ISO_PATH"
hiperiso_log "Log dir:  $HIPERISO_LOG_DIR"
hiperiso_log "Trace:    ${HIPERISO_TRACE_LEVEL:-standard}"
hiperiso_log "RAM:      ${HIPERISO_GUEST_RAM:-2048} MB"
hiperiso_log "CPUs:     ${HIPERISO_GUEST_CPUS:-2}"

# ── Step 3: Find and mount the USB data partition ────────────
DATA_PART=$(hiperiso_find_usb_partition)
if [ -z "$DATA_PART" ]; then
    hiperiso_log "FATAL: Could not find hiperiso USB data partition"
    hiperiso_log "Scanned: blkid label, /dev/sd[abcd]2, sysfs"
    exec /bin/sh
fi
hiperiso_log "Data partition: $DATA_PART"

mkdir -p "$DATA_MOUNT"
_mounted=0
for _fstype in exfat ntfs3 ntfs vfat ext4 ext3 ext2; do
    if mount -t "$_fstype" "$DATA_PART" "$DATA_MOUNT" 2>/dev/null; then
        hiperiso_log "Mounted $DATA_PART at $DATA_MOUNT (type: $_fstype)"
        _mounted=1
        break
    fi
done

if [ "$_mounted" -eq 0 ]; then
    if mount "$DATA_PART" "$DATA_MOUNT" 2>/dev/null; then
        hiperiso_log "Mounted $DATA_PART at $DATA_MOUNT (auto-detected)"
        _mounted=1
    fi
fi

if [ "$_mounted" -eq 0 ]; then
    hiperiso_log "FATAL: Failed to mount $DATA_PART"
    exec /bin/sh
fi

# ── Step 4: Mount the EFI System Partition (for OVMF) ────────
ESP_PART=$(printf '%s' "$DATA_PART" | sed 's/1$/2/')

if [ ! -e "$ESP_PART" ]; then
    for _d in /dev/sda2 /dev/sdb2 /dev/sdc2 /dev/sdd2 /dev/sde2 /dev/sdf2 /dev/nvme0n1p2 /dev/nvme1n1p2 /dev/mmcblk0p2; do
        if [ -e "$_d" ]; then
            ESP_PART="$_d"
            break
        fi
    done
fi

mkdir -p "$EFI_MOUNT"
if mount -t vfat "$ESP_PART" "$EFI_MOUNT" 2>/dev/null; then
    hiperiso_log "Mounted ESP $ESP_PART at $EFI_MOUNT"
else
    hiperiso_log "WARNING: Could not mount ESP $ESP_PART -- OVMF may be unavailable"
fi

# ── Step 5: Resolve absolute paths and create log dir ────────
# Derive a unique per-session directory with UTC timestamp so
# every boot gets its own log set (no overwriting).
HIPERISO_LOG_DIR=$(hiperiso_derive_session_dir "$HIPERISO_LOG_DIR")
export HIPERISO_LOG_DIR

LOG_DIR="${DATA_MOUNT}${HIPERISO_LOG_DIR}"
ISO_PATH="${DATA_MOUNT}${HIPERISO_ISO_PATH}"
OVMF_FULL_PATH="${EFI_MOUNT}${OVMF_PATH}"

# Collision-safe: if session dir already exists (e.g. same-second boot),
# append an incrementing suffix so logs are never overwritten.
if [ -d "$LOG_DIR" ]; then
    _i=2
    while [ -d "${LOG_DIR%/}_${_i}" ]; do
        _i=$((_i + 1))
    done
    LOG_DIR="${LOG_DIR%/}_${_i}/"
    HIPERISO_LOG_DIR="${HIPERISO_LOG_DIR%/}_${_i}/"
fi
mkdir -p "$LOG_DIR"

export LOG_DIR ISO_PATH OVMF_FULL_PATH EFI_MOUNT DATA_MOUNT

# ── Record early timing marks retroactively ──────────────────
# These phases elapsed before LOG_DIR existed; capture them now
# using the uptime values saved at each checkpoint.
printf 'kernel_start\t%s\t%s\n' "${_INIT_UPTIME:-0}" "$(hiperiso_utc_iso)" >> "${LOG_DIR}/timing.dat"
hiperiso_timing_mark "session_created"

hiperiso_log "Session dir: $HIPERISO_LOG_DIR"

# ── Step 6: Write session metadata and environment snapshot ──
hiperiso_write_session_meta "started"
hiperiso_write_status "running" "mounted"
hiperiso_write_env
hiperiso_timing_mark "env_written"

# ── Step 7: KVM detection ────────────────────────────────────
KVM_RESULT=$(/kvm_check.sh)
printf 'KVM_CHECK_RESULT=%s\n' "$KVM_RESULT" >> "${LOG_DIR}/env.txt"

case "$KVM_RESULT" in
    KVM_OK)
        hiperiso_log "KVM available -- proceeding with hypervisor boot"
        export KVM_AVAILABLE=1
        export KVM_STATUS="available"
        hiperiso_write_status "running" "kvm_ok"
        ;;
    KVM_FAIL:*)
        hiperiso_log "KVM check failed: ${KVM_RESULT#KVM_FAIL:}"
        export KVM_AVAILABLE=0
        export KVM_STATUS="unavailable"
        if [ "$HIPERISO_FALLBACK" != "1" ]; then
            hiperiso_write_status "failed" "fallback_no_kvm"
            /fallback_boot.sh
            exec /bin/sh
        fi
        hiperiso_log "Fallback forced -- continuing with TCG emulation"
        hiperiso_write_status "running" "tcg_emulation"
        ;;
esac
hiperiso_timing_mark "kvm_checked"

# ── Step 8: Verify ISO exists ────────────────────────────────
if [ ! -f "$ISO_PATH" ]; then
    hiperiso_log "FATAL: ISO file not found: $ISO_PATH"
    hiperiso_log "Contents of $DATA_MOUNT:"
    ls -la "$DATA_MOUNT" 2>/dev/null >> "${LOG_DIR}/env.txt"
    hiperiso_write_status "failed" "iso_not_found"
    hiperiso_run_report
    hiperiso_finalize_session "failed" "" "iso_not_found"
    exec /bin/sh
fi
hiperiso_log "ISO found: $ISO_PATH"
hiperiso_timing_mark "iso_verified"

# ── Step 8b: Run conf_replace plugin (if configured) ─────────
_modified_iso=$(sh /conf_replace.sh 2>"${LOG_DIR}/conf_replace.log" || true)
if [ -n "$_modified_iso" ] && [ -f "$_modified_iso" ]; then
    ISO_PATH="$_modified_iso"
    export ISO_PATH
    hiperiso_log "Using modified ISO: $ISO_PATH"
fi

# ── Step 9: Start log flush daemon ───────────────────────────
rm -f /tmp/hiperiso_done
/log_flush.sh "$LOG_DIR" &
LOG_FLUSH_PID=$!
hiperiso_log "Log flush daemon started (PID $LOG_FLUSH_PID)"

hiperiso_write_status "running" "launching_qemu"
hiperiso_timing_mark "qemu_launching"

# ── Step 10: Launch QEMU ─────────────────────────────────────
/qemu_launch.sh
QEMU_EXIT_CODE=$?

# ── Step 11: Flush remaining logs ────────────────────────────
touch /tmp/hiperiso_done
hiperiso_timing_mark "qemu_returned"

_wait=0
while kill -0 "$LOG_FLUSH_PID" 2>/dev/null; do
    _wait=$((_wait + 1))
    if [ "$_wait" -gt 10 ]; then
        kill "$LOG_FLUSH_PID" 2>/dev/null
        break
    fi
    sleep 1
done

sync
printf 'QEMU_EXIT_CODE=%s\n' "$QEMU_EXIT_CODE" >> "${LOG_DIR}/env.txt"

hiperiso_write_status "running" "qemu_exited" "$QEMU_EXIT_CODE"

if [ "$QEMU_EXIT_CODE" -eq 0 ]; then
    _session_status="complete"
    _session_stage="session_complete"
else
    _session_status="failed"
    _session_stage="qemu_error"
fi

hiperiso_run_report
hiperiso_timing_mark "report_done"
hiperiso_finalize_session "$_session_status" "$QEMU_EXIT_CODE" "$_session_stage"
hiperiso_timing_mark "session_finalized"
hiperiso_timing_write_json

# ── Step 12: Print session summary ───────────────────────────
printf '\n'
printf '======================================================\n'
printf '  hiperiso boot session complete                      \n'
printf '======================================================\n'
printf '  ISO:       %s\n' "$HIPERISO_ISO_PATH"
printf '  Session:   %s\n' "$(hiperiso_session_id)"
printf '  QEMU exit: %s\n' "$QEMU_EXIT_CODE"
printf '  Logs:      %s\n' "$LOG_DIR"
printf '======================================================\n'
printf '\n'
printf 'Log files:\n'
ls -la "$LOG_DIR" 2>/dev/null
printf '\n'

# ── Step 13: Offer reboot ────────────────────────────────────
printf 'Press ENTER to reboot, or type "shell" for recovery: '
read -r _response

case "$_response" in
    shell|sh)
        printf '\nDropping to recovery shell...\n'
        exec /bin/sh
        ;;
esac

printf '\nRebooting...\n'
sync

if command -v reboot >/dev/null 2>&1; then
    reboot -f
else
    printf 'b' > /proc/sysrq-trigger 2>/dev/null
fi

# Kernel will panic if PID 1 exits -- but reboot should have fired
exec /bin/sh
