# /*
#  * 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/python3

import logging
import json
from gi.repository import Gio
from SystemUpdater.modules.i18n import _
from SystemUpdater.Core.errors import *
from SystemUpdater.Core.enums import *
from typing import Optional, List, Dict
from typing import List, Dict, Optional
from pydantic import BaseModel
from datetime import datetime

class UpdateContentInfo(BaseModel):
    repo_name: str = None
    branch: str = None
    revision: str = None
    timestamp: str = None
    metadata: dict = {}
    reflock: bool = False     # 为True时 必须和当前部署分支处于相同的分支下 才可以升级
    booted_serial: int = 0
    update_type: str = None
    active_update: bool = False     # 为True时 表示不受推送影响 主动的从服务端检查并提示可更新
    downloaded: bool = False
    def asdict(self):
        return json.loads(self.json())
    
class PostAction(BaseModel):
    command: Optional[str] = None   # 可执行文件路径
    value: Optional[str] = ""       # 附加信息（来源目录/分类）
    priority: int = 100             # 优先级（由文件名前缀数字决定）
    success: bool = True
    error_desc: Optional[str] = None

class Transaction(BaseModel):
    # -------------------------
    # 通用状态
    # -------------------------
    success: bool = False                      # 当前 Transaction 执行是否成功，每个阶段完成后都会更新
    sender: Optional[str] = None               # 触发此更新的调用方标识（例如 D-Bus 调用者）
    sender_id: Optional[str] = None               # 触发此更新的调用方标识（例如 D-Bus 调用者）
    role: str = ROLE_UNSET                      # 标识处理的流程或角色，用于区分不同类型的更新操作
    status: Optional[str] = None               # 标识当前 Transaction 所处阶段，例如 'checking', 'downloading', 'deploying', 'finished'
    is_kylin: bool = False                      # 标识是否在 Kylin 系统环境下执行
    trans_timestamp: Optional[str] = None      # Transaction 创建或执行的时间戳
    error: Optional[str] = None                # 错误类型或错误代码，用于快速判断失败原因
    error_message: Optional[str] = None        # 详细的错误描述信息
    delay_error: Optional[str] = None          # 延迟报告的错误信息，用于非阻塞操作中的错误传递

    # -------------------------
    # 版本信息
    # -------------------------
    prev_revision: Optional[str] = None        # 当前部署版本的 revision（原版本）
    new_revision: Optional[str] = None         # 计划升级到的新版本 revision
    cur_deployed: Optional[UpdateContentInfo] = None  # 当前已经部署的版本信息

    # -------------------------
    # 下载 & 部署相关
    # -------------------------
    ovl_size: int = 0                          # 本次更新的覆盖大小（字节），用于计算下载量或容量
    backup_success: bool = False               # 是否成功备份旧版本，备份失败可能影响回滚
    download_limit: int = 0                    # 下载限速，单位 KB/s，根据系统约定
    should_backup: bool = True
    
    # -------------------------
    # 系统控制
    # -------------------------
    reboot_required: bool = True               # 部署完成后是否需要重启系统；False 表示可能只需关闭相关服务

    # -------------------------
    # 前端状态 & 更新推送信息
    # -------------------------
    has_read: bool = False                      # 前端是否已经读取过此 Transaction 状态；读取后可设置为 True
    selected: Optional[UpdateContentInfo] = None       # 当前选中的更新内容，通常是单个组件或包
    origin_push: List[UpdateContentInfo] = None  # 原始的推送内容列表
    push_content: List[UpdateContentInfo] = None  # 计划拉取/更新的内容列表
    hooks: Dict[str, List[PostAction]] = None     # 各阶段需要执行的钩子动作，例如 pre_download/post_download/pre_deploy/post_deploy

    # -------------------------
    # 数据迁移
    # -------------------------
    data_migration_path: str = None             #记录数据迁移repack的包输出路径
    data_migration_path_chroot: str = None         #记录数据迁移repack的包输出相对根目录路径

    def cancelled():
        logging.info("Task has been cancelled...")
    _cancellable = Gio.Cancellable.new()
    _cancellable.connect(cancelled)

    def asdict(self):
        self.trans_timestamp = datetime.now()
        return json.loads(self.json())