#!/usr/bin/env bash

# Define colors and logging functions
RED="\033[0;31m"
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
BLUE="\033[0;34m"
CYAN="\033[0;36m"
BOLD="\033[1m"
RESET="\033[0m"

info()    { echo -e "${CYAN}==>${RESET} $1"; }
success() { echo -e "${GREEN}✔${RESET} $1"; }
warn()    { echo -e "${YELLOW}⚠${RESET} $1"; }
error()   { echo -e "${RED}✘${RESET} $1"; exit 1; }

echo -e "${BOLD}${BLUE}==============================================================${RESET}"
echo -e "${BOLD}${BLUE} Nickvision macOS publish-and-package Script${RESET}"
echo -e "${BOLD}${BLUE}==============================================================${RESET}"

# Initialize script and check arguments
CURRENT_PWD=$(pwd)
set -euo pipefail
if [[ $# -lt 1 ]]; then
    error "Usage: $0 runtime"
fi

# Change pwd to script directory
info "Changing to script directory..."
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
cd "$SCRIPT_DIR"
success "Changed to script directory: $SCRIPT_DIR"

# Load variables
info "Loading variables..."
APP_ID="org.nickvision.tubeconverter"
APP_NAME="Parabolic"
PROJECT="Nickvision.Parabolic.GNOME"
RUNTIME="$1"
APP_BUNDLE="${APP_NAME}.app"
info "Runtime: $RUNTIME"
info "App bundle: $APP_BUNDLE"
success "Loaded variables."

echo -e "${BOLD}${BLUE}==============================================================${RESET}"
echo -e "${BOLD}${BLUE} Publishing and Packaging ${APP_NAME}${RESET}"
echo -e "${BOLD}${BLUE}==============================================================${RESET}"

# Publish application
info "Publishing application..."
export DOTNET_CLI_TELEMETRY_OPTOUT=1
dotnet publish -c Release \
    "../../$PROJECT/$PROJECT.csproj" \
    --runtime $RUNTIME \
    --self-contained true \
    -p:PublishReadyToRun=true
PUBLISH_DIR="$(find "../../$PROJECT/bin/Release" -type d -name publish | head -n1)"
if [[ ! -d "$PUBLISH_DIR" ]]; then
    error "Publish directory not found!"
fi
success "Published application."

# Create app bundle structure
info "Creating app bundle structure..."
mkdir -p "$APP_BUNDLE/Contents/MacOS"
mkdir -p "$APP_BUNDLE/Contents/Resources"
success "Created app bundle structure."

# Copy published files to app bundle
info "Copying published files to app bundle..."
cp -R "$PUBLISH_DIR/"* "$APP_BUNDLE/Contents/MacOS/"
success "Copied published files to app bundle."
cp "Info.plist" "$APP_BUNDLE/Contents/Info.plist"
sed -i '' "s|@APP_ID@|$APP_ID|g" "$APP_BUNDLE/Contents/Info.plist"
sed -i '' "s|@APP_NAME@|$APP_NAME|g" "$APP_BUNDLE/Contents/Info.plist"
sed -i '' "s|@OUTPUT_NAME@|$PROJECT|g" "$APP_BUNDLE/Contents/Info.plist"
success "Created Info.plist."

# Set app icon
info "Setting app icon..."
if [[ -f "../${APP_ID}.png" ]]; then
    mkdir -p AppIcon.iconset
    sips -z 16 16 "../${APP_ID}.png" --out AppIcon.iconset/icon_16x16.png
    sips -z 32 32 "../${APP_ID}.png" --out AppIcon.iconset/icon_16x16@2x.png
    sips -z 32 32 "../${APP_ID}.png" --out AppIcon.iconset/icon_32x32.png
    sips -z 64 64 "../${APP_ID}.png" --out AppIcon.iconset/icon_32x32@2x.png
    sips -z 128 128 "../${APP_ID}.png" --out AppIcon.iconset/icon_128x128.png
    sips -z 256 256 "../${APP_ID}.png" --out AppIcon.iconset/icon_128x128@2x.png
    sips -z 256 256 "../${APP_ID}.png" --out AppIcon.iconset/icon_256x256.png
    sips -z 512 512 "../${APP_ID}.png" --out AppIcon.iconset/icon_256x256@2x.png
    sips -z 512 512 "../${APP_ID}.png" --out AppIcon.iconset/icon_512x512.png
    sips -z 1024 1024 "../${APP_ID}.png" --out AppIcon.iconset/icon_512x512@2x.png
    iconutil -c icns AppIcon.iconset -o "$APP_BUNDLE/Contents/Resources/${APP_ID}.icns"
    rm -rf AppIcon.iconset
    success "Set app icon."
else
    warn "Icon file not found at ../${APP_ID}.png"
fi

# Bundle dependencies
info "Bundling dependencies..."
YT_DLP_PATH="./yt-dlp"
ARIA2C_PATH="$(which aria2c 2>/dev/null || true)"
FFMPEG_PATH="$(which ffmpeg 2>/dev/null || true)"
FFPROBE_PATH="$(which ffprobe 2>/dev/null || true)"
FFPLAY_PATH="$(which ffplay 2>/dev/null || true)"
DENO_PATH="$(which deno 2>/dev/null || true)"
if [[ -f "$YT_DLP_PATH" ]]; then
    cp "$YT_DLP_PATH" "$APP_BUNDLE/Contents/MacOS/yt-dlp"
    chmod +x "$APP_BUNDLE/Contents/MacOS/yt-dlp"
    success "Bundled yt-dlp."
else
    warn "yt-dlp not found at $YT_DLP_PATH"
fi
if [[ -n "$ARIA2C_PATH" && -f "$ARIA2C_PATH" ]]; then
    cp "$ARIA2C_PATH" "$APP_BUNDLE/Contents/MacOS/aria2c"
    chmod +x "$APP_BUNDLE/Contents/MacOS/aria2c"
    success "Bundled aria2c."
else
    warn "aria2c not found"
fi
if [[ -n "$FFMPEG_PATH" && -f "$FFMPEG_PATH" ]]; then
    cp "$FFMPEG_PATH" "$APP_BUNDLE/Contents/MacOS/ffmpeg"
    chmod +x "$APP_BUNDLE/Contents/MacOS/ffmpeg"
    success "Bundled ffmpeg."
else
    warn "ffmpeg not found"
fi
if [[ -n "$FFPROBE_PATH" && -f "$FFPROBE_PATH" ]]; then
    cp "$FFPROBE_PATH" "$APP_BUNDLE/Contents/MacOS/ffprobe"
    chmod +x "$APP_BUNDLE/Contents/MacOS/ffprobe"
    success "Bundled ffprobe."
else
    warn "ffprobe not found"
fi
if [[ -n "$FFPLAY_PATH" && -f "$FFPLAY_PATH" ]]; then
    cp "$FFPLAY_PATH" "$APP_BUNDLE/Contents/MacOS/ffplay"
    chmod +x "$APP_BUNDLE/Contents/MacOS/ffplay"
    success "Bundled ffplay."
else
    warn "ffplay not found"
fi
if [[ -n "$DENO_PATH" && -f "$DENO_PATH" ]]; then
    cp "$DENO_PATH" "$APP_BUNDLE/Contents/MacOS/deno"
    chmod +x "$APP_BUNDLE/Contents/MacOS/deno"
    success "Bundled deno."
else
    warn "deno not found"
fi
success "Bundled dependencies."

# Bundle GTK4 and libadwaita
info "Bundling GTK4 and libadwaita..."
BREW_PREFIX="$(brew --prefix)"
FRAMEWORKS_DIR="$APP_BUNDLE/Contents/Frameworks"
BUNDLE_RESOURCES_DIR="$APP_BUNDLE/Contents/Resources"
mkdir -p "$FRAMEWORKS_DIR"
mkdir -p "$BUNDLE_RESOURCES_DIR"

# Recursively copy a dylib and its Homebrew-installed dependencies into Contents/Frameworks,
# rewriting install names to use @rpath so the dylibs can resolve each other at runtime.
bundle_dylib() {
    local lib_path="$1"
    local lib_name
    lib_name="$(basename "$lib_path")"
    [[ -f "$FRAMEWORKS_DIR/$lib_name" ]] && return
    [[ ! -f "$lib_path" ]] && { warn "Library not found: $lib_path"; return; }
    cp "$lib_path" "$FRAMEWORKS_DIR/$lib_name"
    chmod 755 "$FRAMEWORKS_DIR/$lib_name"
    install_name_tool -id "@rpath/$lib_name" "$FRAMEWORKS_DIR/$lib_name" 2>/dev/null || true
    # Add @loader_path as rpath so this dylib finds its bundled siblings from the same directory
    install_name_tool -add_rpath "@loader_path" "$FRAMEWORKS_DIR/$lib_name" 2>/dev/null || true
    while IFS= read -r dep_line; do
        local dep_path dep_name
        dep_path="$(echo "$dep_line" | awk '{print $1}')"
        if [[ "$dep_path" == "$BREW_PREFIX"* && -f "$dep_path" && "$dep_path" != "$lib_path" ]]; then
            dep_name="$(basename "$dep_path")"
            install_name_tool -change "$dep_path" "@rpath/$dep_name" "$FRAMEWORKS_DIR/$lib_name" 2>/dev/null || true
            bundle_dylib "$dep_path"
        fi
    done < <(otool -L "$lib_path" | tail -n +2)
}

# Bundle libgtk-4 and libadwaita; all transitive dependencies are pulled in automatically
for LIB_NAME in "libgtk-4.1.dylib" "libadwaita-1.0.dylib"; do
    LIB_PATH="$(find "$BREW_PREFIX/lib" -name "$LIB_NAME" | head -n1)"
    if [[ -n "$LIB_PATH" ]]; then
        bundle_dylib "$LIB_PATH"
        success "Bundled $LIB_NAME and its dependencies."
    else
        warn "$LIB_NAME not found under $BREW_PREFIX/lib"
    fi
done

# Fixes dynamic library references in a standalone executable by rewriting any absolute
# Homebrew-style paths to @rpath and bundling the referenced libraries into Frameworks.
# This ensures that executables like ffmpeg/ffprobe work on all Macs regardless of whether
# Homebrew lives in /usr/local (Intel) or /opt/homebrew (Apple Silicon).
fix_tool_deps() {
    local tool_path="$1"
    local tool_name
    tool_name="$(basename "$tool_path")"
    [[ -f "$tool_path" ]] || return
    # Add rpath so the tool finds its bundled Frameworks directory at runtime
    install_name_tool -add_rpath "@executable_path/../Frameworks" "$tool_path" 2>/dev/null || true
    while IFS= read -r dep_line; do
        local dep_path dep_name actual_path
        dep_path="$(echo "$dep_line" | awk '{print $1}')"
        dep_name="$(basename "$dep_path")"
        # Match any absolute Homebrew-style library path (/usr/local or /opt/homebrew)
        if [[ "$dep_path" == /usr/local/* || "$dep_path" == /opt/homebrew/* ]]; then
            # Rewrite the hardcoded path to a relocatable @rpath reference
            install_name_tool -change "$dep_path" "@rpath/$dep_name" "$tool_path" 2>/dev/null || true
            # Resolve the actual library on this system and bundle it
            actual_path=""
            if [[ -f "$dep_path" ]]; then
                actual_path="$dep_path"
            elif [[ -f "$BREW_PREFIX/lib/$dep_name" ]]; then
                actual_path="$BREW_PREFIX/lib/$dep_name"
            else
                actual_path="$(find "$BREW_PREFIX" -name "$dep_name" -type f 2>/dev/null | head -n1)"
            fi
            if [[ -n "$actual_path" && -f "$actual_path" ]]; then
                bundle_dylib "$actual_path"
            else
                warn "[$tool_name] Could not find library to bundle: $dep_name"
            fi
        fi
    done < <(otool -L "$tool_path" | tail -n +2)
    success "Fixed $tool_name library dependencies."
}

# Fix any Homebrew references in .NET-published native libs and add Frameworks rpath
find "$APP_BUNDLE/Contents/MacOS" \( -name "*.dylib" -o -name "*.so" \) | while read -r lib; do
    install_name_tool -add_rpath "@loader_path/../Frameworks" "$lib" 2>/dev/null || true
    while IFS= read -r dep_line; do
        dep_path="$(echo "$dep_line" | awk '{print $1}')"
        if [[ "$dep_path" == "$BREW_PREFIX"* && -f "$dep_path" ]]; then
            dep_name="$(basename "$dep_path")"
            install_name_tool -change "$dep_path" "@rpath/$dep_name" "$lib" 2>/dev/null || true
        fi
    done < <(otool -L "$lib" | tail -n +2)
done

# Add Frameworks rpath and fix any direct Homebrew references in the main executable
install_name_tool -add_rpath "@executable_path/../Frameworks" "$APP_BUNDLE/Contents/MacOS/$PROJECT" 2>/dev/null || true
while IFS= read -r dep_line; do
    dep_path="$(echo "$dep_line" | awk '{print $1}')"
    if [[ "$dep_path" == "$BREW_PREFIX"* && -f "$dep_path" ]]; then
        dep_name="$(basename "$dep_path")"
        install_name_tool -change "$dep_path" "@rpath/$dep_name" "$APP_BUNDLE/Contents/MacOS/$PROJECT" 2>/dev/null || true
    fi
done < <(otool -L "$APP_BUNDLE/Contents/MacOS/$PROJECT" | tail -n +2)

# Fix library dependencies for bundled media tool executables so they resolve their
# dylib references at runtime regardless of the Homebrew prefix on the host system.
info "Fixing bundled media tool library dependencies..."
for TOOL in ffmpeg ffprobe ffplay aria2c deno; do
    fix_tool_deps "$APP_BUNDLE/Contents/MacOS/$TOOL"
done
success "Fixed bundled media tool library dependencies."

# Bundle GLib schemas required by GTK4 and libadwaita
info "Bundling GLib schemas..."
SCHEMAS_DEST="$BUNDLE_RESOURCES_DIR/share/glib-2.0/schemas"
mkdir -p "$SCHEMAS_DEST"
find "$BREW_PREFIX/share/glib-2.0/schemas" -name "*.xml" -exec cp {} "$SCHEMAS_DEST/" \; 2>/dev/null || true
glib-compile-schemas "$SCHEMAS_DEST"
success "Bundled GLib schemas."

# Bundle icon themes required by GTK4
info "Bundling icon themes..."
ICONS_DEST="$BUNDLE_RESOURCES_DIR/share/icons"
mkdir -p "$ICONS_DEST"
for THEME in hicolor Adwaita; do
    if [[ -d "$BREW_PREFIX/share/icons/$THEME" ]]; then
        cp -R "$BREW_PREFIX/share/icons/$THEME" "$ICONS_DEST/"
        success "Bundled $THEME icon theme."
    else
        warn "$THEME icon theme not found under $BREW_PREFIX/share/icons"
    fi
done

# Bundle GDK pixbuf loaders required to render icons
info "Bundling GDK pixbuf loaders..."
GDK_PIXBUF_SRC="$(find "$BREW_PREFIX/lib/gdk-pixbuf-2.0" -maxdepth 1 -type d | grep -v "^$BREW_PREFIX/lib/gdk-pixbuf-2.0$" | head -n1)"
if [[ -n "$GDK_PIXBUF_SRC" ]]; then
    GDK_PIXBUF_DEST="$BUNDLE_RESOURCES_DIR/lib/gdk-pixbuf-2.0"
    mkdir -p "$GDK_PIXBUF_DEST"
    cp -R "$GDK_PIXBUF_SRC" "$GDK_PIXBUF_DEST/"
    LOADERS_CACHE="$(find "$GDK_PIXBUF_DEST" -name "loaders.cache" | head -n1)"
    if [[ -n "$LOADERS_CACHE" ]]; then
        # Rewrite absolute paths in loaders.cache to use a placeholder; launcher will set GDK_PIXBUF_MODULE_FILE
        sed -i '' "s|$BREW_PREFIX/lib/gdk-pixbuf-2.0|@GDK_PIXBUF_DIR@|g" "$LOADERS_CACHE"
        success "Bundled GDK pixbuf loaders."
    fi
    # Fix Homebrew references in loader .so files and bundle their dependencies (e.g. librsvg for SVG rendering).
    # Loaders are dlopen()'d at runtime, so their deps must be in Frameworks and their rpaths must point there.
    info "Fixing GDK pixbuf loader library references..."
    find "$GDK_PIXBUF_DEST" -name "*.so" | while read -r loader; do
        install_name_tool -add_rpath "@loader_path/../../../../Frameworks" "$loader" 2>/dev/null || true
        while IFS= read -r dep_line; do
            dep_path="$(echo "$dep_line" | awk '{print $1}')"
            if [[ "$dep_path" == "$BREW_PREFIX"* && -f "$dep_path" ]]; then
                dep_name="$(basename "$dep_path")"
                install_name_tool -change "$dep_path" "@rpath/$dep_name" "$loader" 2>/dev/null || true
                bundle_dylib "$dep_path"
            fi
        done < <(otool -L "$loader" | tail -n +2)
        codesign --force --sign - "$loader" 2>/dev/null || true
    done
    success "Fixed GDK pixbuf loader library references."
else
    warn "GDK pixbuf loaders not found under $BREW_PREFIX/lib/gdk-pixbuf-2.0"
fi

# Create a launcher script that sets up the GTK environment before running the app.
# DYLD_LIBRARY_PATH is needed because GirCore uses dlopen() by library name at runtime.
info "Creating GTK environment launcher script..."
REAL_BINARY="${PROJECT}.bin"
mv "$APP_BUNDLE/Contents/MacOS/$PROJECT" "$APP_BUNDLE/Contents/MacOS/$REAL_BINARY"
chmod +x "$APP_BUNDLE/Contents/MacOS/$REAL_BINARY"
cat > "$APP_BUNDLE/Contents/MacOS/$PROJECT" << 'LAUNCHER_EOF'
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BUNDLE_CONTENTS="$(cd "$SCRIPT_DIR/.." && pwd)"
export DYLD_LIBRARY_PATH="$BUNDLE_CONTENTS/Frameworks${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"
export GSETTINGS_SCHEMA_DIR="$BUNDLE_CONTENTS/Resources/share/glib-2.0/schemas"
export XDG_DATA_DIRS="$BUNDLE_CONTENTS/Resources/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}"
GDK_PIXBUF_CACHE="$(find "$BUNDLE_CONTENTS/Resources/lib/gdk-pixbuf-2.0" -name "loaders.cache" 2>/dev/null | head -n1)"
if [[ -n "$GDK_PIXBUF_CACHE" ]]; then
    GDK_PIXBUF_DIR="$(dirname "$(dirname "$GDK_PIXBUF_CACHE")")"
    sed "s|@GDK_PIXBUF_DIR@|$GDK_PIXBUF_DIR|g" "$GDK_PIXBUF_CACHE" > "/tmp/gdk-pixbuf-loaders-$$.cache"
    export GDK_PIXBUF_MODULE_FILE="/tmp/gdk-pixbuf-loaders-$$.cache"
fi
exec "$SCRIPT_DIR/__REAL_BINARY__" "$@"
LAUNCHER_EOF
sed -i '' "s|__REAL_BINARY__|${REAL_BINARY}|g" "$APP_BUNDLE/Contents/MacOS/$PROJECT"
chmod +x "$APP_BUNDLE/Contents/MacOS/$PROJECT"
success "Created GTK environment launcher script."

# Set executable permissions
info "Setting executable permissions..."
chmod +x "$APP_BUNDLE/Contents/MacOS/$PROJECT"
success "Set executable permissions."

# Ad-hoc re-sign all binaries after install_name_tool modifications
info "Ad-hoc signing bundled libraries and executables..."
find "$FRAMEWORKS_DIR" \( -name "*.dylib" -o -name "*.so" \) | while read -r lib; do
    codesign --force --sign - "$lib" 2>/dev/null || true
done
find "$APP_BUNDLE/Contents/MacOS" \( -name "*.dylib" -o -name "*.so" \) | while read -r lib; do
    codesign --force --sign - "$lib" 2>/dev/null || true
done
# Re-sign media tool executables after install_name_tool has modified their rpaths/deps
for TOOL in ffmpeg ffprobe ffplay aria2c deno; do
    if [[ -f "$APP_BUNDLE/Contents/MacOS/$TOOL" ]]; then
        codesign --force --sign - "$APP_BUNDLE/Contents/MacOS/$TOOL" 2>/dev/null || true
    fi
done
codesign --force --sign - "$APP_BUNDLE/Contents/MacOS/$REAL_BINARY"
success "Ad-hoc signed bundled libraries and executables."

# Restore pwd
info "Restoring previous working directory..."
cd "$CURRENT_PWD"
success "Restored working directory to $CURRENT_PWD."

echo -e "${BOLD}${BLUE}==============================================================${RESET}"
echo -e "${BOLD}${GREEN}✔ Published and Packaged ${APP_NAME} Successfully!${RESET}"
echo -e "${BOLD}${BLUE}==============================================================${RESET}"
