#!/bin/bash

# Native Windows build script
# Runs in Git Bash (Git for Windows)

set -euo pipefail

# Use plain `pwd` (POSIX path). `pwd -W` returns a Windows-form path
# with backslashes on MSYS / Git Bash, which would then break `dirname`
# and `source "$SCRIPT_DIR/build-common.sh"` since those expect POSIX
# paths. If a Windows-form path is needed downstream (e.g. for cmake-js
# or a non-MSYS tool), convert at the point of use via
# `cygpath -w "$SCRIPT_DIR"`.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG="$PROJECT_DIR/.build-config.json"

# Platform identifier
export PLATFORM="windows"

# Check for Git Bash/MSYS
if [[ "$OSTYPE" != "msys" ]] && [[ "$OSTYPE" != "win32" ]] && [[ -z "${MSYSTEM:-}" ]]; then
    echo "Error: This script must be run in Git Bash (Git for Windows)" >&2
    echo "Download: https://git-scm.com/download/win" >&2
    exit 1
fi

echo "=== Slopsmith Desktop Windows Build ==="
echo ""

# Color setup
export RED='\033[0;31m'
export GREEN='\033[0;32m'
export YELLOW='\033[1;33m'
export BLUE='\033[0;34m'
export NC='\033[0m'

# Source common build logic
source "$SCRIPT_DIR/build-common.sh"

# Platform-specific: Return expected artifact patterns
# Windows target is now "dir" (electron-builder unpacked only) because Velopack
# generates the installer via `vpk pack` on tagged CI runs. For non-tag builds
# (PR validation, main-branch pushes) only the unpacked dir is produced.
get_expected_artifacts() {
    printf "%s\n" "$PROJECT_DIR/release/win-unpacked/*.exe"
}

# Platform-specific: Install system dependencies
install_system_deps() {
    # Windows: install via Chocolatey if available
    if command -v choco.exe &>/dev/null || command -v choco &>/dev/null; then
        choco install cmake ffmpeg -y --installargs 'ADD_CMAKE_TO_PATH=System' || echo "Chocolatey install may have failed, continuing..."
    else
        echo_warning "Chocolatey not found - skipping system package installation"
        echo "  Make sure cmake and ffmpeg are already in PATH"
    fi
}

# Platform-specific: Bundle Python runtime
bundle_python_impl() {
    # Windows: download embeddable Python
    PYTHON_VERSION=$(python3 "$SCRIPT_DIR/parse-build-config.py" "$CONFIG" .versions.python)
    PYTHON_MAJOR="${PYTHON_VERSION%%.*}"
    PYTHON_MINOR="${PYTHON_VERSION#*.}"
    PYTHON_EMBED_URL="https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-embed-amd64.zip"
    
    echo "Downloading Python embeddable..."
    local curl_status=0
    curl -sL --fail --retry 5 --retry-delay 5 --retry-all-errors "$PYTHON_EMBED_URL" -o /tmp/python-embed.zip || curl_status=$?
    if [[ "$curl_status" -ne 0 ]]; then
        echo_error "Failed to download Python embeddable package"
        echo "  URL: $PYTHON_EMBED_URL"
        echo "  curl exit code: $curl_status"
        exit 1
    fi
    
    # Wipe the existing python dir before extracting so a re-run doesn't
    # leave stale files (e.g. an old ._pth from a previous Python version)
    # mixed in with the freshly-extracted embeddable distribution.
    rm -rf "$PROJECT_DIR/resources/python"
    mkdir -p "$PROJECT_DIR/resources/python"
    unzip -q /tmp/python-embed.zip -d "$PROJECT_DIR/resources/python/"

    # Enable site-packages by editing the ._pth file
    # IMPORTANT: On Windows embeddable Python, PYTHONPATH environment variable is IGNORED
    # when a ._pth file exists (isolated mode). We must add paths directly to the .pth file.
    # The embeddable zip is supposed to ship a ._pth file; if it doesn't,
    # the rest of the script's PATH-injection won't work, so fail fast.
    PTH_FILE=$(find "$PROJECT_DIR/resources/python" -name "*._pth" | head -1)
    if [[ -z "$PTH_FILE" ]]; then
        echo_error "No ._pth file found in extracted embeddable Python — upstream zip layout may have changed"
        exit 1
    fi
    if [[ -n "$PTH_FILE" ]]; then
        # Enable site-packages
        sed -i 's/#import site/import site/' "$PTH_FILE"
        echo "Lib/site-packages" >> "$PTH_FILE"
        # Add Slopsmith paths (relative to resources/python)
        # These must be in the .pth file since PYTHONPATH is ignored in isolated mode
        echo "# Slopsmith modules (relative to resources/python)" >> "$PTH_FILE"
        echo "../slopsmith" >> "$PTH_FILE"
        echo "../slopsmith/lib" >> "$PTH_FILE"
    fi
    
    # Install pip
    echo "Downloading pip..."
    if ! curl -sL --fail --retry 5 --retry-delay 5 --retry-all-errors \
        https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py; then
        echo_error "Failed to download pip installer"
        exit 1
    fi
    "$PROJECT_DIR/resources/python/python.exe" /tmp/get-pip.py --quiet --no-cache-dir

# Install packages
# Install build tools first (required for building from source on Windows embeddable Python)
"$PROJECT_DIR/resources/python/python.exe" -m pip install --quiet --no-cache-dir \
        setuptools wheel
# Install slopsmith runtime requirements (single source of truth —
# drift used to silently break desktop builds whenever slopsmith added
# a dep), then desktop-only extras. SLOPSMITH_DIR is exported by
# clone_slopsmith() in build-common.sh; fall back to local-dev paths
# to match bundle-slopsmith.sh's discovery so this works outside CI.
if [[ -z "${SLOPSMITH_DIR:-}" ]]; then
    if [[ -d "$PROJECT_DIR/../slopsmith" ]]; then
        SLOPSMITH_DIR="$PROJECT_DIR/../slopsmith"
    elif [[ -d "$HOME/Repositories/slopsmith" ]]; then
        SLOPSMITH_DIR="$HOME/Repositories/slopsmith"
    fi
fi
if [[ -z "${SLOPSMITH_DIR:-}" ]] || [[ ! -f "$SLOPSMITH_DIR/requirements.txt" ]]; then
    echo "ERROR: slopsmith requirements.txt not found (SLOPSMITH_DIR=${SLOPSMITH_DIR:-<unset>})." >&2
    echo "       Expected SLOPSMITH_DIR to be exported by clone_slopsmith() in build-common.sh," >&2
    echo "       or slopsmith cloned next to this repo." >&2
    exit 1
fi
"$PROJECT_DIR/resources/python/python.exe" -m pip install --quiet --no-cache-dir \
        -r "$SLOPSMITH_DIR/requirements.txt"
"$PROJECT_DIR/resources/python/python.exe" -m pip install --quiet --no-cache-dir \
        -r "$PROJECT_DIR/.packages/python.txt"
}

# Usage: download_with_retries <url> <output_path> <description>
download_with_retries() {
    local url="$1"
    local output_path="$2"
    local description="$3"
    local max_attempts=3
    local attempt=1
    local delay=10
    
    while [[ $attempt -le $max_attempts ]]; do
        echo "  Downloading $description (attempt $attempt/$max_attempts)..."
        if curl -sL --fail --max-time 120 "$url" -o "$output_path"; then
            echo "    Successfully downloaded $description"
            return 0
        fi
        
        local exit_code=$?
        echo "    Download failed with exit code $exit_code"
        
        if [[ $attempt -lt $max_attempts ]]; then
            echo "    Retrying in ${delay}s..."
            sleep $delay
            delay=$((delay * 2))
        fi
        
        attempt=$((attempt + 1))
    done
    
    echo_error "Failed to download $description after $max_attempts attempts"
    return 1
}

# Platform-specific: Bundle system binaries
# These binaries are REQUIRED for core functionality. The build will fail
# if downloads don't succeed after multiple retry attempts.
bundle_binaries_impl() {
    mkdir -p "$PROJECT_DIR/resources/bin"
    
    # ffmpeg static build
    echo "Downloading ffmpeg..."
    if ! download_with_retries \
        "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip" \
        "/tmp/ffmpeg.zip" \
        "ffmpeg"; then
        exit 1
    fi
    unzip -q /tmp/ffmpeg.zip -d /tmp/ffmpeg
    # Validate the expected layout instead of `cp ... || true` — a broken
    # / changed zip layout would otherwise drop `ffmpeg.exe` silently and
    # surface as a less-direct error in `verify_bundled_binaries`.
    FFMPEG_BIN=$(find /tmp/ffmpeg -name 'ffmpeg.exe' -type f | head -1)
    if [[ -z "$FFMPEG_BIN" ]]; then
        echo_error "ffmpeg.exe not found after extracting /tmp/ffmpeg.zip — upstream zip layout may have changed"
        exit 1
    fi
    cp "$FFMPEG_BIN" "$PROJECT_DIR/resources/bin/"

    # Sloppak conversion encodes .ogg with -c:a libvorbis. The BtbN GPL
    # build typically ships libvorbis, but verify so a future upstream
    # change doesn't silently degrade users to the built-in vorbis
    # encoder. The lib/sloppak_convert.py fallback is a safety net for
    # unbundled installs, not a license to ship a libvorbis-less binary.
    if ! "$PROJECT_DIR/resources/bin/ffmpeg.exe" -hide_banner -encoders 2>/dev/null | grep -wq libvorbis; then
        echo_error "bundled ffmpeg lacks libvorbis encoder. Sloppak conversion would fall back to the lower-quality built-in vorbis encoder on user machines."
        echo_error "BtbN's GPL build no longer includes --enable-libvorbis; pick a different release asset (or earlier build) that ships it."
        exit 1
    fi

    # ffprobe ships in the same BtbN zip as ffmpeg. demucs's audio loader
    # spawns ffprobe before ffmpeg to read stream metadata; without it the
    # loader dies with FileNotFoundError before ffmpeg is ever invoked.
    FFPROBE_BIN=$(find /tmp/ffmpeg -name 'ffprobe.exe' -type f | head -1)
    if [[ -z "$FFPROBE_BIN" ]]; then
        echo_error "ffprobe.exe not found after extracting /tmp/ffmpeg.zip — upstream zip layout may have changed"
        exit 1
    fi
    cp "$FFPROBE_BIN" "$PROJECT_DIR/resources/bin/"

    # vgmstream-cli
    echo "Downloading vgmstream-cli..."
    if ! download_with_retries \
        "https://github.com/vgmstream/vgmstream/releases/latest/download/vgmstream-win64.zip" \
        "/tmp/vgmstream.zip" \
        "vgmstream-cli"; then
        exit 1
    fi
    unzip -q /tmp/vgmstream.zip -d /tmp/vgmstream
    VGMSTREAM_EXE="/tmp/vgmstream/vgmstream-cli.exe"
    shopt -s nullglob
    VGMSTREAM_DLLS=("/tmp/vgmstream"/*.dll)
    shopt -u nullglob
    if [[ ! -f "$VGMSTREAM_EXE" ]]; then
        echo_error "vgmstream-cli.exe not found at $VGMSTREAM_EXE after extraction"
        exit 1
    fi
    if [[ ${#VGMSTREAM_DLLS[@]} -eq 0 ]]; then
        echo_error "Expected vgmstream DLLs in /tmp/vgmstream after extraction but found none"
        exit 1
    fi
    cp "$VGMSTREAM_EXE" "$PROJECT_DIR/resources/bin/"
    cp "${VGMSTREAM_DLLS[@]}" "$PROJECT_DIR/resources/bin/"
    
    # fluidsynth
    echo "Downloading fluidsynth..."
    # Reuse parse-build-config.py — Git Bash auto-converts the MSYS-style
    # $CONFIG path to native Windows form when it's passed as an arg, but
    # NOT when it's interpolated into an inline `python -c "...open('$CONFIG')..."`
    # string. The previous inline form silently failed with set -e on
    # Windows because Python opened a "/d/a/.../config.json" path that
    # doesn't exist as a literal Windows path.
    FS_URL=$(python3 "$SCRIPT_DIR/parse-build-config.py" "$CONFIG" .external.fluidsynth_windows.url 2>/dev/null || true)
    if [[ -n "$FS_URL" ]]; then
        if ! download_with_retries \
            "$FS_URL" \
            "/tmp/fluidsynth.zip" \
            "fluidsynth"; then
            exit 1
        fi
        unzip -q /tmp/fluidsynth.zip -d /tmp/fluidsynth
        FS_BIN=$(find /tmp/fluidsynth -name 'fluidsynth.exe' -type f | head -1)
        if [[ -n "$FS_BIN" ]]; then
            cp "$FS_BIN" "$PROJECT_DIR/resources/bin/"
            cp "$(dirname "$FS_BIN")"/*.dll "$PROJECT_DIR/resources/bin/" 2>/dev/null || true
        fi
    else
        echo_error "Fluidsynth URL not found in build config"
        exit 1
    fi
    
    echo_summary "All required Windows binaries downloaded and installed"
}

# Run the build
main "$@"
