#!/bin/sh
#
# Install MasterSCADA 4D runtime on the device.
#
# Remote (from the PLC):
#   curl -fsSL https://ftp.owen.ru/MasterSCADA_4D/masterscada.sh | sh
#   curl -fsSL https://ftp.owen.ru/MasterSCADA_4D/masterscada.sh | sh -s -- -c rc -v owen
#
# Local:
#   ./masterscada.sh [options]
#
# Catalog URLs follow mplc-update:
#   OWEN: https://ftp.owen.ru/MasterSCADA_4D/00_MPS_SOFT/RT/{Release|RC|Beta}/<machine>/...
#   MPS:  http://download.mps-soft.ru:8023/support/...
#
# All work is inside main() so `curl | sh` can parse the whole script
# before anything that might read stdin (install.sh) is executed.

main() {
    # === Settings ===
    DEFAULT_MACHINE="plc210rk"
    MACHINE=""
    CLI_MACHINE=""
    CHANNEL="release"
    VENDOR="mps"
    BASE_URL=""
    OWEN_HTTP="https://ftp.owen.ru/MasterSCADA_4D/00_MPS_SOFT"
    MPS_HTTP="http://download.mps-soft.ru:8023/support"
    USERNAME="GuestSupport"
    PASSWORD="guest"
    TARGET_DIR="${TMPDIR:-/tmp}/ms_download"
    LUCI_MOD_STATUS_URL="https://ftp.owen.ru/MasterSCADA_4D/10_Firmware/ipks/luci-mod-status_1.0-tano4.110.owen32.ms1.0_aarch64.ipk"
    LUCI_APP_MPLC_URL="https://ftp.owen.ru/MasterSCADA_4D/10_Firmware/ipks/luci-app-mplc_1.0-owen1.20.110.0_plc210rk.ipk"

    # === Flags ===
    DO_INSTALL=1       # 1 = perform installation, 0 = download only
    KEEP_ARTIFACTS=0   # 1 = keep TARGET_DIR after successful install

    usage() {
        cat <<EOF
Usage:
  curl -fsSL https://ftp.owen.ru/MasterSCADA_4D/masterscada.sh | sh
  curl -fsSL https://ftp.owen.ru/MasterSCADA_4D/masterscada.sh | sh -s -- [options]
  sh masterscada.sh [options]

Options:
  -c channel   Download channel: release | rc | beta (default: release)
  -v vendor    Download source: mps | owen (default: mps)
  -m machine   Machine name for URL (priority over fw_printenv board)
  -d    Download files only, do NOT run install.sh
  -k    Keep artifacts directory (${TARGET_DIR}) after successful installation
  -h    Show this help

Default behavior: download Release from MPS, run install.sh and
after SUCCESSFUL installation remove ${TARGET_DIR}.
EOF
    }

    # Parse options (with `curl | sh` pass them as: sh -s -- -c beta -v owen)
    while getopts "c:v:m:dkh" opt; do
        case "$opt" in
            c)
                CHANNEL=$(echo "$OPTARG" | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz')
                ;;
            v)
                VENDOR=$(echo "$OPTARG" | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz')
                ;;
            m)
                CLI_MACHINE="$OPTARG"
                ;;
            d) DO_INSTALL=0 ;;
            k) KEEP_ARTIFACTS=1 ;;
            h)
                usage
                exit 0
                ;;
            *)
                usage
                exit 1
                ;;
        esac
    done
    shift $((OPTIND - 1))

    case "$CHANNEL" in
        release|rc|beta) ;;
        *)
            echo "❌ Unknown channel: $CHANNEL"
            usage
            exit 1
            ;;
    esac

    case "$VENDOR" in
        mps|owen) ;;
        *)
            echo "❌ Unknown source: $VENDOR (expected mps or owen)"
            usage
            exit 1
            ;;
    esac

    if ! command -v curl >/dev/null 2>&1; then
        echo "❌ curl is required (used as: curl -fsSL https://ftp.owen.ru/MasterSCADA_4D/masterscada.sh | sh)"
        exit 1
    fi

    uboot_var() {
        if command -v fw_printenv >/dev/null 2>&1; then
            fw_printenv -n "$1" 2>/dev/null
        fi
    }

    plc_mod_name() {
        case "$1" in
            L) echo "PLC210-11" ;;
            M) echo "PLC210-12" ;;
            N) echo "PLC210-13" ;;
            O) echo "PLC210-14" ;;
            P) echo "PLC210-15" ;;
            R) echo "PLC210-4G" ;;
        esac
    }

    print_device_info() {
        board=$(uboot_var board)
        sn=$(uboot_var sn)
        plc_mod=$(uboot_var plc_mod)
        mod_name=$(plc_mod_name "$plc_mod")
        if [ -n "$mod_name" ]; then
            mod_label="$mod_name ($plc_mod)"
        else
            mod_label="${plc_mod:-н/д}"
        fi
        fw=""
        if [ -f /etc/RELEASE ]; then
            fw=$(sed -n '2p' /etc/RELEASE | tr -d '\r')
        fi

        runtime=""
        [ -e /etc/init.d/codesys ] && runtime="${runtime}${runtime:+ / }CODESYS"
        [ -e /etc/init.d/pastartup ] && runtime="${runtime}${runtime:+ / }Poligon"
        [ -e /etc/init.d/alta ] && runtime="${runtime}${runtime:+ / }ALTA"
        [ -e /etc/init.d/mplc4 ] && runtime="${runtime}${runtime:+ / }MasterSCADA"

        echo "📟 Прибор: ${board:-н/д}"
        echo "🔢 SN: ${sn:-н/д}"
        echo "🔩 Модификация: ${mod_label}"
        echo "💾 Версия прошивки: ${fw:-н/д}"
        echo "⚙️  Текущий рантайм: ${runtime:-не обнаружен}"
    }

    stop_other_runtimes() {
        # Leave mplc4 alone: install.sh stops/starts MasterSCADA itself.
        for svc in codesys pastartup alta; do
            init="/etc/init.d/$svc"
            [ -e "$init" ] || continue
            echo "🛑 Stopping $svc ($init stop)..."
            if "$init" stop; then
                echo "✅ $svc stopped"
            else
                echo "⚠️ $init stop failed"
            fi
            echo "🚫 Disabling $svc ($init disable)..."
            if "$init" disable; then
                echo "✅ $svc disabled"
            else
                echo "⚠️ $init disable failed"
            fi
        done
    }

    check_supported_device() {
        board=$(uboot_var board)
        if [ "$board" != "plc210rk" ]; then
            echo "❌ Устройство не поддерживается: ${board:-н/д}"
            echo "ℹ️ Скрипт можно запускать только на plc210rk"
            exit 1
        fi

        plc_mod=$(uboot_var plc_mod)
        mod_name=$(plc_mod_name "$plc_mod")
        case "$plc_mod" in
            L|M|N)
                echo "✅ Модификация поддерживается: ${mod_name} ($plc_mod)"
                ;;
            *)
                if [ -n "$mod_name" ]; then
                    echo "❌ Модификация не поддерживается: ${mod_name} ($plc_mod)"
                else
                    echo "❌ Модификация не поддерживается: ${plc_mod:-н/д}"
                fi
                echo "ℹ️ Разрешены только PLC210-11 (L), PLC210-12 (M), PLC210-13 (N)"
                echo "ℹ️ Остальные не поддержаны: PLC210-14 (O), PLC210-15 (P), PLC210-4G (R)"
                exit 1
                ;;
        esac
    }

    print_package_version() {
        if [ -f "$TARGET_DIR/version.txt" ]; then
            PACKAGE_VERSION=$(sed -n '1p' "$TARGET_DIR/version.txt" | tr -d '\r')
            if [ -n "$PACKAGE_VERSION" ]; then
                echo "📦 Версия MPLC к установке: $PACKAGE_VERSION"
            else
                echo "⚠️ version.txt is empty."
            fi
        else
            echo "⚠️ version.txt not found in $TARGET_DIR."
        fi
    }

    install_luci_plugins() {
        if ! command -v opkg >/dev/null 2>&1; then
            echo "❌ opkg is required to install LuCI plugins"
            exit 1
        fi

        echo "📥 Installing LuCI plugins..."
        for url in "$LUCI_MOD_STATUS_URL" "$LUCI_APP_MPLC_URL"; do
            pkg=${url##*/}
            echo "→ Downloading $pkg..."
            if ! curl -fL -o "$TARGET_DIR/$pkg" "$url"; then
                echo "❌ Failed to download $url"
                echo "ℹ️ Directory $TARGET_DIR is NOT removed for analysis."
                exit 1
            fi
            echo "📦 opkg install $pkg --force-reinstall --force-overwrite"
            if ! opkg install "$TARGET_DIR/$pkg" --force-reinstall --force-overwrite; then
                echo "❌ opkg install failed for $pkg"
                echo "ℹ️ Directory $TARGET_DIR is NOT removed for analysis."
                exit 1
            fi
        done
        echo "✅ LuCI plugins installed"
    }

    reboot_device() {
        echo "🔄 Устройство будет перезагружено через 5 секунд..."
        n=5
        while [ "$n" -gt 0 ]; do
            echo "⏳ Перезагрузка через $n..."
            sleep 1
            n=$((n - 1))
        done
        echo "🔁 Перезагрузка сейчас"
        sync
        reboot
    }

    url_decode() {
        echo "$1" | sed 's/%28/(/g; s/%29/)/g; s/%20/ /g'
    }

    url_encode() {
        echo "$1" | sed 's/(/%28/g; s/)/%29/g; s/ /%20/g'
    }

    join_url() {
        joined=${1%/}
        shift
        for part in "$@"; do
            part=$(url_encode "$part")
            joined="$joined/${part#/}"
        done
        echo "$joined/"
    }

    http_curl() {
        if [ "$VENDOR" = "mps" ]; then
            curl -u "$USERNAME:$PASSWORD" "$@"
        else
            curl "$@"
        fi
    }

    http_get() {
        http_curl -sfL "$1"
    }

    extract_hrefs() {
        grep -oE 'href="[^"]+"' | sed 's/^href="//; s/"$//'
    }

    list_remote_dirs() {
        http_get "${1%/}/" | extract_hrefs | while IFS= read -r href; do
            case "$href" in
                */*)
                    ;;
                *)
                    continue
                    ;;
            esac
            case "$href" in
                http://*|https://*|/*|\#*|../|./|\?*)
                    continue
                    ;;
            esac
            case "$href" in
                */)
                    name=$(url_decode "${href%/}")
                    [ -n "$name" ] && echo "$name"
                    ;;
            esac
        done
    }

    list_remote_files() {
        http_get "${1%/}/" | extract_hrefs | while IFS= read -r href; do
            case "$href" in
                */*|*\?*|\#*|http://*|https://*)
                    continue
                    ;;
            esac
            [ -n "$href" ] && echo "$href"
        done | sort -u
    }

    version_from_txt() {
        echo "$1" | sed -n '1p' | tr -d '\r' | sed 's/(.*//'
    }

    version_from_folder() {
        echo "$1" | sed 's/(.*//'
    }

    is_version_folder() {
        echo "$1" | grep -qE '^[0-9]+(\.[0-9]+)*\([^)]+\)$'
    }

    # True if dotted numeric $1 > $2.
    version_gt() {
        va=$1
        vb=$2
        old_ifs=$IFS
        IFS=.
        set -- $va
        a1=${1:-0} a2=${2:-0} a3=${3:-0} a4=${4:-0}
        set -- $vb
        b1=${1:-0} b2=${2:-0} b3=${3:-0} b4=${4:-0}
        IFS=$old_ifs
        [ "$a1" -gt "$b1" ] && return 0
        [ "$a1" -lt "$b1" ] && return 1
        [ "$a2" -gt "$b2" ] && return 0
        [ "$a2" -lt "$b2" ] && return 1
        [ "$a3" -gt "$b3" ] && return 0
        [ "$a3" -lt "$b3" ] && return 1
        [ "$a4" -gt "$b4" ] && return 0
        return 1
    }

    owen_channel_folder() {
        case "$CHANNEL" in
            release) echo "Release" ;;
            rc) echo "RC" ;;
            beta) echo "Beta" ;;
        esac
    }

    resolve_owen_url() {
        root=$(join_url "$OWEN_HTTP" "RT" "$(owen_channel_folder)" "$MACHINE")
        best_ver=""
        best_url=""

        while IFS= read -r series; do
            [ -z "$series" ] && continue
            series_url=$(join_url "$root" "$series")
            while IFS= read -r folder; do
                [ -z "$folder" ] && continue
                is_version_folder "$folder" || continue
                ver=$(version_from_folder "$folder")
                if [ -z "$best_ver" ] || version_gt "$ver" "$best_ver"; then
                    best_ver=$ver
                    best_url=$(join_url "$series_url" "$folder")
                fi
            done <<EOF
$(list_remote_dirs "$series_url")
EOF
        done <<EOF
$(list_remote_dirs "$root")
EOF

        if [ -z "$best_url" ]; then
            echo "❌ No OWEN $CHANNEL drop found for $MACHINE under $root"
            exit 1
        fi
        BASE_URL=$best_url
    }

    resolve_mps_url() {
        case "$CHANNEL" in
            release)
                BASE_URL=$(join_url "$MPS_HTTP" "Updates" "Installation" "MasterSCADA_4D" "RunTime" "$MACHINE")
                return 0
                ;;
            beta)
                BASE_URL=$(join_url "$MPS_HTTP" "Dev" "MasterSCADA4D" "RT" "Beta" "$MACHINE")
                return 0
                ;;
        esac

        rt_url=$(join_url "$MPS_HTTP" "Dev" "MasterSCADA4D" "RT")
        best_ver=""
        best_url=""

        while IFS= read -r series; do
            [ -z "$series" ] && continue
            case "$series" in
                *.RC) ;;
                *) continue ;;
            esac
            drop_url=$(join_url "$rt_url" "$series" "$MACHINE")
            ver_txt=$(http_get "${drop_url}version.txt") || continue
            ver=$(version_from_txt "$ver_txt")
            [ -z "$ver" ] && continue
            if [ -z "$best_ver" ] || version_gt "$ver" "$best_ver"; then
                best_ver=$ver
                best_url=$drop_url
            fi
        done <<EOF
$(list_remote_dirs "$rt_url")
EOF

        if [ -z "$best_url" ]; then
            echo "❌ No MPS RC drop found for $MACHINE under $rt_url"
            exit 1
        fi
        BASE_URL=$best_url
    }

    print_device_info

    # Determine machine name:
    # 1) CLI (-m) has highest priority
    # 2) U-Boot board via fw_printenv
    # 3) DEFAULT_MACHINE fallback
    if [ -n "$CLI_MACHINE" ]; then
        MACHINE="$CLI_MACHINE"
        echo "ℹ️ Using machine from CLI: $MACHINE"
    else
        MACHINE=$(uboot_var board)
        if [ -z "$MACHINE" ]; then
            MACHINE="$DEFAULT_MACHINE"
            echo "ℹ️ fw_printenv board is unavailable, fallback to machine: $MACHINE"
        else
            echo "ℹ️  Detected machine from U-Boot: $MACHINE"
        fi
    fi

    check_supported_device

    echo "📡 Источник: $VENDOR"
    echo "📦 Канал: $CHANNEL"

    if [ "$VENDOR" = "owen" ]; then
        resolve_owen_url
    else
        resolve_mps_url
    fi

    echo "🔗 Origin: ${BASE_URL%/}"

    # Fresh work dir: leftover tmp.tar from an interrupted install.sh
    # makes BusyBox gzip fail with: gzip: can't open 'tmp.tar': File exists
    if [ -e "$TARGET_DIR" ]; then
        echo "🧹 Cleaning previous artifacts: $TARGET_DIR"
        rm -rf "$TARGET_DIR"
    fi
    mkdir -p "$TARGET_DIR" || {
        echo "❌ Failed to create directory $TARGET_DIR"
        exit 1
    }

    echo "📥 Fetching file list from $BASE_URL ..."

    FILE_LIST=$(list_remote_files "$BASE_URL")

    if [ -z "$FILE_LIST" ]; then
        echo "❌ Failed to get file list (empty list)."
        exit 1
    fi

    echo "📄 Files found:"
    echo "$FILE_LIST"

    echo "📥 Starting download into $TARGET_DIR"

    while IFS= read -r FILE; do
        [ -z "$FILE" ] && continue
        echo "→ Downloading $FILE..."
        http_curl -fL -o "$TARGET_DIR/$FILE" "${BASE_URL%/}/$(url_encode "$FILE")"
        if [ $? -ne 0 ]; then
            echo "⚠️ Error downloading $FILE — skipping."
        fi
    done <<EOF
$FILE_LIST
EOF

    echo "✅ Download attempt for all files finished."

    print_package_version

    # Download-only mode
    if [ "$DO_INSTALL" -eq 0 ]; then
        echo "ℹ️ Download-only mode (-d): install.sh will NOT be executed."
        echo "📂 Artifacts are in: $TARGET_DIR"
        exit 0
    fi

    stop_other_runtimes

    # Change to target directory
    cd "$TARGET_DIR" || {
        echo "❌ Failed to change directory to $TARGET_DIR"
        exit 1
    }

    # Check for install.sh
    if [ ! -f install.sh ]; then
        echo "❌ install.sh not found in $TARGET_DIR — installation cannot continue."
        ls -l
        echo "ℹ️ Artifacts are kept for analysis."
        exit 1
    fi

    # install.sh copies archives to tmp.tar.gz and runs `gzip -d` (BusyBox
    # tar on plc210rk has no gzip). gzip will not overwrite an existing tmp.tar.
    rm -f tmp.tar tmp.tar.gz
    chmod +x install.sh
    echo "🚀 Running install.sh..."
    ./install.sh
    INSTALL_RC=$?

    if [ $INSTALL_RC -ne 0 ]; then
        echo "❌ install.sh exited with error code $INSTALL_RC."
        echo "ℹ️ Directory $TARGET_DIR is NOT removed for analysis."
        exit $INSTALL_RC
    fi

    echo "✅ Installation completed successfully."

    install_luci_plugins

    if [ "$KEEP_ARTIFACTS" -eq 1 ]; then
        echo "ℹ️ -k flag set: keeping artifacts directory: $TARGET_DIR"
    else
        echo "🧹 Removing artifacts directory: $TARGET_DIR"
        rm -rf "$TARGET_DIR"
    fi

    reboot_device
    exit 0
}

main "$@"
