#!/bin/bash
# ==============================================================
# build-vineforce-teams-deb.sh
# Builds a real .deb installer for Vineforce Teams (Linux)
# ==============================================================
set -euo pipefail

# ---------- CONFIG ----------
PKG_NAME="vineforce-teams"
PKG_VERSION="1.0.0"
PKG_ARCH="amd64"
ZIP_URL="https://cdn01.vineforce.net/download-v1/vineforce/linux/Vineforce_Teams.zip"
# The published app zip does NOT include window_logo.ico (it's only in the
# source repo's Resources/Images folder, not copied to publish output),
# so the icon is hosted and downloaded separately instead.
ICON_URL="https://cdn01.vineforce.net/logos/Vineforce_Favicon_128.ico"
EXTENSION_UUID="window-calls-extended@hseliger.eu"
GITHUB_BASE="https://raw.githubusercontent.com/hseliger/window-calls-extended/main"
EXECUTABLE_NAME="Vineforce.Admin.Desktop.Avalonia"

# WM_CLASS is used so the *running* app's taskbar/dock icon also
# matches the launcher icon, not just the app-grid entry.
# To confirm/update this value: run `xprop WM_CLASS` in a terminal,
# then click the running app's window. Use the SECOND quoted value.
WM_CLASS="Vineforce.Admin.Desktop.Avalonia"

BUILD_DIR="$(pwd)/${PKG_NAME}_build"
PKG_ROOT="${BUILD_DIR}/${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}"

echo "=== Cleaning previous build ==="
rm -rf "$BUILD_DIR"
mkdir -p "$PKG_ROOT"

# ==============================================================
# STEP 1: Create Debian package directory structure
# ==============================================================
echo "=== Creating package structure ==="

mkdir -p "$PKG_ROOT/DEBIAN"
mkdir -p "$PKG_ROOT/opt/${PKG_NAME}"
mkdir -p "$PKG_ROOT/usr/bin"
mkdir -p "$PKG_ROOT/usr/share/applications"
mkdir -p "$PKG_ROOT/usr/share/gnome-shell/extensions/${EXTENSION_UUID}"
mkdir -p "$PKG_ROOT/etc/xdg/autostart"
mkdir -p "$PKG_ROOT/usr/share/icons/hicolor/256x256/apps"

# ==============================================================
# STEP 2: Download and stage the application
# ==============================================================
echo "=== Downloading application ==="

TMP_ZIP="$(mktemp --suffix=.zip)"
curl -sSL "$ZIP_URL" -o "$TMP_ZIP"
unzip -q "$TMP_ZIP" -d "$PKG_ROOT/opt/${PKG_NAME}"
rm -f "$TMP_ZIP"

# Confirm the executable exists where we expect it.
# NOTE: adjust this path if the zip's internal folder structure differs.
EXE_REL_PATH="publish/${EXECUTABLE_NAME}"
if [ ! -f "$PKG_ROOT/opt/${PKG_NAME}/${EXE_REL_PATH}" ]; then
    echo "ERROR: Executable not found at opt/${PKG_NAME}/${EXE_REL_PATH}"
    echo "Check the zip's actual folder structure and update EXE_REL_PATH."
    exit 1
fi

chmod +x "$PKG_ROOT/opt/${PKG_NAME}/${EXE_REL_PATH}"

# ==============================================================
# STEP 3: Find and convert the app icon (window_logo.ico -> PNG)
# Fully automatic: picks the LARGEST embedded frame in the .ico
# so no manual conversion step is ever needed.
# ==============================================================
echo "=== Converting app icon ==="

ICON_NAME="vineforce-teams"
ICON_DEST="$PKG_ROOT/usr/share/icons/hicolor/256x256/apps/${ICON_NAME}.png"

if ! command -v convert >/dev/null 2>&1 && ! command -v magick >/dev/null 2>&1; then
    echo "ImageMagick not found — installing it now (needed to convert the .ico icon)..."
    sudo apt-get update -qq
    sudo apt-get install -y imagemagick
fi

# Use whichever ImageMagick binary is available (newer distros use 'magick')
if command -v magick >/dev/null 2>&1; then
    IM_CONVERT="magick"
    IM_IDENTIFY="magick identify"
else
    IM_CONVERT="convert"
    IM_IDENTIFY="identify"
fi

# Download the icon directly from the CDN rather than searching the
# extracted zip, since window_logo.ico is not included in the published
# app output (it only exists in the source repo's Resources/Images folder).
ICON_SRC="$(mktemp --suffix=.ico)"

echo "Downloading icon from: $ICON_URL"
if curl -sSL --fail "$ICON_URL" -o "$ICON_SRC"; then
    # An .ico can bundle several resolutions (16x16, 32x32, 256x256...).
    # Inspect every embedded frame and pick the one with the largest
    # width, so we always convert the highest-quality version.
    BEST_FRAME="$(${IM_IDENTIFY} -format "%p %w\n" "$ICON_SRC" 2>/dev/null \
        | sort -k2 -n -r | head -n 1 | awk '{print $1}')"

    if [ -z "$BEST_FRAME" ]; then
        echo "Could not inspect .ico frames, defaulting to frame 0."
        BEST_FRAME=0
    fi

    echo "Using largest icon frame index: $BEST_FRAME"
    ${IM_CONVERT} "${ICON_SRC}[${BEST_FRAME}]" -resize 256x256 "$ICON_DEST"
    echo "Icon converted to: $ICON_DEST"
else
    echo "WARNING: could not download icon from $ICON_URL"
    echo "The package will fall back to a generic system icon."
fi

rm -f "$ICON_SRC"

# ==============================================================
# STEP 4: Stage the GNOME Shell extension (system-wide)
# ==============================================================
echo "=== Downloading GNOME extension ==="

curl -sSL "$GITHUB_BASE/extension.js" \
    -o "$PKG_ROOT/usr/share/gnome-shell/extensions/${EXTENSION_UUID}/extension.js"
curl -sSL "$GITHUB_BASE/metadata.json" \
    -o "$PKG_ROOT/usr/share/gnome-shell/extensions/${EXTENSION_UUID}/metadata.json"

chmod 644 "$PKG_ROOT/usr/share/gnome-shell/extensions/${EXTENSION_UUID}"/*

# ==============================================================
# STEP 5: CLI symlink (usr/bin/vineforce-teams)
# ==============================================================
echo "=== Creating CLI symlink ==="

ln -sf "/opt/${PKG_NAME}/${EXE_REL_PATH}" "$PKG_ROOT/usr/bin/${PKG_NAME}"

# ==============================================================
# STEP 6: Desktop menu entry (system-wide, all users)
# StartupWMClass added so the taskbar/dock icon matches too,
# not just the app-grid launcher tile.
# ==============================================================
echo "=== Writing desktop menu entry ==="

cat > "$PKG_ROOT/usr/share/applications/${PKG_NAME}.desktop" << EOF
[Desktop Entry]
Type=Application
Name=Vineforce Teams
Comment=Vineforce Teams desktop tracker
Exec=/opt/${PKG_NAME}/${EXE_REL_PATH}
Path=/opt/${PKG_NAME}/publish
Icon=${ICON_NAME}
StartupWMClass=${WM_CLASS}
Terminal=false
Categories=Office;
NoDisplay=false
EOF

# ==============================================================
# STEP 7: Autostart entry (system-wide, applies to every user
# via the XDG autostart spec — no per-user hacking required)
# ==============================================================
echo "=== Writing autostart entry ==="

cat > "$PKG_ROOT/etc/xdg/autostart/${PKG_NAME}.desktop" << EOF
[Desktop Entry]
Type=Application
Name=Vineforce Teams
Comment=Vineforce Teams desktop tracker
Exec=/opt/${PKG_NAME}/${EXE_REL_PATH}
Path=/opt/${PKG_NAME}/publish
Icon=${ICON_NAME}
StartupWMClass=${WM_CLASS}
Terminal=false
X-GNOME-Autostart-enabled=true
Hidden=false
NoDisplay=false
EOF

# ==============================================================
# STEP 8: DEBIAN/control
# Dependencies are declared here so apt resolves/installs them
# automatically — no manual apt-get calls needed at runtime.
# ==============================================================
echo "=== Writing control file ==="

INSTALLED_SIZE=$(du -sk "$PKG_ROOT" | cut -f1)

cat > "$PKG_ROOT/DEBIAN/control" << EOF
Package: ${PKG_NAME}
Version: ${PKG_VERSION}
Section: utils
Priority: optional
Architecture: ${PKG_ARCH}
Installed-Size: ${INSTALLED_SIZE}
Depends: procps, coreutils, sed, grep, gnome-shell-extension-prefs, dbus-x11, curl, gnome-screenshot, xdg-desktop-portal
Maintainer: Vineforce <support@vineforce.net>
Description: Vineforce Teams desktop tracker
EOF

# ==============================================================
# STEP 9: postinst — runs after files are unpacked
# ==============================================================
echo "=== Writing postinst script ==="

cat > "$PKG_ROOT/DEBIAN/postinst" << 'EOF'
#!/bin/bash
set -e

# Install .NET 10 runtime system-wide if not already present.
# NOTE: .NET isn't in standard Debian/Ubuntu repos, so this installs
# it directly from Microsoft's install script into /opt/dotnet.
# If your fleet already provisions .NET via Microsoft's APT feed,
# you can remove this block and add a proper Depends entry instead.
if ! command -v dotnet >/dev/null 2>&1; then
    echo "Installing .NET 10 runtime..."
    TMP_INSTALL_SH="$(mktemp)"
    curl -sSL https://dot.net/v1/dotnet-install.sh -o "$TMP_INSTALL_SH"
    chmod +x "$TMP_INSTALL_SH"
    "$TMP_INSTALL_SH" --channel 10.0 --runtime dotnet --install-dir /opt/dotnet
    ln -sf /opt/dotnet/dotnet /usr/local/bin/dotnet
    rm -f "$TMP_INSTALL_SH"
fi

# Refresh desktop entry caches so the app shows up immediately.
if command -v update-desktop-database >/dev/null 2>&1; then
    update-desktop-database /usr/share/applications || true
fi

# Refresh the icon cache so the custom icon shows up immediately
# instead of requiring a logout/login.
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
    gtk-update-icon-cache -f /usr/share/icons/hicolor || true
fi

echo ""
echo "Vineforce Teams installed successfully."
echo "Log out and back in once to activate the GNOME Shell extension"
echo "and the login autostart entry."
echo ""

# ------------------------------------------------------------
# Launch the app immediately, right now, without waiting for
# next login. postinst runs as root (via sudo/dpkg), so we
# need to figure out who the *real* logged-in desktop user is
# and run the GUI app as them, with their session environment
# (DISPLAY/DBUS), otherwise the app either fails or has no window.
#
# IMPORTANT: everything in this block is best-effort. If detection
# or launch fails for any reason, we must NOT let it fail postinst
# itself (dpkg marks the whole package broken if postinst exits
# non-zero), so every risky step below is guarded.
# ------------------------------------------------------------
(
    set +e

    REAL_USER="${SUDO_USER:-}"
    if [ -z "$REAL_USER" ]; then
        REAL_USER="$(loginctl list-sessions --no-legend 2>/dev/null | awk '{print $3}' | head -n 1)"
    fi

    if [ -n "$REAL_USER" ] && id "$REAL_USER" >/dev/null 2>&1; then
        USER_UID="$(id -u "$REAL_USER" 2>/dev/null)"
        USER_HOME="$(getent passwd "$REAL_USER" 2>/dev/null | cut -d: -f6)"

        # Ask loginctl for this user's actual graphical session Display
        # rather than relying on tools like 'w' that may not be installed.
        SESSION_ID="$(loginctl list-sessions --no-legend 2>/dev/null \
            | awk -v u="$REAL_USER" '$3==u {print $1; exit}')"
        DISPLAY_VAL=""
        if [ -n "$SESSION_ID" ]; then
            DISPLAY_VAL="$(loginctl show-session "$SESSION_ID" -p Display --value 2>/dev/null)"
        fi
        [ -z "$DISPLAY_VAL" ] && DISPLAY_VAL=":0"

        DBUS_ADDR="unix:path=/run/user/${USER_UID}/bus"

        echo "Launching Vineforce Teams now for user: $REAL_USER"
        sudo -u "$REAL_USER" \
            DISPLAY="$DISPLAY_VAL" \
            DBUS_SESSION_BUS_ADDRESS="$DBUS_ADDR" \
            HOME="$USER_HOME" \
            XDG_RUNTIME_DIR="/run/user/${USER_UID}" \
            nohup /opt/vineforce-teams/publish/Vineforce.Admin.Desktop.Avalonia \
            >/dev/null 2>&1 &
        disown 2>/dev/null
    else
        echo "Could not determine the desktop user automatically — start the app"
        echo "manually this once, or log out/in to trigger the autostart entry."
    fi

    exit 0
) || true

exit 0
EOF

chmod 755 "$PKG_ROOT/DEBIAN/postinst"

# ==============================================================
# STEP 10: prerm — runs BEFORE files are removed, so we can
# stop the running app first (apt purge/remove otherwise leaves
# it running until the user closes it manually)
# ==============================================================
echo "=== Writing prerm script ==="

cat > "$PKG_ROOT/DEBIAN/prerm" << EOF
#!/bin/bash
set -e

APP_PATTERN="${EXECUTABLE_NAME}"

# Politely ask any running instance to exit first.
pkill -TERM -f "\$APP_PATTERN" 2>/dev/null || true

# Wait up to 5 seconds for it to actually exit.
for i in 1 2 3 4 5; do
    if ! pgrep -f "\$APP_PATTERN" >/dev/null 2>&1; then
        break
    fi
    sleep 1
done

# Still running? Force-kill it so the app never survives removal.
if pgrep -f "\$APP_PATTERN" >/dev/null 2>&1; then
    pkill -KILL -f "\$APP_PATTERN" 2>/dev/null || true
    sleep 1
fi

exit 0
EOF

chmod 755 "$PKG_ROOT/DEBIAN/prerm"

# ==============================================================
# STEP 11: postrm — runs on removal/purge
# ==============================================================
echo "=== Writing postrm script ==="

cat > "$PKG_ROOT/DEBIAN/postrm" << 'EOF'
#!/bin/bash
set -e

if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
    rm -rf /usr/share/gnome-shell/extensions/window-calls-extended@hseliger.eu
fi

if command -v update-desktop-database >/dev/null 2>&1; then
    update-desktop-database /usr/share/applications || true
fi

if command -v gtk-update-icon-cache >/dev/null 2>&1; then
    gtk-update-icon-cache -f /usr/share/icons/hicolor || true
fi

exit 0
EOF

chmod 755 "$PKG_ROOT/DEBIAN/postrm"

# ==============================================================
# STEP 12: Build the .deb
# ==============================================================
echo "=== Building .deb package ==="

if command -v fakeroot >/dev/null 2>&1; then
    fakeroot dpkg-deb --build "$PKG_ROOT"
else
    echo "WARNING: 'fakeroot' not found — file ownership in the .deb may not be root:root."
    echo "Install it with: sudo apt-get install fakeroot"
    dpkg-deb --build "$PKG_ROOT"
fi

OUTPUT_DEB="${BUILD_DIR}/${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}.deb"
mv "${PKG_ROOT}.deb" "$OUTPUT_DEB" 2>/dev/null || true

echo ""
echo "=== BUILD COMPLETE ==="
echo "Package: ${OUTPUT_DEB}"
echo ""
echo "Install it with:"
echo "  sudo apt install ${OUTPUT_DEB}"
echo "Or:"
echo "  sudo dpkg -i ${OUTPUT_DEB} && sudo apt-get install -f"
