package com.unibuy.smartdevice.tools;

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;

import com.unibuy.smartdevice.exception.ErrorCode;
import com.unibuy.smartdevice.exception.Logs;
import com.unibuy.smartdevice.external.mqtt.MqttManager;

import java.io.DataOutputStream;
import java.io.File;

/**
 * 負責遠端 APK 的背景下載與 Root 靜默覆蓋安裝管理。
 *
 * <p>下載改用系統 {@link DownloadManager}，解決舊版「自架 Thread 下載」的兩個致命傷：
 * <ul>
 *   <li>由系統程序負責下載，<b>App 重啟/被殺不會中斷</b>，並支援自動續傳與重試；</li>
 *   <li>下載期間定期回報<b>進度 ACK（result="progress"）</b>，後台不視為終態，看得到實際進度。</li>
 * </ul>
 *
 * <p>回報語意修正：舊版一「開始下載」就回 {@code result=success}，導致後台立刻把指令標成完成。
 * 新版只在<b>最終成敗</b>時回 {@code success}/{@code failed}；下載/安裝過程一律走 {@code progress}。
 * 由於 {@code pm install -r} 成功後本程序會被新版替換，<b>「安裝成功」的終態 ACK 改由新版啟動後
 * 透過 {@link #resumeIfPending(Context)} 比對版本回報</b>，確保後台狀態與機台實際版本一致。
 */
public class OtaManager {
    private static final Logs logs = new Logs(OtaManager.class);

    private static final String PREFS = "ota_state";
    private static final String KEY_DOWNLOAD_ID = "download_id";
    private static final String KEY_CMD_ID = "cmd_id";
    private static final String KEY_VERSION = "version";
    private static final String KEY_PHASE = "phase";
    private static final String KEY_INSTALL_ATTEMPTS = "install_attempts";

    private static final String PHASE_DOWNLOADING = "downloading";
    private static final String PHASE_READY = "ready";       // 下載完成，等待機台閒置才安裝
    private static final String PHASE_INSTALLING = "installing";

    /** 非終態回報結果值；後台 ProcessCommandAck 對 update_app 的此值只更新備註、保持 pending。 */
    private static final String RESULT_PROGRESS = "progress";
    private static final String RESULT_SUCCESS = "success";
    private static final String RESULT_FAILED = "failed";

    private static final long PROGRESS_REPORT_INTERVAL_MS = 5000;
    private static final int MAX_INSTALL_ATTEMPTS = 2;
    private static final String APK_FILE_NAME = "update_ota.apk";

    // 安裝閘門：下載完成後，需「不忙碌且閒置達門檻」才執行安裝（安裝會替換 App 並重啟，
    // 不可在有人操作/交易中進行）。忙碌時持續等待、定期重試。
    private static final int INSTALL_REQUIRED_IDLE_SECONDS = 60;
    private static final long IDLE_CHECK_INTERVAL_MS = 30000;
    private static final int WAIT_NOTIFY_EVERY_N_CHECKS = 10; // 約每 5 分鐘回報一次等待中

    // 防止重複監看同一筆下載（resume 會在每次 MQTT 連線時被呼叫）
    private static volatile long monitoringDownloadId = -1;
    private static volatile boolean awaitingIdleInstall = false;
    private static BroadcastReceiver completionReceiver;

    // 防重入：MQTT 可能重複投遞同一則 update_app（機台若有多條堆疊連線會收到 N 次）。
    // 同一 cmdId 已在進行中就忽略重複，避免 N 個並行 startUpdate 經 cancelExisting() 互相取消下載。
    // 不在 clearState 清除（cancelExisting 會於 startUpdate 開頭呼叫 clearState），改由新 cmdId 覆蓋 + 15 分鐘保險過期。
    private static volatile String activeOtaCmdId = null;
    private static volatile long activeOtaStartMs = 0L;
    private static final long OTA_REENTRY_WINDOW_MS = 15 * 60 * 1000L;

    /** 相容舊呼叫端：未帶版本資訊。 */
    public static void startUpdate(Context context, String apkUrl, String cmdId) {
        startUpdate(context, apkUrl, cmdId, null);
    }

    /**
     * 啟動遠端 APP OTA 更新。
     *
     * @param context        應用程式 Context
     * @param apkUrl         新版 APK 的下載位址
     * @param cmdId          MQTT 指令 ID，用於回報執行結果
     * @param expectedVersion 目標版本名（如 "21_02_9_R"），安裝後用於比對確認；可為 null
     */
    public static void startUpdate(Context context, String apkUrl, String cmdId, String expectedVersion) {
        Context appContext = context.getApplicationContext();
        MqttManager mqttManager = MqttManager.getInstance();

        // 防重入：同一 cmdId 的重複投遞直接忽略，避免並行 startUpdate 互相 cancelExisting() 取消下載。
        synchronized (OtaManager.class) {
            long now = System.currentTimeMillis();
            if (cmdId != null && cmdId.equals(activeOtaCmdId) && (now - activeOtaStartMs) < OTA_REENTRY_WINDOW_MS) {
                logs.info("OTA duplicate command ignored (cmdId=" + cmdId + "); already in progress.");
                return;
            }
            activeOtaCmdId = cmdId;
            activeOtaStartMs = now;
        }

        try {
            logs.info("Starting OTA update process. URL: " + apkUrl + " (cmdId: " + cmdId
                    + ", version: " + expectedVersion + ")");

            // 先清掉前一筆殘留下載，避免重複下載/狀態混淆
            cancelExisting(appContext);

            DownloadManager dm = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
            if (dm == null) {
                // 極少數 ROM 停用 DownloadManager：退回舊版直連下載（不可續傳，僅作保險）
                logs.info("DownloadManager unavailable, falling back to legacy direct download.");
                legacyDownloadAndInstall(appContext, apkUrl, cmdId, expectedVersion);
                return;
            }

            File target = new File(appContext.getExternalFilesDir(null), APK_FILE_NAME);
            if (target.exists()) {
                target.delete();
            }

            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
            request.setTitle("OTA Update");
            request.setDescription("Downloading APK " + (expectedVersion != null ? expectedVersion : ""));
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
            // 下載到 App 私有外部目錄，su (root) 可讀取進行安裝，免 WRITE_EXTERNAL_STORAGE 權限
            request.setDestinationInExternalFilesDir(appContext, null, APK_FILE_NAME);
            request.setAllowedOverMetered(true);
            request.setAllowedOverRoaming(true);

            long downloadId = dm.enqueue(request);

            // 持久化狀態：App 重啟後可由 resumeIfPending 接手（續傳監看 / 安裝 / 回報）
            prefs(appContext).edit()
                    .putLong(KEY_DOWNLOAD_ID, downloadId)
                    .putString(KEY_CMD_ID, cmdId)
                    .putString(KEY_VERSION, expectedVersion)
                    .putString(KEY_PHASE, PHASE_DOWNLOADING)
                    .putInt(KEY_INSTALL_ATTEMPTS, 0)
                    .apply();

            mqttManager.publishCommandAck(cmdId, RESULT_PROGRESS, "Downloading update APK...");

            registerCompletionReceiver(appContext);
            startProgressMonitor(appContext, downloadId, cmdId);

        } catch (Exception e) {
            logs.error(ErrorCode.UNKNOWN_ERROR, "OTA Update failed to start: " + e.getMessage());
            mqttManager.publishCommandAck(cmdId, RESULT_FAILED, "OTA Update failed: " + e.getMessage());
            clearState(appContext);
        }
    }

    /**
     * App 啟動且 MQTT 連線後呼叫：若有未完成的 OTA，依目前狀態接手。
     * <ul>
     *   <li>安裝階段（phase=installing）：比對實際版本 → 回報 success；不符則重試安裝或回報 failed。</li>
     *   <li>下載階段：依 DownloadManager 狀態 → 已完成則安裝；進行中則重掛監看；失敗則回報 failed。</li>
     * </ul>
     * 本方法可重入：無待辦時直接返回，重複呼叫不會重複監看或重複回報。
     */
    public static void resumeIfPending(Context context) {
        Context appContext = context.getApplicationContext();
        SharedPreferences prefs = prefs(appContext);
        long downloadId = prefs.getLong(KEY_DOWNLOAD_ID, -1);
        if (downloadId < 0) {
            return;
        }
        String cmdId = prefs.getString(KEY_CMD_ID, null);
        String phase = prefs.getString(KEY_PHASE, PHASE_DOWNLOADING);

        // 安裝階段：新版已（應）啟動，比對版本回報終態
        if (PHASE_INSTALLING.equals(phase)) {
            confirmAfterInstall(appContext, cmdId);
            return;
        }

        // 已下載完成、等待閒置安裝：重啟後重新掛上閒置等待迴圈（APK 仍在本機）
        if (PHASE_READY.equals(phase)) {
            File apkFile = new File(appContext.getExternalFilesDir(null), APK_FILE_NAME);
            if (apkFile.exists()) {
                scheduleInstallWhenIdle(appContext, apkFile, cmdId);
            } else {
                if (cmdId != null) {
                    MqttManager.getInstance().publishCommandAck(cmdId, RESULT_FAILED,
                            "Downloaded APK missing before install.");
                }
                clearState(appContext);
            }
            return;
        }

        DownloadManager dm = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
        if (dm == null) {
            return;
        }
        int status = queryStatus(dm, downloadId);
        logs.info("OTA resume: pending download " + downloadId + " (cmdId: " + cmdId + ") status=" + status);

        switch (status) {
            case DownloadManager.STATUS_SUCCESSFUL:
                handleDownloadComplete(appContext, cmdId);
                break;
            case DownloadManager.STATUS_RUNNING:
            case DownloadManager.STATUS_PENDING:
            case DownloadManager.STATUS_PAUSED:
                registerCompletionReceiver(appContext);
                startProgressMonitor(appContext, downloadId, cmdId);
                break;
            case DownloadManager.STATUS_FAILED:
            default:
                if (cmdId != null) {
                    MqttManager.getInstance().publishCommandAck(cmdId, RESULT_FAILED,
                            "OTA download failed or record missing after restart.");
                }
                clearState(appContext);
                break;
        }
    }

    // ====================================================================
    // 內部流程
    // ====================================================================

    /** 註冊下載完成廣播（runtime）。重複呼叫會先解除舊的，避免洩漏。 */
    private static synchronized void registerCompletionReceiver(Context appContext) {
        unregisterCompletionReceiver(appContext);
        completionReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context ctx, Intent intent) {
                long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                long expected = prefs(appContext).getLong(KEY_DOWNLOAD_ID, -1);
                if (id >= 0 && id == expected) {
                    String cmdId = prefs(appContext).getString(KEY_CMD_ID, null);
                    handleDownloadComplete(appContext, cmdId);
                }
            }
        };
        IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            appContext.registerReceiver(completionReceiver, filter, Context.RECEIVER_EXPORTED);
        } else {
            appContext.registerReceiver(completionReceiver, filter);
        }
    }

    private static synchronized void unregisterCompletionReceiver(Context appContext) {
        if (completionReceiver != null) {
            try {
                appContext.unregisterReceiver(completionReceiver);
            } catch (Exception ignored) {
            }
            completionReceiver = null;
        }
    }

    /** 背景輪詢下載進度並節流回報，下載結束（非進行中）即停止。 */
    private static void startProgressMonitor(Context appContext, long downloadId, String cmdId) {
        if (monitoringDownloadId == downloadId) {
            return; // 已在監看，避免重複起執行緒
        }
        monitoringDownloadId = downloadId;
        new Thread(() -> {
            DownloadManager dm = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
            if (dm == null) {
                monitoringDownloadId = -1;
                return;
            }
            int lastPercent = -1;
            try {
                while (true) {
                    Thread.sleep(PROGRESS_REPORT_INTERVAL_MS);
                    if (prefs(appContext).getLong(KEY_DOWNLOAD_ID, -1) != downloadId) {
                        break; // 狀態已被清除或換成新下載
                    }
                    int[] info = queryProgress(dm, downloadId); // {status, percent}
                    int status = info[0];
                    int percent = info[1];
                    if (status == DownloadManager.STATUS_RUNNING || status == DownloadManager.STATUS_PENDING
                            || status == DownloadManager.STATUS_PAUSED) {
                        if (percent >= 0 && percent != lastPercent) {
                            lastPercent = percent;
                            MqttManager.getInstance().publishCommandAck(cmdId, RESULT_PROGRESS,
                                    "Downloading update APK... " + percent + "%");
                        }
                    } else {
                        break; // 成功/失敗皆交由完成廣播或 resume 處理
                    }
                }
            } catch (InterruptedException ignored) {
            } finally {
                monitoringDownloadId = -1;
            }
        }, "ota-progress-monitor").start();
    }

    /** 下載完成：標記為「待安裝」並等待機台閒置後才安裝。 */
    private static synchronized void handleDownloadComplete(Context appContext, String cmdId) {
        SharedPreferences prefs = prefs(appContext);
        // 已進入待安裝/安裝階段就不重複觸發（完成廣播 + resume 可能同時到達）
        String currentPhase = prefs.getString(KEY_PHASE, PHASE_DOWNLOADING);
        if (PHASE_READY.equals(currentPhase) || PHASE_INSTALLING.equals(currentPhase)) {
            return;
        }
        long downloadId = prefs.getLong(KEY_DOWNLOAD_ID, -1);
        DownloadManager dm = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
        if (dm == null || queryStatus(dm, downloadId) != DownloadManager.STATUS_SUCCESSFUL) {
            if (cmdId != null) {
                MqttManager.getInstance().publishCommandAck(cmdId, RESULT_FAILED, "OTA download did not complete.");
            }
            clearState(appContext);
            return;
        }
        unregisterCompletionReceiver(appContext);
        File apkFile = new File(appContext.getExternalFilesDir(null), APK_FILE_NAME);
        if (!apkFile.exists()) {
            if (cmdId != null) {
                MqttManager.getInstance().publishCommandAck(cmdId, RESULT_FAILED, "Downloaded APK file missing.");
            }
            clearState(appContext);
            return;
        }
        logs.info("APK downloaded successfully to: " + apkFile.getAbsolutePath());
        prefs.edit().putString(KEY_PHASE, PHASE_READY).apply();
        scheduleInstallWhenIdle(appContext, apkFile, cmdId);
    }

    /**
     * 等待機台閒置後才安裝：背景輪詢，僅在「不忙碌（無對話框/NFC 交易）且閒置達門檻」時
     * 觸發安裝；忙碌則持續等待並定期回報「等待閒置中」。可重入：已在等待則直接返回。
     */
    private static void scheduleInstallWhenIdle(Context appContext, File apkFile, String cmdId) {
        if (awaitingIdleInstall) {
            return;
        }
        awaitingIdleInstall = true;
        MqttManager.getInstance().publishCommandAck(cmdId, RESULT_PROGRESS,
                "Update downloaded, waiting for machine idle to install...");
        new Thread(() -> {
            int checks = 0;
            try {
                while (true) {
                    // 狀態已被清除（如安裝完成/失敗）或不再是待安裝階段 → 結束等待
                    if (!PHASE_READY.equals(prefs(appContext).getString(KEY_PHASE, ""))) {
                        break;
                    }
                    String busyReason = com.unibuy.smartdevice.MyApp.getInstance().getBusyReason();
                    long idle = com.unibuy.smartdevice.MyApp.getInstance().getIdleSeconds();
                    if (busyReason.isEmpty() && idle >= INSTALL_REQUIRED_IDLE_SECONDS) {
                        logs.info("OTA install gate passed (idle " + idle + "s). Installing now.");
                        installNow(appContext, apkFile, cmdId);
                        break;
                    }
                    if (checks % WAIT_NOTIFY_EVERY_N_CHECKS == 0) {
                        String detail = !busyReason.isEmpty() ? busyReason : ("idle " + idle + "s");
                        logs.info("OTA install deferred, machine in use (" + detail + "). Waiting...");
                        MqttManager.getInstance().publishCommandAck(cmdId, RESULT_PROGRESS,
                                "Downloaded, waiting for machine idle to install (" + detail + ")...");
                    }
                    checks++;
                    Thread.sleep(IDLE_CHECK_INTERVAL_MS);
                }
            } catch (InterruptedException ignored) {
            } finally {
                awaitingIdleInstall = false;
            }
        }, "ota-install-idle-gate").start();
    }

    /**
     * 執行靜默安裝。安裝成功（exit 0）後本程序即將被新版替換，終態 success 由
     * {@link #confirmAfterInstall} 於新版啟動後回報；安裝指令失敗則就地回報 failed。
     */
    private static void installNow(Context appContext, File apkFile, String cmdId) {
        prefs(appContext).edit().putString(KEY_PHASE, PHASE_INSTALLING).apply();
        MqttManager.getInstance().publishCommandAck(cmdId, RESULT_PROGRESS, "Installing update APK...");
        boolean triggered = silentInstall(apkFile.getAbsolutePath());
        if (!triggered) {
            // pm install 回傳非 0（無 root / 簽章不符等），此時 App 未被替換，可直接回報失敗
            logs.error(ErrorCode.UNKNOWN_ERROR, "Silent install command returned non-zero status.");
            if (cmdId != null) {
                MqttManager.getInstance().publishCommandAck(cmdId, RESULT_FAILED,
                        "Silent install failed (root/signature?).");
            }
            clearState(appContext);
        } else {
            logs.info("Silent installation triggered. Awaiting package replacement & restart for confirmation.");
        }
    }

    /** 新版啟動後比對實際版本，回報最終成敗。 */
    private static void confirmAfterInstall(Context appContext, String cmdId) {
        SharedPreferences prefs = prefs(appContext);
        String expected = prefs.getString(KEY_VERSION, null);
        String current = currentVersionName(appContext);
        logs.info("OTA confirm after install. expected=" + expected + " current=" + current);

        boolean ok = (expected == null) || expected.equals(current);
        if (ok) {
            if (cmdId != null) {
                MqttManager.getInstance().publishCommandAck(cmdId, RESULT_SUCCESS,
                        "App updated to " + (current != null ? current : "(unknown)") + ".");
            }
            clearState(appContext);
            return;
        }

        // 版本未變：可能上次安裝被中途打斷。APK 還在就重試一次，否則回報失敗。
        int attempts = prefs.getInt(KEY_INSTALL_ATTEMPTS, 0);
        File apkFile = new File(appContext.getExternalFilesDir(null), APK_FILE_NAME);
        if (apkFile.exists() && attempts < MAX_INSTALL_ATTEMPTS) {
            prefs.edit().putInt(KEY_INSTALL_ATTEMPTS, attempts + 1).apply();
            logs.info("OTA confirm: version mismatch, retrying install (attempt " + (attempts + 1) + ").");
            installNow(appContext, apkFile, cmdId);
        } else {
            if (cmdId != null) {
                MqttManager.getInstance().publishCommandAck(cmdId, RESULT_FAILED,
                        "Install did not result in expected version " + expected
                                + " (current " + current + ").");
            }
            clearState(appContext);
        }
    }

    // ====================================================================
    // 工具
    // ====================================================================

    private static SharedPreferences prefs(Context appContext) {
        return appContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
    }

    private static void clearState(Context appContext) {
        prefs(appContext).edit().clear().apply();
        monitoringDownloadId = -1;
        awaitingIdleInstall = false;
        unregisterCompletionReceiver(appContext);
    }

    /** 取消並移除先前殘留的下載任務。 */
    private static void cancelExisting(Context appContext) {
        long prev = prefs(appContext).getLong(KEY_DOWNLOAD_ID, -1);
        if (prev >= 0) {
            DownloadManager dm = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
            if (dm != null) {
                try {
                    dm.remove(prev);
                } catch (Exception ignored) {
                }
            }
        }
        clearState(appContext);
    }

    private static int queryStatus(DownloadManager dm, long downloadId) {
        return queryProgress(dm, downloadId)[0];
    }

    /** @return {status, percent}；查無紀錄回 {STATUS_FAILED, -1}。 */
    private static int[] queryProgress(DownloadManager dm, long downloadId) {
        Cursor c = null;
        try {
            c = dm.query(new DownloadManager.Query().setFilterById(downloadId));
            if (c != null && c.moveToFirst()) {
                int status = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
                long done = c.getLong(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                long total = c.getLong(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                int percent = (total > 0) ? (int) (done * 100 / total) : -1;
                return new int[]{status, percent};
            }
        } catch (Exception e) {
            logs.info("OTA queryProgress error: " + e.getMessage());
        } finally {
            if (c != null) c.close();
        }
        return new int[]{DownloadManager.STATUS_FAILED, -1};
    }

    private static String currentVersionName(Context appContext) {
        try {
            return appContext.getPackageManager()
                    .getPackageInfo(appContext.getPackageName(), 0).versionName;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 退回方案：DownloadManager 不可用時的直連下載（背景 Thread，不可續傳）。
     * 下載完成後一樣走 {@link #installNow} 與安裝後版本確認流程。
     */
    private static void legacyDownloadAndInstall(Context appContext, String apkUrl, String cmdId, String expectedVersion) {
        prefs(appContext).edit()
                .putLong(KEY_DOWNLOAD_ID, 0) // 佔位，使 resume 視為有待辦（安裝階段）
                .putString(KEY_CMD_ID, cmdId)
                .putString(KEY_VERSION, expectedVersion)
                .putString(KEY_PHASE, PHASE_DOWNLOADING)
                .putInt(KEY_INSTALL_ATTEMPTS, 0)
                .apply();
        MqttManager.getInstance().publishCommandAck(cmdId, RESULT_PROGRESS, "Downloading update APK...");
        new Thread(() -> {
            try {
                File apkFile = new File(appContext.getExternalFilesDir(null), APK_FILE_NAME);
                if (apkFile.exists()) apkFile.delete();

                java.net.HttpURLConnection conn =
                        (java.net.HttpURLConnection) new java.net.URL(apkUrl).openConnection();
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(120000);
                conn.connect();
                if (conn.getResponseCode() != java.net.HttpURLConnection.HTTP_OK) {
                    throw new Exception("Server returned HTTP " + conn.getResponseCode());
                }
                try (java.io.InputStream in = new java.io.BufferedInputStream(conn.getInputStream());
                     java.io.FileOutputStream out = new java.io.FileOutputStream(apkFile)) {
                    byte[] buf = new byte[8192];
                    int n;
                    while ((n = in.read(buf)) != -1) {
                        out.write(buf, 0, n);
                    }
                    out.flush();
                } finally {
                    conn.disconnect();
                }
                logs.info("APK downloaded (legacy) to: " + apkFile.getAbsolutePath());
                prefs(appContext).edit().putString(KEY_PHASE, PHASE_READY).apply();
                scheduleInstallWhenIdle(appContext, apkFile, cmdId);
            } catch (Exception e) {
                logs.error(ErrorCode.UNKNOWN_ERROR, "Legacy OTA download failed: " + e.getMessage());
                MqttManager.getInstance().publishCommandAck(cmdId, RESULT_FAILED,
                        "OTA Update failed: " + e.getMessage());
                clearState(appContext);
            }
        }, "ota-legacy-download").start();
    }

    /**
     * 調用 Root 權限執行 pm install -r 靜默安裝。
     *
     * @return 安裝指令是否成功觸發（exit code 0）
     */
    private static boolean silentInstall(String apkPath) {
        Process process = null;
        DataOutputStream os = null;
        try {
            logs.info("Executing silent install command via su: pm install -r -d " + apkPath);
            process = Runtime.getRuntime().exec("su");
            os = new DataOutputStream(process.getOutputStream());
            os.writeBytes("pm install -r -d " + apkPath + "\n");
            os.writeBytes("exit\n");
            os.flush();

            int exitValue = process.waitFor();
            logs.info("Silent install process exit value: " + exitValue);
            return exitValue == 0;
        } catch (Exception e) {
            logs.info("Failed to execute silent install su command: " + e.getMessage());
            logs.warning(e);
            return false;
        } finally {
            try {
                if (os != null) os.close();
                if (process != null) process.destroy();
            } catch (Exception ignored) {
            }
        }
    }
}
