// SPDX-FileCopyrightText: 2018 - 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

package updateplatform

import (
	"archive/tar"
	"bufio"
	"bytes"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/linuxdeepin/lastore-daemon/src/lastore-update-tools/controller/check"

	"github.com/godbus/dbus/v5"
	ConfigManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager"
	"github.com/linuxdeepin/go-lib/keyfile"
	"github.com/linuxdeepin/go-lib/log"
	"github.com/linuxdeepin/go-lib/strv"
	"github.com/linuxdeepin/go-lib/utils"
	Cfg "github.com/linuxdeepin/lastore-daemon/src/internal/config"
	"github.com/linuxdeepin/lastore-daemon/src/internal/ratelimit"
	"github.com/linuxdeepin/lastore-daemon/src/internal/system"
)

var logger = log.NewLogger("lastore/messageReport")

const defaultPlatformRepoComponents = "main community commercial"

var setIPFSRateLimit = ratelimit.SetIPFSRateLimit

const defaultDownloadSpeedLimitConfig = `{"DownloadSpeedLimitEnabled":false,"LimitSpeed":"1024","IsOnlineSpeedLimit":false}`
const defaultDeliveryRateLimitConfig = `{"LimitType":0,"StartTime":"0001-01-01T00:00:00Z","EndTime":"0001-01-01T00:00:00Z","LimitRate":10485760,"CurrentRate":10485760}`

type ProcessEvent struct {
	TaskID       int    `json:"taskID"`
	EventType    int    `json:"eventType"`
	EventStatus  bool   `json:"eventStatus"`
	EventContent string `json:"eventContent"`
	ExecAt       int64  `json:"execAt"`
}

const (
	CheckEnv         int = 1
	GetUpdateEvent   int = 2
	StartDownload    int = 3
	DownloadComplete int = 4
	StartBackUp      int = 5
	BackUpComplete   int = 6
	StartInstall     int = 7

	PreUpdateCheck    int = 101
	PostUpdateCheck   int = 102
	PreDownloadCheck  int = 103
	PostDownloadCheck int = 104
	PreBackupCheck    int = 105
	PostBackupCheck   int = 106
	PreUpgradeCheck   int = 107
	MidUpgradeCheck   int = 108
	PostUpgradeCheck  int = 109
)

type ShellCheck struct {
	Name  string `json:"name"`  //检查脚本的名字
	Shell string `json:"shell"` //检查脚本的内容
}

// 发送给更新平台的状态信息
type StatusMessage struct {
	Type           string `json:"type"`           //消息类型，info,warning,error
	UpdateType     string `json:"updateType"`     //system.UpdateType类型
	JobDescription string `json:"jobDescription"` //job.Description
	Detail         string `json:"detail"`         //消息详情
}

type UpdatePlatformManager struct {
	config                            *Cfg.Config
	allowPostSystemUpgradeMessageType system.UpdateType
	preBuild                          string // 进行系统更新前的版本号，如20.1060.11018.100.100，本地os-baseline获取
	preBaseline                       string // 更新前的基线号，从baseline获取，本地os-baseline获取

	targetVersion          string // 更新到的目标版本号,本地os-baseline.b获取
	targetBaseline         string // 更新到的目标基线号,本地os-baseline.b获取
	checkTime              string // 基线检查时间
	systemTypeFromPlatform string // 从更新平台获取的系统类型,本地os-baseline.b获取
	requestUrl             string // 更新平台请求地址
	taskID                 int

	PreUpgradeCheck   []ShellCheck                  // 更新前检查脚本
	MidUpgradeCheck   []ShellCheck                  // 更新后检查脚本
	PostUpgradeCheck  []ShellCheck                  // 更新完成重启后检查脚本
	PreUpdateCheck    []ShellCheck                  // 检查更新前检查脚本
	PostUpdateCheck   []ShellCheck                  // 检查更新后检查脚本
	PreDownloadCheck  []ShellCheck                  // 下载更新前检查脚本
	PostDownloadCheck []ShellCheck                  // 下载更新后检查脚本
	PreBackupCheck    []ShellCheck                  // 备份前检查脚本
	PostBackupCheck   []ShellCheck                  // 备份后检查脚本
	TargetCorePkgs    map[string]system.PackageInfo // 必须安装软件包信息清单  	对应dut的core list
	BaselinePkgs      map[string]system.PackageInfo // 当前版本的核心软件包清单	对应dut的baseline
	SelectPkgs        map[string]system.PackageInfo // 可选软件包清单
	FreezePkgs        map[string]system.PackageInfo // 禁止升级包清单
	PurgePkgs         map[string]system.PackageInfo // 删除软件包清单

	repoInfos        []repoInfo      // 从更新平台获取的仓库信息
	SystemUpdateLogs []UpdateLogMeta // 更新注记
	cveDataTime      string
	cvePkgs          map[string][]string // cve信息 pkgname:[cveid...]

	packagesPrefixList []string // 根据仓库

	Token string
	arch  string

	Tp              UpdateTp  // 更新策略类型:1.非强制更新，2.强制更新/立即更新，3.强制更新/关机或重启时更新，4.强制更新/指定时间更新
	UpdateTime      time.Time // 更新时间(指定时间更新时的时间)
	OnlineRateLimit throttlingMessage
	IPFSConfig      ratelimit.IPFSConfig // ipfs配置
	UpdateNowForce  bool                 // 立即更新
	mu              sync.Mutex

	jobPostMsgMap     map[string]*UpgradePostMsg
	jobPostMsgMapMu   sync.Mutex
	TimerHasChanged   bool
	inhibitAutoQuit   func()
	UnInhibitAutoQuit func()
}

type platformCacheContent struct {
	CoreListPkgs      map[string]system.PackageInfo
	BaselinePkgs      map[string]system.PackageInfo
	SelectPkgs        map[string]system.PackageInfo
	PreUpgradeCheck   []ShellCheck
	MidUpgradeCheck   []ShellCheck
	PostUpgradeCheck  []ShellCheck
	PreUpdateCheck    []ShellCheck
	PostUpdateCheck   []ShellCheck
	PreDownloadCheck  []ShellCheck
	PostDownloadCheck []ShellCheck
	PreBackupCheck    []ShellCheck
	PostBackupCheck   []ShellCheck
}

// 需要注意cache文件的同步时机，所有数据应该不会从os-version和os-baseline获取
const (
	CacheVersion  = "/var/lib/lastore/os-version.b"
	cacheBaseline = "/var/lib/lastore/os-baseline.b"
	realBaseline  = "/etc/os-baseline"
	realVersion   = "/etc/os-version"
	cacheTaskInfo = "/var/lib/lastore/os-task-info"

	KeyNow      string = "now"      // 立即更新
	KeyShutdown string = "shutdown" // 关机更新
	KeyLayout   string = "15:04"
)

func NewUpdatePlatformManager(c *Cfg.Config, updateToken bool) *UpdatePlatformManager {
	platformUrl := c.PlatformUrl
	if len(platformUrl) == 0 {
		platformUrl = os.Getenv("UPDATE_PLATFORM_URL")
	}

	if !utils.IsFileExist(CacheVersion) {
		err := os.Symlink(realVersion, CacheVersion)
		if err != nil {
			logger.Warning(err)
		}
	}
	if !utils.IsFileExist(cacheBaseline) {
		copyFile(realBaseline, cacheBaseline)
	}
	arch, err := GetArchInfo()
	if err != nil {
		logger.Warning(err)
	}
	var token string
	if updateToken {
		token = UpdateTokenConfigFile(c.IncludeDiskInfo, c.GetHardwareIdByHelper) // update source时生成即可,初始化时由于授权服务返回SN非常慢(超过25s),因此不在初始化时生成
	}
	cache := platformCacheContent{}
	if strings.TrimSpace(c.OnlineCache) != "" {
		err = json.Unmarshal([]byte(c.OnlineCache), &cache)
		if err != nil {
			logger.Warning(err)
		}
	}

	systemType := getTargetSystemType()
	if systemType == "" {
		systemType = getCurrentSystemType()
	}

	return &UpdatePlatformManager{
		config:                            c,
		allowPostSystemUpgradeMessageType: system.SystemUpdate,
		preBuild:                          genPreBuild(),
		preBaseline:                       getCurrentBaseline(),
		targetVersion:                     getTargetVersion(),
		targetBaseline:                    getTargetBaseline(),
		systemTypeFromPlatform:            systemType,
		taskID:                            getTaskId(),
		requestUrl:                        platformUrl,
		cvePkgs:                           make(map[string][]string),
		Token:                             token,
		arch:                              arch,
		Tp:                                UnknownUpdate,
		UpdateNowForce:                    false,
		jobPostMsgMap:                     getLocalJobPostMsg(),
		TargetCorePkgs:                    cache.CoreListPkgs,
		BaselinePkgs:                      cache.BaselinePkgs,
		SelectPkgs:                        cache.SelectPkgs,
		PreUpgradeCheck:                   cache.PreUpgradeCheck,
		MidUpgradeCheck:                   cache.MidUpgradeCheck,
		PostUpgradeCheck:                  cache.PostUpgradeCheck,
		PreUpdateCheck:                    cache.PreUpdateCheck,
		PostUpdateCheck:                   cache.PostUpdateCheck,
		PreDownloadCheck:                  cache.PreDownloadCheck,
		PostDownloadCheck:                 cache.PostDownloadCheck,
		PreBackupCheck:                    cache.PreBackupCheck,
		PostBackupCheck:                   cache.PostBackupCheck,
	}
}

func (m *UpdatePlatformManager) GetCVEUpdateLogs(pkgs []string) map[string]CEVInfo {
	var cveInfos = make(map[string]CEVInfo)
	for _, name := range pkgs {
		for _, id := range m.cvePkgs[name] {
			if _, ok := cveInfos[id]; ok {
				continue
			}
			cveInfos[id] = CVEs[id]
		}
	}
	return cveInfos
}

func (m *UpdatePlatformManager) HasDeliveryRepo() bool {
	for _, repo := range m.repoInfos {
		if strings.Contains(repo.Source, "delivery://") {
			return true
		}
	}
	return false
}

func (m *UpdatePlatformManager) GetPlatformRepoSources() []string {
	var repos []string
	for _, repo := range m.repoInfos {
		if strings.HasPrefix(repo.Source, "deb ") {
			repos = append(repos, repo.Source)
			continue
		}

		components := defaultPlatformRepoComponents
		if m.config != nil && m.config.PlatformRepoComponents != "" {
			components = m.config.PlatformRepoComponents
		}
		repos = append(repos, fmt.Sprintf("deb %s %s %s", repo.Uri, repo.CodeName, components))
	}
	return repos
}

func genPreBuild() string {
	var preBuild string
	infoMap, err := GetOSVersionInfo(CacheVersion)
	if err != nil {
		logger.Warning(err)
	} else {
		preBuild = strings.Join(
			[]string{infoMap["MajorVersion"], infoMap["MinorVersion"], infoMap["OsBuild"]}, ".")
	}
	return preBuild
}

func copyFile(src, dst string) {
	content, err := os.ReadFile(src)
	if err != nil {
		logger.Warning(err)
		return
	}
	err = os.WriteFile(dst, content, 0644)
	if err != nil {
		logger.Warning(err)
		return
	}
}

type updateTarget struct {
	TargetVersion   string
	TargetOsVersion string
	CheckTime       string
}

func (m *UpdatePlatformManager) GetUpdateTarget() string {
	target := &updateTarget{
		TargetOsVersion: m.targetVersion,
		TargetVersion:   m.targetBaseline,
		CheckTime:       m.checkTime,
	}
	content, err := json.Marshal(target)
	if err != nil {
		logger.Warning(err)
		return ""
	}
	return string(content)
}

/*
[General]
Baseline=""
SystemType=""
*/
func getCurrentBaseline() string {
	return getGeneralValueFromKeyFile(realBaseline, "Baseline")
}

func getCurrentSystemType() string {
	return getGeneralValueFromKeyFile(realBaseline, "SystemType")
}

func getTargetBaseline() string {
	return getGeneralValueFromKeyFile(cacheBaseline, "Baseline")
}

func getTargetSystemType() string {
	return getGeneralValueFromKeyFile(cacheBaseline, "SystemType")
}

func getTargetVersion() string {
	return getGeneralValueFromKeyFile(cacheBaseline, "Version")
}

func getGeneralValueFromKeyFile(path, key string) string {
	kf := keyfile.NewKeyFile()
	err := kf.LoadFromFile(path)
	if err != nil {
		logger.Warning(err)
		return ""
	}
	content, err := kf.GetString("General", key)
	if err != nil {
		logger.Warning(err)
		return ""
	}
	return content
}

// UpdateBaseline 更新安装并检查成功后，同步baseline文件
func (m *UpdatePlatformManager) UpdateBaseline() {
	// 更新成功后,使用内存中的信息更新baseline,防止其他渠道更新了baseline.b影响了系统的baseline
	updateBaseline(cacheBaseline, m.targetBaseline)
	updateSystemType(cacheBaseline, m.systemTypeFromPlatform)
	updateVersion(cacheBaseline, m.targetVersion)
	copyFile(cacheBaseline, realBaseline)
	m.preBaseline = getCurrentBaseline()
}

// ReplaceVersionCache 进行安装更新前，需要复制文件替换软连接
func (m *UpdatePlatformManager) ReplaceVersionCache() {
	if utils.IsSymlink(CacheVersion) {
		// 如果cacheVersion已经是文件了，那么不再用源文件替换
		err := os.RemoveAll(CacheVersion)
		if err != nil {
			logger.Warning(err)
		}
		copyFile(realVersion, CacheVersion)
	}
}

func (m *UpdatePlatformManager) UpdateSourceList() {
	if !m.config.IntranetUpdate || !m.config.PlatformUpdate {
		return
	}

	var toWriteContent string
	for _, repo := range m.repoInfos {
		if len(repo.Source) != 0 {
			toWriteContent += repo.Source
			toWriteContent += "\n"
		}
	}
	if len(toWriteContent) != 0 {
		err := ioutil.WriteFile(system.OriginSourceFile, []byte(toWriteContent), 0644)
		if err != nil {
			logger.Warning(err)
		}
	}
}

// RecoverVersionLink 安装更新并检查完成后，需要用软连接替换文件
func (m *UpdatePlatformManager) RecoverVersionLink() {
	err := os.RemoveAll(CacheVersion)
	if err != nil {
		logger.Warning(err)
	}
	err = os.Symlink(realVersion, CacheVersion)
	if err != nil {
		logger.Warning(err)
	}
}

func (m *UpdatePlatformManager) UpdateBaselineCache() {
	updateBaseline(cacheBaseline, m.targetBaseline)
	updateSystemType(cacheBaseline, m.systemTypeFromPlatform)
	updateVersion(cacheBaseline, m.targetVersion)
}

func updateBaseline(path, content string) bool {
	return updateKeyFile(path, "Baseline", content)
}

func updateSystemType(path, content string) bool {
	return updateKeyFile(path, "SystemType", content)
}

func updateVersion(path, content string) bool {
	return updateKeyFile(path, "Version", content)
}

func updateKeyFile(path, key, content string) bool {
	kf := keyfile.NewKeyFile()
	if system.NormalFileExists(path) {
		err := kf.LoadFromFile(path)
		if err != nil {
			logger.Warning(err)
			return false
		}
	}
	kf.SetString("General", key, content)
	err := kf.SaveToFile(path)
	if err != nil {
		logger.Warning(err)
		return false
	}
	return true
}

const (
	ReleaseVersion  = 1
	UnstableVersion = 2
)

func isUnstable() int {
	sysBus, err := dbus.SystemBus()
	if err != nil {
		return ReleaseVersion
	}
	ds := ConfigManager.NewConfigManager(sysBus)
	dsPath, err := ds.AcquireManager(0, "org.deepin.unstable", "org.deepin.unstable", "")
	if err != nil {
		logger.Warning(err)
		return ReleaseVersion
	}
	unstableManager, err := ConfigManager.NewManager(sysBus, dsPath)
	if err != nil {
		logger.Warning(err)
		return ReleaseVersion
	}
	v, err := unstableManager.Value(0, "updateUnstable")
	if err != nil {
		return ReleaseVersion
	} else {
		value := v.Value().(string)
		if value == "Enabled" {
			return UnstableVersion
		} else {
			return ReleaseVersion
		}
	}
}

type Version struct {
	Version  string `json:"version"`
	Baseline string `json:"baseline"`
	TaskID   int    `json:"taskID"`
}

type AllDayRateLimit struct {
	Bps    int  `json:"bps"` // unit: KB/s
	Enable bool `json:"enable"`
}

type PeakOrNotTimeRateLimit struct {
	Enable    bool   `json:"enable"`
	StartTime string `json:"startTime"`
	EndTime   string `json:"endTime"`
	Bps       int    `json:"bps"` // unit: KB/s
}

type Policy struct {
	Tp UpdateTp `json:"tp"`

	Data policyData `json:"data"`
}

type policyData struct {
	UpdateTime string `json:"updateTime"`
}

type repoInfo struct {
	Uri      string `json:"uri"`
	Cdn      string `json:"cdn"`
	CodeName string `json:"codename"`
	Version  string `json:"version"`
	Source   string `json:"source"`
}

type ClientPollSetting struct {
	CheckPolicyInterval int   `json:"checkPolicyInterval"` // 检查策略间隔，单位秒
	StartCheckRange     []int `json:"startCheckRange"`     // 开始检查范围，单位秒
}

type updateMessage struct {
	SystemType        string            `json:"systemType"`
	Version           Version           `json:"version"`
	Policy            Policy            `json:"policy"`
	RepoInfos         []repoInfo        `json:"repoInfos"`
	ClientPollSetting ClientPollSetting `json:"clientPollSetting"`
}

type throttlingMessage struct {
	ServerTime           string                 `json:"serverTime"`
	AllDayRateLimit      AllDayRateLimit        `json:"allDayRateLimit"`      // 全天限速
	PeakTimeRateLimit    PeakOrNotTimeRateLimit `json:"peakTimeRateLimit"`    // 忙时(07:00~22:00)限速
	OffPeakTimeRateLimit PeakOrNotTimeRateLimit `json:"offPeakTimeRateLimit"` // 闲时(22:00~07:00)限速
}

type tokenMessage struct {
	Result bool            `json:"result"`
	Code   int             `json:"code"`
	Data   json.RawMessage `json:"data"`
}
type tokenErrorMessage struct {
	Result bool   `json:"result"`
	Code   int    `json:"code"`
	Msg    string `json:"msg"`
}

type requestType uint

const (
	GetVersion requestType = iota
	GetUpdateLog
	GetTargetPkgLists // 系统软件包清单
	GetCurrentPkgLists
	GetPkgCVEs // CVE 信息
	GetThrottling
	PostProcess
	PostProcessEvent
	PostResult
	GetIPFSConfig
)

type requestContent struct {
	path   string
	method string
}

func (r requestType) string() string {
	return fmt.Sprintf("%v %v", Urls[r].method, Urls[r].path)
}

var Urls = map[requestType]requestContent{
	GetVersion: {
		"/api/v1/version",
		"GET",
	},
	GetTargetPkgLists: {
		"/api/v1/package",
		"GET",
	},
	GetCurrentPkgLists: {
		"/api/v1/package",
		"GET",
	},
	GetUpdateLog: {
		"/api/v1/systemupdatelogs",
		"GET",
	},
	GetPkgCVEs: {
		"/api/v1/cve/sync",
		"GET",
	},
	GetThrottling: {
		"/api/v1/throttling/client",
		"GET",
	},
	PostProcess: {
		"/api/v1/process",
		"POST",
	},
	PostProcessEvent: {
		"/api/v1/process/events",
		"POST",
	},
	PostResult: {
		"/api/v1/update/status",
		"POST",
	},
	GetIPFSConfig: {
		"/api/v1/ipfs/config",
		"GET",
	},
}

const secret = "DflXyFwTmaoGmbDkVj8uD62XGb01pkJn"

func (m *UpdatePlatformManager) getToken() string {
	if len(m.Token) == 0 {
		m.Token = UpdateTokenConfigFile(m.config.IncludeDiskInfo, m.config.GetHardwareIdByHelper)
	}
	return m.Token
}

func (m *UpdatePlatformManager) genVersionResponse() (*http.Response, error) {
	policyUrl := m.requestUrl + Urls[GetVersion].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	request, err := http.NewRequest(Urls[GetVersion].method, policyUrl, nil)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %v ", GetVersion.string(), err.Error())
	}
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	request.Header.Set("X-Packages", base64.RawStdEncoding.EncodeToString([]byte(getClientPackageInfo(m.config.ClientPackageName))))
	return client.Do(request)
}

func (m *UpdatePlatformManager) genThrottlingResponse() (*http.Response, error) {
	throttlingUrl := m.requestUrl + Urls[GetThrottling].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	request, err := http.NewRequest(Urls[GetThrottling].method, throttlingUrl, nil)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %v ", GetThrottling.string(), err.Error())
	}
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	return client.Do(request)
}

func (m *UpdatePlatformManager) genTargetPkgListsResponse() (*http.Response, error) {
	policyUrl := m.requestUrl + Urls[GetTargetPkgLists].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	values := url.Values{}
	values.Add("baseline", m.targetBaseline)
	policyUrl = policyUrl + "?" + values.Encode()

	request, err := http.NewRequest(Urls[GetTargetPkgLists].method, policyUrl, nil)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %v ", GetTargetPkgLists.string(), err.Error())
	}
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	return client.Do(request)
}

func (m *UpdatePlatformManager) genCurrentPkgListsResponse() (*http.Response, error) {
	policyUrl := m.requestUrl + Urls[GetCurrentPkgLists].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	values := url.Values{}
	values.Add("baseline", m.preBaseline)
	policyUrl = policyUrl + "?" + values.Encode()
	request, err := http.NewRequest(Urls[GetCurrentPkgLists].method, policyUrl, nil)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %v ", GetCurrentPkgLists.string(), err.Error())
	}
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	return client.Do(request)
}

func (m *UpdatePlatformManager) genCVEInfoResponse(syncTime string) (*http.Response, error) {
	policyUrl := m.requestUrl + Urls[GetPkgCVEs].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	values := url.Values{}
	values.Add("synctime", syncTime)
	policyUrl = policyUrl + "?" + values.Encode()
	request, err := http.NewRequest(Urls[GetPkgCVEs].method, policyUrl, nil)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %v ", GetPkgCVEs.string(), err.Error())
	}
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	return client.Do(request)
}

func (m *UpdatePlatformManager) genUpdateLogResponse() (*http.Response, error) {
	policyUrl := m.requestUrl + Urls[GetUpdateLog].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	values := url.Values{}
	values.Add("baseline", m.targetBaseline)
	values.Add("isUnstable", fmt.Sprintf("%d", isUnstable()))
	policyUrl = policyUrl + "?" + values.Encode()
	request, err := http.NewRequest(Urls[GetUpdateLog].method, policyUrl, nil)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %v ", GetUpdateLog.string(), err.Error())
	}
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	return client.Do(request)
}

// genPostProcessResponse 生成数据，发送请求，并返回response.
// buf: 数据输入,io.Reader.
// filePath: 生成的xz压缩的中间文件.
func (m *UpdatePlatformManager) genPostProcessResponse(buf io.Reader, filePath string) (*http.Response, error) {
	policyUrl := m.requestUrl + Urls[PostProcess].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	if log.LevelDebug != logger.GetLogLevel() {
		defer os.RemoveAll(filePath)
	}
	xzFile, err := os.Create(filePath)
	if err != nil {
		logger.Warning("create file failed:", err)
		return nil, err
	}
	xzCmd := exec.Command("xz", "-z", "-c")
	xzCmd.Stdin = buf
	xzCmd.Stdout = xzFile
	if err := xzCmd.Run(); err != nil {
		_ = xzFile.Close()
		logger.Warning("exec xz command err:", err)
		return nil, err
	}
	_ = xzFile.Close()

	hash := sha256.New()
	xTime := fmt.Sprintf("%d", time.Now().Unix())

	xzFileContent, err := os.ReadFile(filePath)
	if err != nil {
		logger.Warning("open xz file failed:", err)
		return nil, err
	}
	body := bytes.NewBuffer(xzFileContent)

	hash.Write([]byte(fmt.Sprintf("%s%s%s", secret, xTime, xzFileContent)))
	sign := base64.StdEncoding.EncodeToString([]byte(hex.EncodeToString(hash.Sum(nil))))
	request, err := http.NewRequest(Urls[PostProcess].method, policyUrl, body)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %v ", PostProcess.string(), err.Error())
	}
	hardwareId := GetHardwareId(m.config.IncludeDiskInfo, m.config.GetHardwareIdByHelper)

	request.Header.Set("X-MachineID", hardwareId)
	request.Header.Set("X-CurrentBaseline", m.preBaseline)
	request.Header.Set("X-Baseline", m.targetBaseline)
	request.Header.Set("X-Time", xTime)
	request.Header.Set("X-Sign", sign)
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	logger.Debug("genPostProcessResponse:", request.Header)
	return client.Do(request)
}

func (m *UpdatePlatformManager) genIpfsConfigResponse() (*http.Response, error) {
	uri, err := url.Parse(m.requestUrl)
	if err != nil {
		return nil, fmt.Errorf("failed to parse %v: %w", uri, err)
	}
	parentPath := filepath.Dir(uri.Path)
	if parentPath == "." {
		parentPath = "/"
	}
	uri.Path = parentPath

	// ipfs限速配置的路由需要使用p2p-boot前缀来替换requestUrl部分的最后一个路径段
	uri = uri.JoinPath("p2p-boot", Urls[GetIPFSConfig].path)

	q := uri.Query()
	q.Set("id", m.IPFSConfig.ID)
	uri.RawQuery = q.Encode()
	client := &http.Client{
		Timeout: 40 * time.Second,
	}
	request, err := http.NewRequest(Urls[GetIPFSConfig].method, uri.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("%v new request failed: %w", GetIPFSConfig.string(), err)
	}
	return client.Do(request)
}

// getResponseData 解析 HTTP 响应数据，提取响应体中的 JSON 数据
// response: HTTP 响应对象
// reqType: 请求类型，用于日志记录
// 返回值:
//   - json.RawMessage: 解析后的 JSON 数据
//   - bool: 请求结果，成功时为 true，失败时为 false
//   - int: 错误码，成功时为响应中的 Code 字段，失败时为错误码
//   - error: 错误信息，成功时为 nil
func getResponseData(response *http.Response, reqType requestType) (json.RawMessage, bool, int, error) {
	if http.StatusOK == response.StatusCode {
		respData, err := io.ReadAll(response.Body)
		if err != nil {
			return nil, false, 0, fmt.Errorf("%v failed to read response body: %v ", response.Request.RequestURI, err.Error())
		}
		logger.Debugf("%v request for %v respData:%s ", reqType.string(), response.Request.URL, string(respData))
		msg := &tokenMessage{}
		err = json.Unmarshal(respData, msg)
		if err != nil {
			logger.Warningf("%v request for %v respData:%s ", reqType.string(), response.Request.URL, string(respData))
			return nil, false, 0, fmt.Errorf("%v failed to Unmarshal respData to tokenMessage: %v ", reqType.string(), err.Error())
		}
		if !msg.Result {
			logger.Warningf("%v request for %v respData:%s ", reqType.string(), response.Request.URL, string(respData))
			errorMsg := &tokenErrorMessage{}
			err = json.Unmarshal(respData, errorMsg)
			if err != nil {
				return nil, false, 0, fmt.Errorf("%v request for %s", reqType.string(), response.Request.RequestURI)
			}
			return nil, false, errorMsg.Code, fmt.Errorf("%v request for %s err:%s", reqType.string(), response.Request.RequestURI, errorMsg.Msg)
		}
		return msg.Data, msg.Result, msg.Code, nil
	} else {
		return nil, false, 0, fmt.Errorf("request for %s failed, response code=%d", response.Request.URL, response.StatusCode)
	}
}

func getVersionData(data json.RawMessage) *updateMessage {
	tmp := &updateMessage{}
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		logger.Warningf("%v failed to Unmarshal msg.Data to updateMessage: %v ", GetVersion.string(), err.Error())
		return nil
	}
	return tmp
}

func getThrottlingData(data json.RawMessage) *throttlingMessage {
	tmp := &throttlingMessage{}
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		logger.Warningf("%v failed to Unmarshal msg.Data to updateMessage: %v ", GetThrottling.string(), err.Error())
		return nil
	}
	serverTime, err := time.ParseInLocation(time.RFC3339, tmp.ServerTime, time.Local)
	tmp.ServerTime = serverTime.Format("15:04:05")
	return tmp
}

func getTargetPkgListData(data json.RawMessage) *PreInstalledPkgMeta {
	tmp := &PreInstalledPkgMeta{}
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		logger.Warningf("%v failed to Unmarshal msg.Data to PreInstalledPkgMeta: %v ", GetTargetPkgLists.string(), err.Error())
		return nil
	}
	return tmp
}

func getCurrentPkgListsData(data json.RawMessage) *PreInstalledPkgMeta {
	tmp := &PreInstalledPkgMeta{}
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		logger.Warningf("%v failed to Unmarshal msg.Data to PreInstalledPkgMeta: %v ", GetCurrentPkgLists.string(), err.Error())
		return nil
	}
	return tmp
}
func getUpdateLogData(data json.RawMessage) []UpdateLogMeta {
	var tmp []UpdateLogMeta
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		logger.Warningf("%v failed to Unmarshal msg.Data to UpdateLogMeta: %v ", GetUpdateLog.string(), err.Error())
		return nil
	}
	return tmp
}

func getCVEData(data json.RawMessage) *CVEMeta {
	tmp := &CVEMeta{}
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		logger.Warningf("%v failed to Unmarshal msg.Data to CVEMeta: %v ", GetPkgCVEs.string(), err.Error())
		return tmp
	}
	return tmp
}

// LoadConfig 加载服务器端配置
func getIPFSConfigData(data json.RawMessage) *ratelimit.IPFSConfig {
	tmp := &ratelimit.IPFSConfig{}
	if err := json.Unmarshal(data, &tmp); err != nil {
		logger.Warningf("%v failed to Unmarshal msg.Data to IPFSConfig: %v", GetIPFSConfig.string(), err)
		return tmp
	}
	return tmp
}

type UpdateTp int

const (
	UnknownUpdate   UpdateTp = 0
	NormalUpdate    UpdateTp = 1 // 更新
	UpdateNow       UpdateTp = 2 // 立即更新 // 以下为强制更新
	UpdateShutdown  UpdateTp = 3 // 关机更新
	UpdateRegularly UpdateTp = 4 // 定时更新
)

func IsForceUpdate(tp UpdateTp) bool {
	if tp >= UpdateNow && tp <= UpdateRegularly {
		return true
	}
	return false
}

// GenUpdatePolicyByToken 检查更新时将token数据发送给更新平台，获取本次更新信息
func (m *UpdatePlatformManager) genUpdatePolicyByToken() error {
	msg, err := m.getUpdateMessage()
	if err != nil {
		return err
	}

	m.targetBaseline = msg.Version.Baseline
	m.targetVersion = msg.Version.Version
	m.systemTypeFromPlatform = msg.SystemType
	m.repoInfos = msg.RepoInfos
	m.taskID = msg.Version.TaskID
	logger.Infof("current policy task id: %v", m.taskID)
	m.checkTime = time.Now().String()
	// 更新策略处理
	m.Tp = msg.Policy.Tp
	if m.Tp == UpdateRegularly {
		m.UpdateTime, _ = time.Parse(time.RFC3339, msg.Policy.Data.UpdateTime)
	}
	m.TimerHasChanged = false
	targetCheckInterval := time.Duration(msg.ClientPollSetting.CheckPolicyInterval) * time.Second
	if !utils.IsElementEqual(m.config.CheckInterval, targetCheckInterval) && msg.ClientPollSetting.CheckPolicyInterval > 0 {
		logger.Infof("Check interval %v --> %v", m.config.CheckInterval, targetCheckInterval)
		if err := m.config.SetCheckInterval(targetCheckInterval); err != nil {
			logger.Warningf("failed to set check interval: %v", err)
		} else {
			m.TimerHasChanged = true
		}
	}
	if !utils.IsElementEqual(m.config.StartCheckRange, msg.ClientPollSetting.StartCheckRange) && msg.ClientPollSetting.StartCheckRange != nil {
		logger.Infof("Start check range %v --> %v", m.config.StartCheckRange, msg.ClientPollSetting.StartCheckRange)
		if err := m.config.SetStartCheckRange(msg.ClientPollSetting.StartCheckRange); err != nil {
			logger.Warningf("failed to set start check range: %v", err)
		} else {
			m.TimerHasChanged = true
		}
	}

	m.UpdateSourceList()
	m.UpdateBaselineCache()
	m.saveTaskId()
	return nil
}

func (m *UpdatePlatformManager) getUpdateMessage() (*updateMessage, error) {
	return m.getUpdateMessageWithRetry(0)
}

func (m *UpdatePlatformManager) getUpdateMessageWithRetry(retryCount int) (*updateMessage, error) {
	const maxRetry = 3
	if retryCount > maxRetry {
		return nil, errors.New("max retry count exceeded for getUpdateMessage")
	}

	response, err := m.genVersionResponse()
	if err != nil {
		return nil, fmt.Errorf("failed get version data %v", err)
	}
	data, _, code, err := getResponseData(response, GetVersion)
	if err != nil {
		if code == 416 {
			unRegisted, unregErr := m.tryToUnRegisterConsole()
			if unRegisted && unregErr == nil {
				logger.Infof("unregister console success, will trigger recheck")
				time.Sleep(5 * time.Second)
				return m.getUpdateMessageWithRetry(retryCount + 1)
			}
			return nil, fmt.Errorf("unregister console failed: %w", unregErr)
		}
		return nil, fmt.Errorf("failed get version data %v", err)
	}
	msg := getVersionData(data)
	if msg == nil {
		return nil, errors.New("failed get version data")
	}
	return msg, nil
}

// SyncRepoAndInRelease 从更新平台同步仓库源配置和InRelease到本地
func (m *UpdatePlatformManager) SyncRepoAndInRelease(useP2PUpdate bool) {
	m.genRepositoryFromPlatform(useP2PUpdate)
	m.checkInReleaseFromPlatform()
}

func (m *UpdatePlatformManager) GenUpdatePolicyByToken() error {
	var err error
	if (m.config.PlatformDisabled & Cfg.DisabledVersion) == 0 {
		err = m.genUpdatePolicyByToken()
	}
	// 根据配置更新Tp
	switch m.Tp {
	case UpdateNow:
		m.config.SetInstallUpdateTime("")
	case UpdateShutdown:
		m.config.SetInstallUpdateTime("")
	case UpdateRegularly:
	default:
		logger.Debug("Config update time:", m.config.UpdateTime)
		switch m.config.UpdateTime {
		case KeyNow:
			m.Tp = UpdateNow
		case KeyShutdown:
			m.Tp = UpdateShutdown
		default:
			updateTime, err := time.Parse(time.RFC3339, m.config.UpdateTime)
			if err == nil {
				m.Tp = UpdateRegularly
				m.UpdateTime = updateTime
			} else {
				logger.Warning(err)
			}
		}
	}
	logger.Info("Policy tp:", m.Tp, "update time:", m.UpdateTime)
	logger.Info("pre Baseline:", m.preBaseline, "target Baseline：", m.targetBaseline)
	if len(m.targetBaseline) == 0 {
		m.Tp = NormalUpdate
	}
	m.UpdateNowForce = false
	switch m.Tp {
	case UpdateNow:
		m.UpdateNowForce = true
	case UpdateRegularly:
		// 距更新时间3min内则立即更新
		nowTime := time.Now()
		dot := time.Duration(3) * time.Minute
		bTime := nowTime.Add(dot)
		updateTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), m.UpdateTime.Hour(), m.UpdateTime.Minute(), 0, 0, nowTime.Location())
		if m.Tp == UpdateRegularly && updateTime.Before(bTime) && updateTime.After(nowTime) {
			m.UpdateNowForce = true
		}
		m.config.SetInstallUpdateTime(m.UpdateTime.Format(time.RFC3339))
	}
	logger.Info("Force Update:", IsForceUpdate(m.Tp), "update Immediate:", m.UpdateNowForce, "updateTime:", m.UpdateTime)
	return err
}

func (m *UpdatePlatformManager) GenThrottlingByToken() error {
	response, err := m.genThrottlingResponse()
	if err != nil {
		return fmt.Errorf("failed get throttling data %v", err)
	}
	data, _, _, err := getResponseData(response, GetThrottling)
	if err != nil {
		return fmt.Errorf("failed get throttling data %v", err)
	}
	msg := getThrottlingData(data)
	if msg == nil {
		return errors.New("failed get throttling data")
	}

	m.OnlineRateLimit = *msg
	return err
}

func (m *UpdatePlatformManager) GenIpfsConfig() error {
	response, err := m.genIpfsConfigResponse()
	if err != nil {
		return fmt.Errorf("failed to get ipfs config: %w", err)
	}
	data, _, _, err := getResponseData(response, GetIPFSConfig)
	if err != nil {
		return fmt.Errorf("failed to get ipfs config data: %w", err)
	}
	msg := getIPFSConfigData(data)
	if msg == nil {
		return fmt.Errorf("failed to get ipfs config data")
	}

	m.IPFSConfig = *msg

	configData, err := json.Marshal(m.IPFSConfig)
	if err != nil {
		return fmt.Errorf("failed to marshal ipfs config: %w", err)
	}
	logger.Infof("ipfs config: %s", string(configData))
	return nil
}

type packageLists struct {
	Core   []system.PlatformPackageInfo `json:"core"`   // "必须安装软件包清单"
	Select []system.PlatformPackageInfo `json:"select"` // "可选软件包清单"
	Freeze []system.PlatformPackageInfo `json:"freeze"` // "禁止升级包清单"
	Purge  []system.PlatformPackageInfo `json:"purge"`  // "删除软件包清单"
}

type PreInstalledPkgMeta struct {
	PreCheck          []ShellCheck `json:"preCheck"`          // "更新前检查脚本"
	MidCheck          []ShellCheck `json:"midCheck"`          // "更新后检查脚本"
	PostCheck         []ShellCheck `json:"postCheck"`         // "更新完成重启后检查脚本"
	PreUpdateCheck    []ShellCheck `json:"preUpdateCheck"`    // "检查更新前检查脚本"
	PostUpdateCheck   []ShellCheck `json:"postUpdateCheck"`   // "检查更新后检查脚本"
	PreDownloadCheck  []ShellCheck `json:"preDownloadCheck"`  // "下载更新前检查脚本"
	PostDownloadCheck []ShellCheck `json:"postDownloadCheck"` // "下载更新后检查脚本"
	PreBackupCheck    []ShellCheck `json:"preBackupCheck"`    // "备份前检查脚本"
	PostBackupCheck   []ShellCheck `json:"postBackupCheck"`   // "备份后检查脚本"
	Packages          packageLists `json:"packages"`          // "基线软件包清单"
}

func isJSONNull(data json.RawMessage) bool {
	return bytes.Equal(bytes.TrimSpace(data), []byte("null"))
}

func (m *UpdatePlatformManager) resetTargetPkgMeta() {
	m.PreUpgradeCheck = nil
	m.MidUpgradeCheck = nil
	m.PostUpgradeCheck = nil
	m.PreUpdateCheck = nil
	m.PostUpdateCheck = nil
	m.PreDownloadCheck = nil
	m.PostDownloadCheck = nil
	m.PreBackupCheck = nil
	m.PostBackupCheck = nil
	m.TargetCorePkgs = make(map[string]system.PackageInfo)
	m.SelectPkgs = make(map[string]system.PackageInfo)
	m.FreezePkgs = make(map[string]system.PackageInfo)
	m.PurgePkgs = make(map[string]system.PackageInfo)
}

// 从更新平台获取升级目标版本的软件包清单
func (m *UpdatePlatformManager) updateTargetPkgMetaSync() error {
	response, err := m.genTargetPkgListsResponse()
	if err != nil {
		return fmt.Errorf("failed get target pkg list data %v", err)
	}
	data, _, _, err := getResponseData(response, GetTargetPkgLists)
	if err != nil {
		return fmt.Errorf("failed get target pkg list data %v", err)
	}

	if isJSONNull(data) {
		m.resetTargetPkgMeta()
		logger.Warning("Target package list data is null")
		return nil
	}

	pkgs := getTargetPkgListData(data)
	if pkgs == nil {
		return errors.New("failed get target pkg list data")
	}

	m.PreUpgradeCheck = pkgs.PreCheck
	m.MidUpgradeCheck = pkgs.MidCheck
	m.PostUpgradeCheck = pkgs.PostCheck
	m.PreUpdateCheck = pkgs.PreUpdateCheck
	m.PostUpdateCheck = pkgs.PostUpdateCheck
	m.PreDownloadCheck = pkgs.PreDownloadCheck
	m.PostDownloadCheck = pkgs.PostDownloadCheck
	m.PreBackupCheck = pkgs.PreBackupCheck
	m.PostBackupCheck = pkgs.PostBackupCheck

	for _, pkg := range pkgs.Packages.Core {
		version := ""
		hasMatch := false
		for _, v := range pkg.AllArchVersion {
			if strings.Contains(v.Arch, m.arch) {
				version = v.Version
				hasMatch = true
				break
			}
		}
		if hasMatch {
			m.TargetCorePkgs[pkg.Name] = system.PackageInfo{
				Name:    pkg.Name,
				Need:    pkg.Need,
				Version: version,
			}
		}
	}

	for _, pkg := range pkgs.Packages.Select {
		version := ""
		hasMatch := false
		for _, v := range pkg.AllArchVersion {
			if strings.Contains(v.Arch, m.arch) {
				version = v.Version
				hasMatch = true
				break
			}
		}
		if hasMatch {
			m.SelectPkgs[pkg.Name] = system.PackageInfo{
				Name:    pkg.Name,
				Version: version,
				Need:    pkg.Need,
			}
		}
	}
	for _, pkg := range pkgs.Packages.Freeze {
		version := ""
		hasMatch := false
		for _, v := range pkg.AllArchVersion {
			if strings.Contains(v.Arch, m.arch) {
				version = v.Version
				hasMatch = true
				break
			}
		}
		if hasMatch {
			m.FreezePkgs[pkg.Name] = system.PackageInfo{
				Name:    pkg.Name,
				Version: version,
				Need:    pkg.Need,
			}
		}

	}
	for _, pkg := range pkgs.Packages.Purge {
		version := ""
		hasMatch := false
		for _, v := range pkg.AllArchVersion {
			if v.Arch == m.arch {
				version = v.Version
				hasMatch = true
				break
			}
		}
		if hasMatch {
			m.PurgePkgs[pkg.Name] = system.PackageInfo{
				Name:    pkg.Name,
				Version: version,
				Need:    pkg.Need,
			}
		}
	}

	return nil
}

// 从更新平台获取当前版本的预装清单
func (m *UpdatePlatformManager) updateCurrentPreInstalledPkgMetaSync() error {
	response, err := m.genCurrentPkgListsResponse()
	if err != nil {
		return fmt.Errorf("failed get current pkg list data %v", err)
	}
	data, _, _, err := getResponseData(response, GetCurrentPkgLists)
	if err != nil {
		return fmt.Errorf("failed get current pkg list data %v", err)
	}
	pkgs := getCurrentPkgListsData(data)
	if pkgs == nil {
		return errors.New("failed get current pkg list data")
	}
	for _, pkg := range pkgs.Packages.Core {
		version := ""
		hasMatch := false
		for _, v := range pkg.AllArchVersion {
			if v.Arch == m.arch {
				version = v.Version
				hasMatch = true
				break
			}
		}
		if hasMatch {
			m.BaselinePkgs[pkg.Name] = system.PackageInfo{
				Name:    pkg.Name,
				Version: version,
				Need:    pkg.Need,
			}
		}
	}

	return nil
}

type CEVInfo struct {
	SyncTime       string `json:"synctime"`       // "CVE类型"
	CveId          string `json:"cveId"`          // "CVE编号"
	Source         string `json:"source"`         // "包名"
	FixedVersion   string `json:"fixedVersion"`   // "修复版本"
	Archs          string `json:"archs"`          // "架构信息"
	Score          string `json:"score"`          // "评分"
	Status         string `json:"status"`         // "修复状态"
	VulCategory    string `json:"vulCategory"`    // "漏洞类型"
	VulName        string `json:"vulName"`        // "漏洞名称"
	VulLevel       string `json:"vulLevel"`       // "⻛险等级"
	PubTime        string `json:"pubTime"`        // "CVE公开时间"
	Binary         string `json:"binary"`         // "二进制包"
	Description    string `json:"description"`    // "漏洞描述"
	CveDescription string `json:"cveDescription"` // "漏洞描述(英文)"
}

type CVEMeta struct {
	DateTime string    `json:"dateTime"`
	Cves     []CEVInfo `json:"cves"`
}

var CVEs map[string]CEVInfo // 保存全局cves信息，方便查询

const cveLocalInfo = "/var/lib/lastore/cve_local_info.json"

func loadLocalCVEData() []byte {
	data, err := os.ReadFile(cveLocalInfo)
	if err != nil {
		logger.Warning(err)
		return nil
	}
	return data
}

type binaryObject struct {
	Name string `json:"name"`
}

func parseBinaryPackages(raw string) []string {
	raw = strings.TrimSpace(raw)
	if raw == "None" || len(raw) == 0 {
		return nil
	}

	// Try JSON object array: [{"name":"pkg", ...}]
	decoded := raw
	if strings.HasPrefix(decoded, "\"") {
		var s string
		if json.Unmarshal([]byte(decoded), &s) == nil {
			decoded = s
		}
	}
	if strings.HasPrefix(decoded, "[") {
		var objs []binaryObject
		if json.Unmarshal([]byte(decoded), &objs) == nil && len(objs) > 0 {
			var pkgs []string
			for _, o := range objs {
				if o.Name != "" {
					pkgs = append(pkgs, o.Name)
				}
			}
			if len(pkgs) > 0 {
				return pkgs
			}
		}
	}

	// Fall back to old Python-style list: ['pkg1', 'pkg2']
	str := raw
	str = strings.ReplaceAll(str, "[", "")
	str = strings.ReplaceAll(str, "]", "")
	str = strings.ReplaceAll(str, " ", "")
	str = strings.ReplaceAll(str, "'", "")
	if str == "None" || len(str) == 0 {
		return nil
	}
	return strings.Split(str, ",")
}

func saveCEVData(meta CVEMeta) {
	data, err := json.Marshal(meta)
	if err != nil {
		logger.Warning(err)
		return
	}
	err = os.WriteFile(cveLocalInfo, data, 0644)
	if err != nil {
		logger.Warning(err)
		return
	}
}

// 从更新平台获取CVE元数据
func (m *UpdatePlatformManager) updateCVEMetaDataSync() error {
	localData := loadLocalCVEData()
	localCVE := getCVEData(localData)
	response, err := m.genCVEInfoResponse(localCVE.DateTime)
	if err != nil {
		return fmt.Errorf("failed get cve meta info %v", err)
	}
	data, _, _, err := getResponseData(response, GetPkgCVEs)
	if err != nil {
		return fmt.Errorf("failed get cve meta info %v", err)
	}
	cves := getCVEData(data)
	if cves == nil {
		return errors.New("failed get cve meta info")
	}
	cves.Cves = append(cves.Cves, localCVE.Cves...)
	saveCEVData(*cves)
	// 重置CVEs
	CVEs = make(map[string]CEVInfo)
	m.cveDataTime = cves.DateTime
	for _, cve := range cves.Cves {
		CVEs[cve.CveId] = cve
		for _, binary := range parseBinaryPackages(cve.Binary) {
			m.cvePkgs[binary] = append(m.cvePkgs[binary], cve.CveId)
		}
	}
	return nil
}

func (m *UpdatePlatformManager) GetSystemMeta() map[string]system.PackageInfo {
	infos := make(map[string]system.PackageInfo)
	for name, info := range m.TargetCorePkgs {
		infos[name] = info
	}
	// 暂时应该只有core中的包是需要装的,可选包需要由前端选择
	// for name, info := range m.selectPkgs {
	// 	infos[name] = info
	// }
	return infos
}

type UpdateLogMeta struct {
	Baseline      string    `json:"baseline"`
	ShowVersion   string    `json:"showVersion"`
	CnLog         string    `json:"cnLog"`
	EnLog         string    `json:"enLog"`
	LogType       int       `json:"logType"`
	IsUnstable    int       `json:"isUnstable"`
	SystemVersion string    `json:"systemVersion"`
	PublishTime   time.Time `json:"publishTime"`
}

// 如果更新日志无法获取到,不会返回错误,而是设置默认日志文案
func (m *UpdatePlatformManager) updateLogMetaSync() error {
	response, err := m.genUpdateLogResponse()
	if err != nil {
		logger.Warning(err)
		return nil
	}
	data, _, _, err := getResponseData(response, GetUpdateLog)
	if err != nil {
		logger.Warning(err)
		return nil
	}
	forceLog := response.Header.Get("X-Force")
	if forceLog == "true" {
		// 强制使用更新平台日志
		m.SystemUpdateLogs = getUpdateLogData(data)
	} else {
		// 根据本地环境判断是否使用更新平台日志
		// m.targetVersion
		m.SystemUpdateLogs = make([]UpdateLogMeta, 0)
		osVersionInfoMap, err := GetOSVersionInfo(realVersion)
		if err != nil {
			logger.Warning("failed to get os-version:", err)
			// 获取本地版本号失败，使用默认日志
		} else {
			minorVersion := osVersionInfoMap["MinorVersion"]
			osBuild := osVersionInfoMap["OsBuild"]
			osBuildSlice := strings.Split(osBuild, ".")
			var secVersionStr string
			if len(osBuildSlice) >= 3 {
				var globalVersionStr string
				var ok bool
				// 社区版的version是minorVersion，直接可以显示小版本
				if osVersionInfoMap["EditionName"] == "Community" {
					ok = true
				} else {
					secVersionStr = osBuildSlice[1]
					secVersionInt, err := strconv.Atoi(secVersionStr)
					if err != nil {
						logger.Warning(err)
						return nil
					}
					realSecVersion := secVersionInt - 100
					minorVersionInt, err := strconv.Atoi(minorVersion)
					if err != nil {
						logger.Warning(err)
						return nil
					}
					globalVersion := minorVersionInt + realSecVersion
					if globalVersion < 1000 {
						logger.Warningf("system version is %v, not support compare with %v", globalVersion, m.targetVersion)
						return nil
					}
					targetVersionInt, err := strconv.Atoi(m.targetVersion)
					if err != nil {
						logger.Warning(err)
						return nil
					}
					if targetVersionInt > globalVersion {
						ok = true
					}
					globalVersionStr = fmt.Sprintf("%v", globalVersion)
				}
				logData := getUpdateLogData(data)
				if ok {
					m.SystemUpdateLogs = logData
				} else {
					var lastLog UpdateLogMeta
					if logData != nil && len(logData) > 0 {
						lastLog = logData[0]
					}

					m.SystemUpdateLogs = append(m.SystemUpdateLogs, UpdateLogMeta{
						Baseline:      lastLog.Baseline,
						ShowVersion:   globalVersionStr,
						CnLog:         "修复部分系统已知问题与缺陷",
						EnLog:         "Fixing some of the system's known problems and defects",
						LogType:       lastLog.LogType,
						IsUnstable:    lastLog.IsUnstable,
						SystemVersion: lastLog.SystemVersion,
						PublishTime:   lastLog.PublishTime,
					})
				}
			}
		}
	}
	return nil
}

func (m *UpdatePlatformManager) genRepositoryFromPlatform(useP2PUpdate bool) {
	repos := genPlatformReposFromRepoInfos(m.repoInfos, m.config.PlatformRepoComponents, useP2PUpdate, m.config.IntranetUpdate)
	err := os.WriteFile(system.PlatFormSourceFile, []byte(strings.Join(repos, "\n")), 0644)
	if err != nil {
		logger.Warning("update source list file err:", err)
	}
}

func genPlatformReposFromRepoInfos(repoInfos []repoInfo, platformRepoComponents string, upgradeDeliveryEnabled bool, intranetUpdate bool) []string {
	var repos []string
	for _, repo := range repoInfos {
		// If the public network has Delivery Optimization enabled,
		// then it is necessary to change http(s):// to delivery://.
		if upgradeDeliveryEnabled && !intranetUpdate {
			httpPrefix := "http://"
			httpsPrefix := "https://"
			deliveryPrefix := "delivery://"
			repo.Source = strings.ReplaceAll(repo.Source, httpPrefix, deliveryPrefix)
			repo.Uri = strings.ReplaceAll(repo.Uri, httpPrefix, deliveryPrefix)
			repo.Source = strings.ReplaceAll(repo.Source, httpsPrefix, deliveryPrefix)
			repo.Uri = strings.ReplaceAll(repo.Uri, httpsPrefix, deliveryPrefix)
		}
		if strings.HasPrefix(repo.Source, "deb ") {
			repos = append(repos, repo.Source)
		} else {
			prefix := "deb"
			// v25上应该是这个
			components := defaultPlatformRepoComponents
			if platformRepoComponents != "" {
				components = platformRepoComponents
			}
			codeName := repo.CodeName
			// 如果有cdn，则使用cdn，效率更高
			var uri = repo.Uri
			// if repo.Cdn != "" {
			// 	uri = repo.Cdn
			// }
			repos = append(repos, fmt.Sprintf("%s %s %s %s", prefix, uri, codeName, components))
		}
	}
	return repos
}

func getAptAuthConf(domain string) (user, password string) {
	AuthFile := "/etc/apt/auth.conf.d/uos.conf"
	file, err := os.Open(AuthFile)
	if err != nil {
		logger.Warning("failed open uos.conf:", err)
		return "", ""
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	// 逐行读取文件内容
	for scanner.Scan() {
		line := strings.Split(scanner.Text(), " ")
		if len(line) < 6 {
			continue
		}
		if line[1] == domain {
			return line[3], line[5]
		}
	}
	return "", ""
}

// 校验InRelease文件，如果平台和本地不同，则删除
func (m *UpdatePlatformManager) checkInReleaseFromPlatform() {
	// 更新获取InRelease文件
	client := &http.Client{
		Timeout: 4 * time.Second,
	}
	// 用于Debug时查看重定向地址
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		logger.Info("CheckRedirect  :", req.Response.Header)
		if len(via) >= 10 {
			return errors.New("stopped after 10 redirects")
		}
		return nil
	}

	for _, repo := range m.repoInfos {
		func(repo repoInfo) {
			needRemoveCache := new(bool)
			*needRemoveCache = true
			var cachePrefix string
			defer func() {
				if *needRemoveCache {
					infos, err := os.ReadDir(system.OnlineListPath)
					if err != nil {
						logger.Warning(err)
						_ = os.RemoveAll(system.OnlineListPath)
						return
					}
					for _, info := range infos {
						if strings.Contains(info.Name(), cachePrefix) {
							_ = os.RemoveAll(filepath.Join(system.OnlineListPath, info.Name()))
						}
					}
				}
			}()
			// 如果有cdn，则使用cdn，效率更高
			var uri = repo.Uri
			if repo.Cdn != "" {
				uri = repo.Cdn
			}
			cachePrefix = strings.ReplaceAll(utils.URIToPath(fmt.Sprintf("%s/dists/%s", uri, repo.CodeName)), "/", "_")
			uri = fmt.Sprintf("%s/dists/%s/InRelease", uri, repo.CodeName)
			logger.Info("prefix:", cachePrefix)
			request, err := http.NewRequest("GET", uri, nil)
			if err != nil {
				logger.Warning(err)
				return
			}

			// 获取仓库文件路径
			file := utils.URIToPath(uri)
			if len(file) == 0 {
				logger.Warning("illegal uri:", repo.Uri)
				return
			}
			// 获取域名
			domain := strings.Split(file, "/")[0]

			request.Header.Set("X-Repo-Token", m.Token)
			request.SetBasicAuth(getAptAuthConf(domain))
			resp, err := client.Do(request)
			if err != nil {
				logger.Warning(err)
				return
			}
			defer resp.Body.Close()

			switch resp.StatusCode {
			case http.StatusOK:
			default:
				logger.Warningf("failed download InRelease:%s ,respCode:%d", uri, resp.StatusCode)
				return
			}

			data, err := io.ReadAll(resp.Body)
			if err != nil {
				logger.Warning(err)
				return
			}

			file = strings.ReplaceAll(file, "/", "_")
			lastoreFile := "/tmp/" + file
			aptFile := filepath.Join(system.OnlineListPath, file)

			err = os.WriteFile(lastoreFile, data, 0644)
			if err != nil {
				logger.Warning(err)
				return
			}
			if utils.IsFileExist(aptFile) {
				// 文件存在，则校验MD5值
				aptSum, ok := utils.SumFileMd5(aptFile)
				if !ok {
					logger.Warningf("check %s md5sum failed", aptFile)
					return
				}
				lastoreSum, ok := utils.SumFileMd5(lastoreFile)
				if !ok {
					logger.Warningf("check %s md5sum failed", lastoreFile)
					return
				}
				if aptSum != lastoreSum {
					logger.Warning("InRelease changed:", aptFile)
				} else {
					logger.Warningf("InRelease unchanged: %s", aptFile)
					*needRemoveCache = false
				}
			}
			// stat 失败情况可以直接忽略，通过apt update获取索引即可
		}(repo)
	}
}

// UpdateAllPlatformDataSync 同步获取所有需要从更新平台获取的数据
func (m *UpdatePlatformManager) UpdateAllPlatformDataSync() error {
	var wg sync.WaitGroup
	var errList []string
	var syncFuncList []func() error
	m.TargetCorePkgs = make(map[string]system.PackageInfo)
	m.BaselinePkgs = make(map[string]system.PackageInfo)
	m.SelectPkgs = make(map[string]system.PackageInfo)
	m.FreezePkgs = make(map[string]system.PackageInfo)
	m.PurgePkgs = make(map[string]system.PackageInfo)
	if (m.config.PlatformDisabled & Cfg.DisabledUpdateLog) == 0 {
		syncFuncList = append(syncFuncList, m.updateLogMetaSync) // 日志
	}
	if (m.config.PlatformDisabled & Cfg.DisabledTargetPkgLists) == 0 {
		syncFuncList = append(syncFuncList, m.updateTargetPkgMetaSync) // 目标版本信息
	}
	if (m.config.PlatformDisabled&Cfg.DisabledCurrentPkgLists) == 0 && m.preBaseline != "" {
		syncFuncList = append(syncFuncList, m.updateCurrentPreInstalledPkgMetaSync) // 基线版本信息
	}
	if (m.config.PlatformDisabled & Cfg.DisabledPkgCVEs) == 0 {
		syncFuncList = append(syncFuncList, m.updateCVEMetaDataSync) // cve信息
	}
	for _, syncFunc := range syncFuncList {
		wg.Add(1)
		go func(f func() error) {
			err := f()
			if err != nil {
				errList = append(errList, err.Error())
			}
			wg.Done()
		}(syncFunc)

	}
	wg.Wait()
	if len(errList) > 0 {
		return errors.New(strings.Join(errList, "\n"))
	}
	return nil
}

func (m *UpdatePlatformManager) PostProcessEventMessage(body ProcessEvent) {
	if !m.config.IntranetUpdate {
		return
	}
	if m.targetBaseline == "" {
		logger.Warning("target baseline is empty")
	}
	logger.Debug("post process event msg:", body)
	body.TaskID = m.taskID
	if body.ExecAt == 0 {
		body.ExecAt = time.Now().UnixMilli()
	}
	if (m.config.PlatformDisabled & Cfg.DisabledProcess) != 0 {
		logger.Warning("platform is disabled")
		return
	}
	if len(body.EventContent) >= 950 {
		body.EventContent = body.EventContent[:950]
	}
	jsonData, err := json.Marshal(body)
	if err != nil {
		logger.Warning("JSON marshaling error:", err)
		return
	}
	policyUrl := m.requestUrl + Urls[PostProcessEvent].path
	client := &http.Client{
		Timeout: 40 * time.Second,
	}

	logger.Debugf("upgrade post process event msg is %v", string(jsonData))
	encryptMsg, err := EncryptMsg(jsonData)
	if err != nil {
		logger.Warning(err)
		return
	}
	request, err := http.NewRequest(Urls[PostProcessEvent].method, policyUrl, strings.NewReader(base64.StdEncoding.EncodeToString(encryptMsg)))
	if err != nil {
		logger.Warningf("%v new request failed: %v ", PostProcessEvent.string(), err.Error())
		return
	}
	hardwareId := GetHardwareId(m.config.IncludeDiskInfo, m.config.GetHardwareIdByHelper)

	request.Header.Set("X-MachineID", hardwareId)
	request.Header.Set("X-CurrentBaseline", m.preBaseline)
	request.Header.Set("X-Baseline", m.targetBaseline)
	request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.getToken())))
	response, err := client.Do(request)
	if err != nil {
		logger.Warningf("post process event msg failed:%v", err)
		return
	}
	_, _, _, err = getResponseData(response, PostProcessEvent)
	if err != nil {
		logger.Warningf("get post process event msg response failed:%v", err)
	}
}

// PostStatusMessage 将检查\下载\安装过程中所有异常状态和每个阶段成功的正常状态上报
func (m *UpdatePlatformManager) PostStatusMessage(message StatusMessage, forceUpload bool) {
	if (m.config.PlatformDisabled & Cfg.DisabledProcess) != 0 {
		return
	}

	if !m.config.UpdateProcessUpload && m.config.IntranetUpdate && !forceUpload {
		return
	}

	if m.targetBaseline == "" {
		logger.Warning("target baseline is empty")
		return
	}

	msg, err := json.Marshal(message)
	if err != nil {
		logger.Warningf("marshal status message failed:%v", err)
		return
	}

	logger.Debugf("post status msg:%s", msg)

	buf := bytes.NewBufferString(string(msg))
	filePath := fmt.Sprintf("/tmp/%s_%s.xz", "update", time.Now().Format("20231019102233444"))
	response, err := m.genPostProcessResponse(buf, filePath)
	if err != nil {
		logger.Warningf("post status message failed:%v", err)
		return
	}
	data, _, _, err := getResponseData(response, PostProcess)
	if err != nil {
		logger.Warningf("get post status response failed:%v", err)
		return
	}
	logger.Info(string(data))
}

func tarFiles(files []string, outFile string) error {
	// 创建tar包文件
	tarFile, err := os.Create(outFile)
	if err != nil {
		logger.Warning("create tar failed:", err)
		return err
	}
	defer tarFile.Close()

	// 创建tar包写入器
	tarWriter := tar.NewWriter(tarFile)
	defer tarWriter.Close()
	// 将文件添加到tar包中
	for _, filePath := range files {
		file, err := os.Open(filePath)
		if err != nil {
			logger.Warning("open file failed:", err)
			return err
		}
		defer file.Close()

		// 获取文件信息
		info, err := file.Stat()
		if err != nil {
			logger.Warning("get file info err:", err)
			return err
		}

		// 创建tar头部信息
		header := new(tar.Header)
		header.Name = filepath.Base(filePath)
		header.Size = info.Size()
		header.Mode = int64(info.Mode())
		header.ModTime = info.ModTime()

		// 写入tar头部信息
		if err := tarWriter.WriteHeader(header); err != nil {
			logger.Warning("create tar header failed:", err)
			return err
		}

		// 写入文件内容到tar包
		if _, err := io.Copy(tarWriter, file); err != nil {
			logger.Warning("input data to tar failed:", err)
			return err
		}
	}
	return nil
}

// PostUpdateLogFiles 将更新日志上传
func (m *UpdatePlatformManager) PostUpdateLogFiles(files []string) {
	if (m.config.PlatformDisabled & Cfg.DisabledProcess) != 0 {
		return
	}
	hardwareId := GetHardwareId(m.config.IncludeDiskInfo, m.config.GetHardwareIdByHelper)

	outFilename := fmt.Sprintf("/tmp/%s_%s_%s_%s.tar", "update", hardwareId, utils.GenUuid(), time.Now().Format("20231019102233444"))
	err := tarFiles(files, outFilename)
	if err != nil {
		logger.Warningf("tar log files failed:%v", err)
		return
	}
	tarFile, err := os.Open(outFilename)
	if err != nil {
		logger.Warning("open file failed:", err)
		return
	}
	defer tarFile.Close()
	response, err := m.genPostProcessResponse(tarFile, outFilename+".xz")
	if err != nil {
		logger.Warningf("post status message failed:%v", err)
		return
	}
	data, _, _, err := getResponseData(response, PostProcess)
	if err != nil {
		logger.Warningf("get post status response failed:%v", err)
		return
	}
	logger.Info(string(data))
}

func (m *UpdatePlatformManager) needPostSystemUpgradeMessage(mode system.UpdateType) bool {
	var editionName string
	infoMap, err := GetOSVersionInfo(CacheVersion)
	if err != nil {
		logger.Warning(err)
	} else {
		editionName = infoMap["EditionName"]
	}
	return strv.Strv(m.config.AllowPostSystemUpgradeMessageVersion).Contains(editionName) && ((mode & m.allowPostSystemUpgradeMessageType) != 0)
}

// CreateJobPostMsgInfo 初始化创建上报信息
func (m *UpdatePlatformManager) CreateJobPostMsgInfo(uuid string, updateType system.UpdateType) {
	if !m.needPostSystemUpgradeMessage(updateType) {
		return
	}
	info := &UpgradePostMsg{
		Uuid:             uuid,
		UpgradeStartTime: time.Now().Unix(),
		PostStatus:       NotReady,
		TaskId:           m.taskID,
	}
	info.save()
	m.jobPostMsgMapMu.Lock()
	defer m.jobPostMsgMapMu.Unlock()
	m.jobPostMsgMap[uuid] = info
	return
}

// SaveJobPostMsgByUUID 需要在success或failed状态迁移前调用,保证数据存储
func (m *UpdatePlatformManager) SaveJobPostMsgByUUID(uuid string, upgradeStatus UpgradeResult, Description string) {
	m.jobPostMsgMapMu.Lock()
	defer m.jobPostMsgMapMu.Unlock()
	if msg, ok := m.jobPostMsgMap[uuid]; ok {
		var upgradeErrorMsg string
		if upgradeStatus == UpgradeFailed || upgradeStatus == CheckFailed {
			upgradeErrorMsg = Description
		}
		hardwareId := GetHardwareId(m.config.IncludeDiskInfo, m.config.GetHardwareIdByHelper)
		msg.MachineID = hardwareId
		msg.UpgradeStatus = upgradeStatus
		msg.UpgradeErrorMsg = upgradeErrorMsg
		msg.PreBuild = m.preBuild
		msg.NextShowVersion = m.targetVersion
		msg.PreBaseline = m.preBaseline
		msg.NextBaseline = m.targetBaseline
		msg.UpgradeEndTime = time.Now().Unix()
		msg.updatePostStatus(WaitPost)
	}
}

// PostSystemUpgradeMessage 发送系统更新成功或失败的状态
func (m *UpdatePlatformManager) PostSystemUpgradeMessage(uuid string) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.jobPostMsgMapMu.Lock()
	defer m.jobPostMsgMapMu.Unlock()
	msg, ok := m.jobPostMsgMap[uuid]
	if !ok {
		return
	}
	msg.updateTimeStamp()
	content, err := json.Marshal(msg)
	if err != nil {
		logger.Warning(err)
		return
	}

	logger.Debugf("upgrade post content is %v", string(content))
	encryptMsg, err := EncryptMsg(content)
	if err != nil {
		logger.Warning(err)
		return
	}
	base64EncodeString := base64.StdEncoding.EncodeToString(encryptMsg)
	client := &http.Client{
		Timeout: 4 * time.Second,
	}
	requestUrl := m.requestUrl + "/api/v1/update/status"
	request, err := http.NewRequest("POST", requestUrl, strings.NewReader(base64EncodeString))
	if err != nil {
		logger.Warning(err)
		return
	}
	response, err := client.Do(request)
	if err == nil {
		defer func() {
			_ = response.Body.Close()
		}()
		body, _ := io.ReadAll(response.Body)
		logger.Debug("postSystemUpgradeMessage response is:", string(body))
		msg.updatePostStatus(PostSuccess)
		delete(m.jobPostMsgMap, uuid)
	} else {
		logger.Warning("post upgrade message failed:", err)
	}
}

func (m *UpdatePlatformManager) RetryPostHistory() {
	for _, v := range m.jobPostMsgMap {
		if v.PostStatus == WaitPost || v.PostStatus == PostFailure {
			m.PostSystemUpgradeMessage(v.Uuid)
		}
	}
	return
}

// PrepareCheckScripts decodes and deploys base64-encoded check scripts to the filesystem.
// It processes multiple types of check scripts, including:
//   - PreCheck/MidCheck/PostCheck: system update checks
//   - preUpdateCheck/postUpdateCheck: before/after checking updates
//   - preDownloadCheck/postDownloadCheck: before/after downloading updates
//   - preBackupCheck/postBackupCheck: before/after backup
//
// The function performs the following operations:
//  1. Cleans up existing script directories to remove stale scripts
//  2. Creates fresh directories only when scripts exist for a stage
//  3. Decodes base64-encoded script content from memory
//  4. Writes executable script files to the filesystem with 0755 permissions
//
// Script files are organized under /var/lib/lastore/check/ in subdirectories:
//   - pre_check/: for pre-update validation scripts (created when present)
//   - mid_check/: for mid-update validation scripts (created when present)
//   - post_check/: for post-update validation scripts (created when present)
//   - pre_update_check/: for pre-update-check validation scripts (created when present)
//   - post_update_check/: for post-update-check validation scripts (created when present)
//   - pre_download_check/: for pre-download validation scripts (created when present)
//   - post_download_check/: for post-download validation scripts (created when present)
//   - pre_backup_check/: for pre-backup validation scripts (created when present)
//   - post_backup_check/: for post-backup validation scripts (created when present)
//
// Any decode or file write errors are logged as warnings but do not stop
// the processing of remaining scripts.
func (m *UpdatePlatformManager) PrepareCheckScripts() {
	type checkGroup struct {
		list []ShellCheck
		dir  string
	}

	checkGroups := []checkGroup{
		{list: m.PreUpgradeCheck, dir: filepath.Join(check.CheckBaseDir, "pre_upgrade_check")},
		{list: m.MidUpgradeCheck, dir: filepath.Join(check.CheckBaseDir, "mid_upgrade_check")},
		{list: m.PostUpgradeCheck, dir: filepath.Join(check.CheckBaseDir, "post_upgrade_check")},
		{list: m.PreUpdateCheck, dir: filepath.Join(check.CheckBaseDir, "pre_update_check")},
		{list: m.PostUpdateCheck, dir: filepath.Join(check.CheckBaseDir, "post_update_check")},
		{list: m.PreDownloadCheck, dir: filepath.Join(check.CheckBaseDir, "pre_download_check")},
		{list: m.PostDownloadCheck, dir: filepath.Join(check.CheckBaseDir, "post_download_check")},
		{list: m.PreBackupCheck, dir: filepath.Join(check.CheckBaseDir, "pre_backup_check")},
		{list: m.PostBackupCheck, dir: filepath.Join(check.CheckBaseDir, "post_backup_check")},
	}

	for _, g := range checkGroups {
		_ = os.RemoveAll(g.dir)

		if len(g.list) == 0 {
			continue
		}

		logger.Info("Preparing check scripts in directory:", g.dir)

		if err := utils.EnsureDirExist(g.dir); err != nil {
			logger.Warning(err)
			continue
		}

		for _, c := range g.list {
			filePath := filepath.Join(g.dir, c.Name)
			// Remove padding for StdEncoding, "IyEvYmluL2Jhc2gKCmVjaG8gImhlbGxvIGRlZXBpbiI="
			shell := strings.TrimRight(c.Shell, "=")
			content, err := base64.RawStdEncoding.DecodeString(shell)
			if err != nil {
				logger.Warningf("decode shell for %s failed: %v", c.Name, err)
				continue
			}
			if err := utils.SyncWriteFile(filePath, content, 0755); err != nil {
				logger.Warningf("write file %s failed: %v", filePath, err)
				continue
			}
		}
	}
}

func (m *UpdatePlatformManager) SaveCache(c *Cfg.Config) {
	cache := platformCacheContent{}
	cache.CoreListPkgs = m.TargetCorePkgs
	cache.BaselinePkgs = m.BaselinePkgs
	cache.SelectPkgs = m.SelectPkgs
	cache.PreUpgradeCheck = m.PreUpgradeCheck
	cache.MidUpgradeCheck = m.MidUpgradeCheck
	cache.PostUpgradeCheck = m.PostUpgradeCheck
	cache.PreUpdateCheck = m.PreUpdateCheck
	cache.PostUpdateCheck = m.PostUpdateCheck
	cache.PreDownloadCheck = m.PreDownloadCheck
	cache.PostDownloadCheck = m.PostDownloadCheck
	cache.PreBackupCheck = m.PreBackupCheck
	cache.PostBackupCheck = m.PostBackupCheck
	content, err := json.Marshal(cache)
	if err != nil {
		logger.Warning("marshal cache failed:", err)
		return
	}
	err = c.SetOnlineCache(string(content))
	if err != nil {
		logger.Warning("save cache failed:", err)
	}
}

func (m *UpdatePlatformManager) PostUpgradeStatus(uuid string, upgradeStatus UpgradeResult, Description string) {
	m.SaveJobPostMsgByUUID(uuid, upgradeStatus, Description)
	go func() {
		if m.inhibitAutoQuit != nil && m.UnInhibitAutoQuit != nil {
			m.inhibitAutoQuit()
			defer m.inhibitAutoQuit()
		}
		m.PostSystemUpgradeMessage(uuid)
	}()
}

func (m *UpdatePlatformManager) SetInhibitAutoQuit() {

}

func (m *UpdatePlatformManager) saveTaskId() {
	kf := keyfile.NewKeyFile()
	if system.NormalFileExists(cacheTaskInfo) {
		err := kf.LoadFromFile(cacheTaskInfo)
		if err != nil {
			logger.Warning(err)
			return
		}
	}
	kf.SetInt("General", "TaskID", m.taskID)
	err := kf.SaveToFile(cacheTaskInfo)
	if err != nil {
		logger.Warning(err)
		return
	}
}

func getTaskId() int {
	kf := keyfile.NewKeyFile()
	err := kf.LoadFromFile(cacheTaskInfo)
	if err != nil {
		logger.Warning(err)
		return 0
	}
	content, err := kf.GetInt("General", "TaskID")
	if err != nil {
		logger.Warning(err)
		return 0
	}
	return content
}

// UpdateRequestUrl 更新平台请求地址
func (m *UpdatePlatformManager) UpdateRequestUrl(url string) {
	m.requestUrl = url
}

func updateNoLimitConfigIfChanged(currentValue string, setFunc func(string) error) error {
	var existingRate ratelimit.RateInfo
	if currentValue != "" {
		if err := json.Unmarshal([]byte(currentValue), &existingRate); err == nil {
			if existingRate.LimitType == ratelimit.RateLimitTypeNo {
				return nil
			}
			newRate := ratelimit.RateInfo{
				LimitType:   ratelimit.RateLimitTypeNo,
				LimitRate:   existingRate.LimitRate,
				CurrentRate: existingRate.CurrentRate,
				StartTime:   existingRate.StartTime,
				EndTime:     existingRate.EndTime,
			}
			data, err := json.Marshal(&newRate)
			if err != nil {
				return err
			}
			return setFunc(string(data))
		}
	}
	newRate := ratelimit.RateInfo{
		LimitType:   ratelimit.RateLimitTypeNo,
		LimitRate:   ratelimit.DefaultRateLimit,
		CurrentRate: ratelimit.DefaultRateLimit,
	}
	data, err := json.Marshal(&newRate)
	if err != nil {
		return err
	}
	return setFunc(string(data))
}

func (m *UpdatePlatformManager) UpdateDeliverySpeedLimit() error {
	if m.IPFSConfig.UploadLimit == nil || m.IPFSConfig.DownloadLimit == nil {
		return nil
	}

	uploadLimitRate, err := ratelimit.GetIPFSLimitRateBySyncLimit(*m.IPFSConfig.UploadLimit)
	if err != nil {
		return fmt.Errorf("failed to get upload limit rate: %w", err)
	}
	downloadLimitRate, err := ratelimit.GetIPFSLimitRateBySyncLimit(*m.IPFSConfig.DownloadLimit)
	if err != nil {
		return fmt.Errorf("failed to get download limit rate: %w", err)
	}

	localUploadRateLimit := ratelimit.GetLocalRateLimitFromConfig(m.config.DeliveryLocalUploadGlobalLimit, m.config.DeliveryLocalUploadPeakLimit, m.config.DeliveryLocalUploadOffPeakLimit)
	localDownloadRateLimit := ratelimit.GetLocalRateLimitFromConfig(m.config.DeliveryLocalDownloadGlobalLimit, m.config.DeliveryLocalDownloadPeakLimit, m.config.DeliveryLocalDownloadOffPeakLimit)
	if localDownloadRateLimit.Global == nil {
		localDownloadRateLimit.Global = &ratelimit.RateInfo{
			LimitType:   ratelimit.RateLimitTypeNo,
			LimitRate:   int64(ratelimit.DefaultRateLimit),
			CurrentRate: int64(ratelimit.DefaultRateLimit),
		}
	}
	if localUploadRateLimit.Global == nil {
		localUploadRateLimit.Global = &ratelimit.RateInfo{
			LimitType:   ratelimit.RateLimitTypeNo,
			LimitRate:   int64(ratelimit.DefaultRateLimit),
			CurrentRate: int64(ratelimit.DefaultRateLimit),
		}
	}
	localDownloadRateLimit.Validate()
	localUploadRateLimit.Validate()

	loggerUpload, err := json.Marshal(localUploadRateLimit)
	if err != nil {
		return fmt.Errorf("failed to marshal upload rate limit: %w", err)
	}
	logger.Infof("upload rate limit: %s", string(loggerUpload))
	loggerDownload, err := json.Marshal(localDownloadRateLimit)
	if err != nil {
		return fmt.Errorf("failed to marshal download rate limit: %w", err)
	}
	logger.Infof("download rate limit: %s", string(loggerDownload))

	if localDownloadRateLimit != nil {
		downloadLimitRate.GlobalLimitLocal = localDownloadRateLimit.Global
		downloadLimitRate.BusyLimitLocal = localDownloadRateLimit.Busy
		downloadLimitRate.FreeLimitLocal = localDownloadRateLimit.Free
	}
	if localUploadRateLimit != nil {
		uploadLimitRate.GlobalLimitLocal = localUploadRateLimit.Global
		uploadLimitRate.BusyLimitLocal = localUploadRateLimit.Busy
		uploadLimitRate.FreeLimitLocal = localUploadRateLimit.Free
	}

	if err := setIPFSRateLimit(uploadLimitRate, downloadLimitRate); err != nil {
		return fmt.Errorf("failed to set ipfs rate limit: %w", err)
	}

	if uploadLimitRate.GlobalLimitRemote != nil {
		remoteUploadGlobalData, err := json.Marshal(uploadLimitRate.GlobalLimitRemote)
		if err == nil {
			if err := m.config.SetDeliveryRemoteUploadGlobalLimit(string(remoteUploadGlobalData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryRemoteUploadGlobalLimit, m.config.SetDeliveryRemoteUploadGlobalLimit); err != nil {
			logger.Warning(err)
		}
	}
	if uploadLimitRate.BusyLimitRemote != nil {
		remoteUploadPeakData, err := json.Marshal(uploadLimitRate.BusyLimitRemote)
		if err == nil {
			if err := m.config.SetDeliveryRemoteUploadPeakLimit(string(remoteUploadPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryRemoteUploadPeakLimit, m.config.SetDeliveryRemoteUploadPeakLimit); err != nil {
			logger.Warning(err)
		}
	}
	if uploadLimitRate.FreeLimitRemote != nil {
		remoteUploadOffPeakData, err := json.Marshal(uploadLimitRate.FreeLimitRemote)
		if err == nil {
			if err := m.config.SetDeliveryRemoteUploadOffPeakLimit(string(remoteUploadOffPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryRemoteUploadOffPeakLimit, m.config.SetDeliveryRemoteUploadOffPeakLimit); err != nil {
			logger.Warning(err)
		}
	}

	if downloadLimitRate.GlobalLimitRemote != nil {
		remoteDownloadGlobalData, err := json.Marshal(downloadLimitRate.GlobalLimitRemote)
		if err == nil {
			if err := m.config.SetDeliveryRemoteDownloadGlobalLimit(string(remoteDownloadGlobalData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryRemoteDownloadGlobalLimit, m.config.SetDeliveryRemoteDownloadGlobalLimit); err != nil {
			logger.Warning(err)
		}
	}
	if downloadLimitRate.BusyLimitRemote != nil {
		remoteDownloadPeakData, err := json.Marshal(downloadLimitRate.BusyLimitRemote)
		if err == nil {
			if err := m.config.SetDeliveryRemoteDownloadPeakLimit(string(remoteDownloadPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryRemoteDownloadPeakLimit, m.config.SetDeliveryRemoteDownloadPeakLimit); err != nil {
			logger.Warning(err)
		}
	}
	if downloadLimitRate.FreeLimitRemote != nil {
		remoteDownloadOffPeakData, err := json.Marshal(downloadLimitRate.FreeLimitRemote)
		if err == nil {
			if err := m.config.SetDeliveryRemoteDownloadOffPeakLimit(string(remoteDownloadOffPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryRemoteDownloadOffPeakLimit, m.config.SetDeliveryRemoteDownloadOffPeakLimit); err != nil {
			logger.Warning(err)
		}
	}

	if uploadLimitRate.GlobalLimitLocal != nil {
		localUploadGlobalData, err := json.Marshal(uploadLimitRate.GlobalLimitLocal)
		if err == nil {
			if err := m.config.SetDeliveryLocalUploadGlobalLimit(string(localUploadGlobalData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryLocalUploadGlobalLimit, m.config.SetDeliveryLocalUploadGlobalLimit); err != nil {
			logger.Warning(err)
		}
	}
	if uploadLimitRate.BusyLimitLocal != nil {
		localUploadPeakData, err := json.Marshal(uploadLimitRate.BusyLimitLocal)
		if err == nil {
			if err := m.config.SetDeliveryLocalUploadPeakLimit(string(localUploadPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryLocalUploadPeakLimit, m.config.SetDeliveryLocalUploadPeakLimit); err != nil {
			logger.Warning(err)
		}
	}
	if uploadLimitRate.FreeLimitLocal != nil {
		localUploadOffPeakData, err := json.Marshal(uploadLimitRate.FreeLimitLocal)
		if err == nil {
			if err := m.config.SetDeliveryLocalUploadOffPeakLimit(string(localUploadOffPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryLocalUploadOffPeakLimit, m.config.SetDeliveryLocalUploadOffPeakLimit); err != nil {
			logger.Warning(err)
		}
	}

	if downloadLimitRate.GlobalLimitLocal != nil {
		localDownloadGlobalData, err := json.Marshal(downloadLimitRate.GlobalLimitLocal)
		if err == nil {
			if err := m.config.SetDeliveryLocalDownloadGlobalLimit(string(localDownloadGlobalData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryLocalDownloadGlobalLimit, m.config.SetDeliveryLocalDownloadGlobalLimit); err != nil {
			logger.Warning(err)
		}
	}
	if downloadLimitRate.BusyLimitLocal != nil {
		localDownloadPeakData, err := json.Marshal(downloadLimitRate.BusyLimitLocal)
		if err == nil {
			if err := m.config.SetDeliveryLocalDownloadPeakLimit(string(localDownloadPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryLocalDownloadPeakLimit, m.config.SetDeliveryLocalDownloadPeakLimit); err != nil {
			logger.Warning(err)
		}
	}
	if downloadLimitRate.FreeLimitLocal != nil {
		localDownloadOffPeakData, err := json.Marshal(downloadLimitRate.FreeLimitLocal)
		if err == nil {
			if err := m.config.SetDeliveryLocalDownloadOffPeakLimit(string(localDownloadOffPeakData)); err != nil {
				logger.Warning(err)
			}
		}
	} else {
		if err := updateNoLimitConfigIfChanged(m.config.DeliveryLocalDownloadOffPeakLimit, m.config.SetDeliveryLocalDownloadOffPeakLimit); err != nil {
			logger.Warning(err)
		}
	}

	return nil
}

func (m *UpdatePlatformManager) UpdateDeliverySpeedLimitWithConfig(ipfsConfig ratelimit.IPFSConfig) error {
	m.IPFSConfig = ipfsConfig
	return m.UpdateDeliverySpeedLimit()
}

func noRemoteIPFSLimitConfig() ratelimit.IPFSConfig {
	return ratelimit.IPFSConfig{
		UploadLimit:   defaultNoRemoteSyncLimit(),
		DownloadLimit: defaultNoRemoteSyncLimit(),
	}
}

func defaultNoRemoteSyncLimit() *ratelimit.SyncLimit {
	return &ratelimit.SyncLimit{
		AllDayRateLimit:   defaultNoRemoteRateLimitWithTime(),
		BusyTimeRateLimit: defaultNoRemoteRateLimitWithTime(),
		FreeTimeRateLimit: defaultNoRemoteRateLimitWithTime(),
	}
}

func defaultNoRemoteRateLimitWithTime() *ratelimit.RateLimitWithTime {
	return &ratelimit.RateLimitWithTime{
		RateLimit: ratelimit.DefaultRateLimit,
		Type:      ratelimit.RateLimitTypeNo,
	}
}

func resetSpeedLimitConfigToDefaults(c *Cfg.Config) {
	c.UpgradeDeliveryEnabled = true
	c.DownloadSpeedLimitConfig = defaultDownloadSpeedLimitConfig
	c.LocalDownloadSpeedLimitConfig = defaultDownloadSpeedLimitConfig
	c.DeliveryRemoteDownloadGlobalLimit = defaultDeliveryRateLimitConfig
	c.DeliveryRemoteUploadGlobalLimit = defaultDeliveryRateLimitConfig
	c.DeliveryRemoteDownloadPeakLimit = defaultDeliveryRateLimitConfig
	c.DeliveryRemoteUploadPeakLimit = defaultDeliveryRateLimitConfig
	c.DeliveryRemoteDownloadOffPeakLimit = defaultDeliveryRateLimitConfig
	c.DeliveryRemoteUploadOffPeakLimit = defaultDeliveryRateLimitConfig
	c.DeliveryLocalDownloadGlobalLimit = defaultDeliveryRateLimitConfig
	c.DeliveryLocalUploadGlobalLimit = defaultDeliveryRateLimitConfig
	c.DeliveryLocalDownloadPeakLimit = defaultDeliveryRateLimitConfig
	c.DeliveryLocalUploadPeakLimit = defaultDeliveryRateLimitConfig
	c.DeliveryLocalDownloadOffPeakLimit = defaultDeliveryRateLimitConfig
	c.DeliveryLocalUploadOffPeakLimit = defaultDeliveryRateLimitConfig
}

// 416 indicates uninstallation is required
func (m *UpdatePlatformManager) tryToUnRegisterConsole() (bool, error) {
	if utils.IsFileExist("/usr/lib/iup-daemon/uninstall") {
		logger.Infof("executing uninstall script")
		cmd := exec.Command("/usr/lib/iup-daemon/uninstall")
		cmd.Env = append(os.Environ(), "IMMUTABLE_DISABLE_REMOUNT=false")
		output, err := cmd.CombinedOutput()
		if err != nil {
			logger.Warningf("failed to exec uninstall. output:%v", string(output))
			return false, err
		}

		m.resetIntranetUpdateSettingsAfterUnregister()

		logger.Infof("uninstall script executed successfully. output: %v", string(output))
		return true, nil
	}
	return false, nil
}

func (m *UpdatePlatformManager) resetIntranetUpdateSettingsAfterUnregister() {
	if m == nil || m.config == nil {
		return
	}

	// 反注册相当于从内网平台切换成公网，必须同步恢复持久化配置和当前进程内配置。
	if err := m.config.SetLastCheckTime(time.Unix(0, 0)); err != nil {
		logger.Warningf("failed to set last check time: %v", err)
	}

	for _, key := range []string{
		Cfg.DSettingsKeyCheckInterval,
		Cfg.DSettingsKeyStartCheckRange,
		Cfg.DSettingsKeyDownloadSpeedLimit,
		Cfg.DSettingsKeyUpgradeDeliveryEnabled,
		Cfg.DSettingsKeyIntranetUpdate,
		Cfg.DSettingsKeyPlatformUpdate,
		Cfg.DSettingsKeyDeliveryRemoteDownloadGlobalLimit,
		Cfg.DSettingsKeyDeliveryRemoteUploadGlobalLimit,
		Cfg.DSettingsKeyDeliveryRemoteDownloadPeakLimit,
		Cfg.DSettingsKeyDeliveryRemoteUploadPeakLimit,
		Cfg.DSettingsKeyDeliveryRemoteDownloadOffPeakLimit,
		Cfg.DSettingsKeyDeliveryRemoteUploadOffPeakLimit,
	} {
		if err := m.config.ResetDSettings(key); err != nil {
			logger.Warningf("failed to reset %s: %v", key, err)
		}
	}
	resetSpeedLimitConfigToDefaults(m.config)

	if err := m.UpdateDeliverySpeedLimitWithConfig(noRemoteIPFSLimitConfig()); err != nil {
		logger.Warningf("failed to reset delivery speed limit: %v", err)
	}
}
