# /*
#  * Copyright (C) 2020, KylinSoft Co., Ltd.
#  *
#  * This program is free software: you can redistribute it and/or modify
#  * it under the terms of the GNU General Public License as published by
#  * the Free Software Foundation, either version 3 of the License, or
#  * (at your option) any later version.
#  *
#  * This program is distributed in the hope that it will be useful,
#  * but WITHOUT ANY WARRANTY; without even the implied warranty of
#  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  * GNU General Public License for more details.
#  *
#  * You should have received a copy of the GNU General Public License
#  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
#  */

#!/usr/bin/env python

import gi
import shutil
import traceback
import logging
import os,subprocess
gi.require_version('OSTree', '1.0')
from gi.repository import GLib, Gio, OSTree,GObject
from SystemUpdater.Core.errors import *
from SystemUpdater.Core.enums import *
from SystemUpdater.self_upgrade.hooks import _execute_command
from SystemUpdater.configs import G_settings
from SystemUpdater.constants import OFFLINE_REPO_POST,KYLIN_OS_NAME,CURRENT_PROGRESS
from SystemUpdater.modules.i18n import _
import SystemUpdater.self_upgrade as self_upgrade

RETRY_LIMIT = 5
DEFAULT_REPO_NAME = "kylin"

class BackendBaseOstree(GObject.GObject):    
    __gsignals__ = {
                    "transaction-done": (GObject.SignalFlags.RUN_FIRST,
                                         None,
                                         (GObject.TYPE_PYOBJECT,)),
                    "download-info": (GObject.SIGNAL_RUN_FIRST,
                                GObject.TYPE_NONE,
                                (   GObject.TYPE_INT,
                                    GObject.TYPE_INT,
                                    GObject.TYPE_ULONG,
                                    GObject.TYPE_ULONG,
                                    GObject.TYPE_ULONG
                                )),
                    "deploy-progress": (GObject.SIGNAL_RUN_FIRST,
                                GObject.TYPE_NONE,
                                (   GObject.TYPE_INT,
                                )),
                    }
    
    def __init__(self,sysroot_path=None,osname=KYLIN_OS_NAME):
        GObject.GObject.__init__(self)
        self._async_progress = None
        if sysroot_path:
            sysroot_path = Gio.File.new_for_path(sysroot_path)
            
        self.sysroot = OSTree.Sysroot.new(sysroot_path)
        self.sysroot.set_mount_namespace_in_use()
        self.sysroot.initialize()
        self.sysroot.load()

        booted_deployment = self.sysroot.get_booted_deployment()
        if booted_deployment:
            self.osname = booted_deployment.get_osname()
        else:
            logging.warning("Booted deployment is None and use to defalut os name(%s)...",osname)
            self.osname = osname

        self.cur_deployment = self.sysroot.get_merge_deployment(self.osname)
        if not self.cur_deployment:
            logging.error("Now system deployment is None...")
            
        self.origin = self.cur_deployment.get_origin()
        self.download_size = 0
        # 加载插件机制
        # 获取当前运行程序的插件目录位置
        # self.plugins_dir = os.path.join(os.getcwd(),"SystemUpdater/plugins")
        # # 基础插件 所有插件的位置
        # test = os.path.join(self.plugins_dir,"base")
        # self.plugin_base = PluginBase(package='SystemUpdater.plugins',searchpath=[os.path.join(self.plugins_dir,"base")])

    def run(self,trans=None):
        logging.info("Processing transaction")
        # self._run_transaction_idle(trans)
        GLib.idle_add(self._run_transaction_idle,trans)

    def _run_transaction_idle(self,trans):
        """Run the transaction"""
        try:
            self._async_progress = OSTree.AsyncProgress.new()
            self._async_progress.connect("changed",self._on_async_progress)
            self._try_lock()
            self._run_transaction(self.sysroot,trans)
        except GLib.Error as error:
            logging.error(str(error))
            trans.success = False
            self._parse_ostree_error(error,trans)
        except UpdateBaseError as excep:
            trans.success = False
            trans.error = excep.code
        except Exception as excep:
            logging.error(str(excep))
            trans.success = False

            tbk = traceback.format_exc()
            logging.error(str(tbk))
        else:
            trans.success = True
        
        finally:
            self._try_unlock()
            self._async_progress.finish()
            logging.info("Finished transaction")

            self._emit_transaction_done(trans)

        return False

    def _run_transaction(self,sysroot,trans):
        """This method needs to be implemented by the backends."""
        pass
        # raise errors.TransactionFailed(enums.ERROR_NOT_SUPPORTED)

    def _try_lock(self):
        _,out_acquired = self.sysroot.try_lock()
        if not out_acquired:
            raise UpdateBaseError(ERROR_OTHER_PROGRESS_OCCUPATION)
    
    def _try_unlock(self):
        self.sysroot.unlock()

    def _emit_transaction_done(self,trans):
        """Emit the transaction-done signal.

        Keyword argument:
        trans -- the finished transaction
        """
        # logging.debug("Emitting transaction-done: %d",)
        self.emit("transaction-done",trans)

    def _on_async_progress(self,obj):
        try:
            start_time = obj.get_uint64("start-time")
            bytes_transferred = obj.get_uint64("bytes-transferred") 
            tmp_bytes_transferred = obj.get_uint64("tmp-bytes-transferred") 
            fetched = obj.get_uint("fetched")
            requested = obj.get_uint("requested")

            # 下载速度
            elapsed_secs=(GLib.get_monotonic_time()-start_time)/GLib.USEC_PER_SEC
            bytes_sec = int(bytes_transferred / elapsed_secs)

            if requested <= 2:
                return
            
            self.emit("download-info",fetched,requested,tmp_bytes_transferred,self.download_size,bytes_sec)
        except Exception as excep:
            tbk = traceback.format_exc()
            logging.error(str(tbk))


    def _parse_ostree_error(self,error,trans):
        if error.code in (37,39):
            trans.error = ERROR_NETWORK_FAILED
        #取消
        elif error.code == 19:
            trans._cancellable.reset()
            trans.error = EXIT_CANCELLED
        elif error.code == 1:
            # 在远程分支 未找到 对应的 分支 
            if "Server returned status 404" in error.message:
                trans.error = ERROR_PUSH_BRANCH_EXCEPTION
            # 未找到 对应的仓库信息 
            elif "Remote" in error.message and "not found" in error.message:
                trans.success = True
        else:
            trans.error = "#05%s"%error.code
            trans.error_message = error.message
    
    def _find_offline_repo(self, mount_point="/media/OfflineSource"):
        """
        尝试在挂载点下找到有效的离线源目录。
        规则：存在 config 文件的子目录即为有效目录。
        """
        if not os.path.isdir(mount_point):
            return None

        try:
            for entry in os.listdir(mount_point):
                flag_file = os.path.join(mount_point, entry, "config")
                if os.path.isfile(flag_file):
                    return os.path.join(mount_point, entry)
        except Exception as e:
            logging.warning("Failed to scan offline source at %s: %s", mount_point, e)

        return None


    def list_offline_deltas(self):
        """
        获取离线仓库的静态 delta 列表。
        如果未挂载或仓库无效，则返回空列表。
        """
        offline_path = self._find_offline_repo(OFFLINE_REPO_POST)
        if not offline_path:
            return []

        deltas_path = os.path.join(offline_path, "deltas")
        if not os.path.isdir(deltas_path):
            return []

        try:
            repo_file = Gio.File.new_for_path(offline_path)
            offline_repo = OSTree.Repo.new(repo_file)
            offline_repo.open(None)
            _, deltas_list = offline_repo.list_static_delta_names(None)
            return deltas_list or []
        except Exception as e:
            logging.warning("Failed to list static deltas from offline repo %s: %s", offline_path, e)
            return []

    def register_formatter(self, name, formatter):
        """A function a plugin can use to register a formatter."""
        self.formatters[name] = formatter

    def _load_plugins(self,identifier):
        # 每次formatters将被刷新
        self.formatters = {}
        plugin_dir = os.path.join(self.plugins_dir,identifier)
        plugins_sources = self.plugin_base.make_plugin_source(searchpath=[plugin_dir],identifier=identifier)

        # 初始化插件
        for plugin_name in plugins_sources.list_plugins():
            plugin = plugins_sources.load_plugin(plugin_name)
            plugin.setup(self)
        return self.formatters

class UpdateBackend(BackendBaseOstree):
    def __init__(self,sysroot_path=None,osname=None):
        """Initialize a new AptWorker instance."""
        BackendBaseOstree.__init__(self,sysroot_path,osname)
        
    def _get_booted_overlay_size(self, sysroot):
        try:
            overlay_size = sysroot.get_booted_overlay_size()
            return overlay_size
        except Exception:
            return 0

    def _run_transaction(self, sysroot,trans):
        g_options = { 
                        "n-network-retries":GLib.Variant("u",RETRY_LIMIT),\
                        "arch-verify":GLib.Variant("b",True),\
                        "flags":GLib.Variant("i",getattr(OSTree.RepoPullFlags, "COMMIT_AND_SIZE_ONLY", OSTree.RepoPullFlags.COMMIT_ONLY)),\
                        "max-speed-limit-kilobyte": GLib.Variant("u",trans.download_limit)
                    }
        push_revision = None
        ostree_repo = sysroot.repo()

        # 获取当前部署分支的checksum与当前部署的分支
        trans.from_revision = self.cur_deployment.get_csum()
        trans.booted_serial = self.cur_deployment.get_deployserial()
        refspec = self.origin.get_string("origin","refspec")
        _,trans.available_repo,trans.deployed_refs = OSTree.parse_refspec(refspec)
        if not trans.available_repo:
            logging.error("Now system deployment repo name is None...")
            raise UpdateBaseError(ERROR_REPO_IS_NONE)

        logging.info("Now system deployment info(%s:%s@%s)...",trans.available_repo,trans.deployed_refs,trans.from_revision)

        # 加载推送更新 当推送为空时 直接去服务端 拉取当前分支最新的commit
        if trans.push_list:
            for push in trans.push_list:
                trans.available_repo = push.repo_name
                trans.available_refs = push.branch
                # 指定拉取的commit id，不指定时拉取当前分支最新的
                if push.revision:
                    g_options["override-commit-ids"] = GLib.Variant("as",[push.revision])
                    push_revision = push.revision
                break
        else:
        # 未推送情况下 获取当前部署的分支
            trans.available_refs = trans.deployed_refs
                
        g_options["refs"] = GLib.Variant("as",[trans.available_refs])
        logging.info("Start pull metadata and options:%s...",g_options)
        ostree_repo.pull_with_options(trans.available_repo,GLib.Variant("a{sv}",g_options),self._async_progress,trans._cancellable)

        # 没有指定commit情况下 检索当前分支最新commit
        if not push_revision:
            _,trans.new_revision = ostree_repo.resolve_rev(f"{trans.available_repo}:{trans.available_refs}",True)
        else:
            trans.new_revision = push_revision

        _,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,trans.new_revision)
        trans.update_metadata = metadata[0]

        # 检查是否当前分支已经终止，切换到新的分支
        if OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE in trans.update_metadata and not trans.push_list:
            # 拉取新分支的元数据
            push_content = trans.update_metadata[OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE]
            logging.info("From origin ref:%s To new rebase:%s...",trans.available_refs, push_content)

            # openkylin版本支持推送
            repo, refs, revision = self._parse_push_content(push_content)
            trans.available_refs = refs
            if repo: trans.available_repo = repo
            if revision: 
                trans.new_revision = revision
                g_options["override-commit-ids"] = GLib.Variant("as", [revision])
            
            logging.info("Start pull new metadata refs:%s...",trans.available_refs)
            g_options["refs"] = GLib.Variant("as",[trans.available_refs])
            ostree_repo.pull_with_options(trans.available_repo,GLib.Variant("a{sv}",g_options),self._async_progress,trans._cancellable)
            _,trans.new_revision = ostree_repo.resolve_rev(f"{trans.available_repo}:{trans.available_refs}",True)
            
        _, metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,trans.new_revision)
        trans.update_metadata = metadata[0]

        # 增加额外的方式来读取元数据
        self.load_and_update_metadata(ostree_repo, trans)

        # 处理优先升级逻辑
        self.handle_priority_upgrade(trans, ostree_repo)

        trans.ovl_size = self._get_booted_overlay_size(sysroot)

    def handle_priority_upgrade(self, trans, ostree_repo):
        """
        处理优先升级流程

        :param trans: 升级事务对象
        :param ostree_repo: ostree 仓库对象
        :raises UpdateBaseError: 当优先升级成功时抛出
        """
        restart_list = set(["kylin-system-updater","kylin-update-frontend","kylin-software-properties"])

        if trans.from_revision == trans.new_revision:
            return  

        if self.list_offline_deltas():
            logging.info("Offline deltas detected, skipping priority upgrade.")
            return

        # 强制自我升级时清理进度
        if trans.update_metadata.get("force_self_upgrade", False):
            logging.info("Force self-upgrade detected, removing current progress directory.")
            shutil.rmtree(CURRENT_PROGRESS, ignore_errors=True)

        # 跳过优先升级场景
        skip_funcs = set(G_settings.get("skip_function", [])) | set(
            trans.update_metadata.get("skip_function", [])
        )
        if PRIORITY_UPGRADE_SUCCCESSED in skip_funcs:
            logging.info("Priority upgrade skipped by configuration.")
            return  

        # 判断是否符合优先升级条件：
        # 1. 麒麟系统：必须推送内容
        # 2. 非麒麟系统：允许无推送直接升级
        # 3. 强制自更新标记：无条件触发
        should_priority_upgrade = (
            (trans.is_kylin and trans.push_list)
            or (not trans.is_kylin and not trans.push_list)
            or trans.update_metadata.get("force_self_upgrade", False)
        )

        if should_priority_upgrade:
            success,upgrade_list = self_upgrade.run_priority_upgrade(ostree_repo, trans)
            logging.info(f"Priority upgrade result: success={success}, upgrade_list={upgrade_list}")
            restart_panel = any(x in restart_list for x in upgrade_list)
            if success and restart_panel:
                raise UpdateBaseError(PRIORITY_UPGRADE_SUCCCESSED)

    def _get_commit_recursive(self,ostree_repo,checksum):
        # 如果 start 不在 all_deltas 中，返回空列表
        _,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,checksum)
        if not metadata:
            return []
        parent = OSTree.commit_get_parent(metadata)
        if parent:
            return [parent] + self._get_commit_recursive(ostree_repo, parent)
        else:
            return []

    def _parse_push_content(self, push_content):
        """
        解析 push_content 字符串，返回解析结果，不修改任何对象。
        支持格式：
            repo:ref@revision
            repo:ref
            ref@revision
            ref
        返回：
            (repo_or_none, refs, revision_or_none)
        """
        parts = push_content.split(":", 1)
        repo = parts[0] if len(parts) == 2 else None
        ref_and_rev = parts[1] if len(parts) == 2 else parts[0]

        refs_parts = ref_and_rev.split("@", 1)
        refs = refs_parts[0]
        revision = refs_parts[1] if len(refs_parts) == 2 and refs_parts[1] else None

        return repo, refs, revision

    def _get_all_commit(self,ostree_repo):
        commit_and_refs = {}
        _,out_all_refs = ostree_repo.list_refs(None,None)
        
        for refs in out_all_refs:
            commit_list = self._get_commit_recursive(ostree_repo,out_all_refs[refs])
            for commit in commit_list:
                commit_and_refs[commit] = refs

        logging.info(commit_and_refs)

    def load_and_update_metadata(self, ostree_repo, trans):
        """
        读取 ostree commit 的 detached metadata 并更新 trans.update_metadata.

        :param ostree_repo: ostree 仓库对象
        :param trans: 包含 new_revision, cancellable, update_metadata 的事务对象
        """
        try:
            _, detached_meta = ostree_repo.read_commit_detached_metadata(
                trans.new_revision, trans._cancellable
            )
            if not detached_meta:
                logging.info(f"No detached metadata for commit:{trans.new_revision}")
                return

            extra_meta = dict(detached_meta.unpack())

            # 关键字段
            download_size = int(extra_meta.get("download-size", 0))
            install_size = int(extra_meta.get("install-size", 0))
            deploy_time = int(extra_meta.get("deploy-estimate-time", 0))

            # 更新事务 metadata
            trans.update_metadata.update({
                "download-size": download_size,
                "install-size": install_size,
                "deploy_time": deploy_time,
            })
            trans.deploy_time = deploy_time
            
            logging.info(
                "Metadata loaded for commit %s: download_size=%d, install_size=%d",
                trans.new_revision, download_size, install_size
            )
        except Exception as e:
            logging.warning("Failed to read detached metadata for commit %s: %s",trans.new_revision, e)

class DownloadBackend(BackendBaseOstree):
    def __init__(self,sysroot_path=None,osname=None):
        """Initialize a new AptWorker instance."""
        BackendBaseOstree.__init__(self,sysroot_path,osname)
        self.download_size = 0

    def _get_offline_deltas(self):
        """
        解析离线仓库中的 delta 信息。
        返回:
            all_deltas: dict[from_rev -> to_rev]
            to_refs: dict[to_rev -> ref_name]
        """
        all_deltas = {}
        to_refs = {}

        deltas_list = self.list_offline_deltas()
        if not deltas_list:
            return all_deltas, to_refs

        logging.info("Offline Repo(%s) has delta files: %r", OFFLINE_REPO_POST, deltas_list)

        for delta_info in deltas_list:
            delta_parts = delta_info.split("-")
            if len(delta_parts) < 2:
                logging.warning("Invalid delta format: %s", delta_info)
                continue

            from_rev, to_rev = delta_parts[0], delta_parts[1]
            all_deltas[from_rev] = to_rev

            # 解析 refs
            if len(delta_parts) == 3:
                ref_repo = delta_parts[2]
                if ":" in ref_repo:
                    _, ref_name = ref_repo.split(":", 1)
                else:
                    ref_name = ref_repo
                to_refs[to_rev] = ref_name

        return all_deltas, to_refs

    def _run_transaction(self, sysroot,trans):
        g_options = {
                            "n-network-retries":GLib.Variant("u",RETRY_LIMIT),\
                            "arch-verify":GLib.Variant("b",True),\
                            "max-speed-limit-kilobyte": GLib.Variant("u",trans.download_limit),\
                            "flags":GLib.Variant("i",OSTree.RepoPullFlags.NONE)
                        }
        
        ostree_repo = sysroot.repo()
        
        remote_repo = trans.available_repo
        if trans.update_metadata:
            self.download_size = int(trans.update_metadata.get("download-size",0))

        g_options["refs"] = GLib.Variant("as",[trans.available_refs])

        all_deltas,to_refs = self._get_offline_deltas()
        if all_deltas:
            pull_deltas = self._get_deltas_recursive(trans.from_revision,all_deltas)
            g_options["require-static-deltas"] = GLib.Variant("b",True)

            # 循环拉取所有的 deltas
            for delta in pull_deltas:
                if delta in to_refs:
                    g_options["refs"] = GLib.Variant("as",[to_refs[delta]])
                g_options["override-commit-ids"] = GLib.Variant("as",[delta])
                logging.info("Start pull data and options:%s...",g_options)
                ostree_repo.pull_with_options(remote_repo,GLib.Variant("a{sv}",g_options),self._async_progress,trans._cancellable)

                # 检查到下载的 delta 已经是目标版本 则退出
                if trans.new_revision == delta:
                    logging.info("Delta %s is target version, stop pulling deltas.", delta)
                    break
        else:
            g_options["override-commit-ids"] = GLib.Variant("as",[trans.new_revision])
            logging.info("Start pull data and options:%s...",g_options)
            ostree_repo.pull_with_options(remote_repo,GLib.Variant("a{sv}",g_options),self._async_progress,trans._cancellable)

    
    def _get_deltas_recursive(self,start, all_deltas):
        # 如果 start 不在 all_deltas 中，返回空列表
        if start not in all_deltas:
            return []
        # 获取下一个值并递归处理
        next_value = all_deltas[start]
        return [next_value] + self._get_deltas_recursive(next_value, all_deltas)

class DeployBackend(BackendBaseOstree):
    def __init__(self,sysroot_path=None,osname=None):
        """Initialize a new AptWorker instance."""
        BackendBaseOstree.__init__(self,sysroot_path,osname)
        
    def _deployment_sanitycheck_true(self,rootfs_path):
        try:
            pid = os.fork()
            if pid == 0:
                os.chroot(rootfs_path)
                os.chdir("/")
                # 执行命令
                result = subprocess.run(["/usr/bin/true"],capture_output=True, text=True)
                os._exit(result.returncode)
            else:
                # 等待子进程结束
                _, exit_code = os.waitpid(pid, 0)
                logging.info("Command exited with status: %d",os.WEXITSTATUS(exit_code))
                if os.WIFEXITED(exit_code) != 0:
                    logging.error("Command did not exit normally.")
        except Exception as e:
            logging.error(str(e))

    def _on_async_progress(self,obj):
        try:
            current_progress = obj.get_uint("current-progress")
            self.emit("deploy-progress",int(current_progress))
        except Exception as excep:
            tbk = traceback.format_exc()
            logging.error(str(excep)+str(tbk))

    def _run_transaction(self, sysroot,trans):
        """
        执行 OSTree 部署事务
        """
        ostree_repo = sysroot.repo()
        new_revision = trans.new_revision
        refs = trans.available_refs
        remote_repo = trans.available_repo

        origin_refspec = f"{remote_repo}:{refs}"
        deploy_origin = sysroot.origin_new_from_refspec(origin_refspec)

        flags = OSTree.SysrootSimpleWriteDeploymentFlags.RETAIN
        logging.info("Start deploy: refs=%s, new_revision=%s", refs, new_revision)

        try:
            sysroot.prepare_cleanup(self._async_progress,None)
            _,upgrade_deployment = sysroot.deploy_tree(self._async_progress,self.osname,new_revision,deploy_origin,self.cur_deployment,None,None)
            sysroot.simple_write_deployment(self._async_progress,self.osname,upgrade_deployment,self.cur_deployment,flags,None)
        except TypeError as e:
            logging.info("Switch to using APIs from the previous of OStree and error:%s",str(e))
            self.emit("deploy-progress",20)
            sysroot.prepare_cleanup(None)
            self.emit("deploy-progress",30)
            _,upgrade_deployment = sysroot.deploy_tree(self.osname,new_revision,deploy_origin,self.cur_deployment,None,None)
            self.emit("deploy-progress",50)
            sysroot.simple_write_deployment(self.osname,upgrade_deployment,self.cur_deployment,flags,None)

        self.emit("deploy-progress",100)
        _,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,new_revision) 

        trans.new_revision = new_revision
        trans.update_metadata = metadata[0]

class RollbackBackend(BackendBaseOstree):
    def __init__(self,sysroot_path=None,osname=None):
        """Initialize a new AptWorker instance."""
        BackendBaseOstree.__init__(self,sysroot_path,osname)

    def _run_transaction(self, sysroot,trans):
        new_deployments = []

        out_pending,out_rollback = sysroot.query_deployments_for(self.osname)

        if out_rollback == None:
            raise UpdateBaseError(ERROR_NOT_ROLLBAK_DEPLOYMENT)

        rollback_csum = out_rollback.get_csum()

        old_deployments = sysroot.get_deployments()

        new_deployments.append(out_rollback)

        # 部署的列表
        for deploys in old_deployments:
            if not out_rollback.equal(deploys):
                new_deployments.append(deploys)

        logging.info("start reollback checksum:%s...",rollback_csum)
        sysroot.write_deployments(new_deployments,None)

        origin = out_rollback.get_origin()
        refspec = origin.get_string("origin","refspec")
        _,remote_repo,origin_ref = OSTree.parse_refspec(refspec)

        trans.available_refs = origin_ref

class LoadCacheBackend(BackendBaseOstree):
    def __init__(self,sysroot_path=None,osname=None):
        """Initialize a new AptWorker instance."""
        BackendBaseOstree.__init__(self,sysroot_path,osname)

    def _run_transaction(self, sysroot,trans):
        trans.deployed_refs = self._get_refs()
        trans.rollback_deployment = self._rollback_deployment(sysroot,self.osname)
        trans.revision = self.cur_deployment.get_csum()

    def _get_refs(self):
        refspec = self.origin.get_string("origin","refspec")
        _,remote_repo,origin_ref = OSTree.parse_refspec(refspec)
        if origin_ref == None:
            origin_ref = ''
        return origin_ref
    
    def _rollback_deployment(self,sysroot,osname):
        out_pending,out_rollback = sysroot.query_deployments_for(osname)
        
        if out_rollback and not out_rollback.is_pinned():
            rollback_origin = out_rollback.get_origin()
            refspec = rollback_origin.get_string("origin","refspec")
            _,remote_repo,origin_ref = OSTree.parse_refspec(refspec)
            
            return [origin_ref]
        else:
            return []