const{app:app}=require("electron"),path=require("path"),fs=require("fs-extra"),axios=require("axios"),crypto=require("crypto"),extract=require("extract-zip"),appConfig=require("./config"),https=require("https"),http=require("http");class HotUpdater{async checkUpdate(){const about_url=appConfig.getAll().about_url;try{const data=(await axios.get(about_url)).data;if(data.success&&data.latestVersion!==appConfig.getVersion())return data}catch(error){}return null}async doUpdate(event,updateInfo){const{latestVersion:latestVersion,downloadUrl:downloadUrl,MD5:MD5,forceOverlay:forceOverlay}=updateInfo,UPDATES_ROOT=appConfig.getUpdatePath(),VERSION_FILE=path.join(UPDATES_ROOT,"version-info.json"),tempPath=(path.join(UPDATES_ROOT,"bgxiong-config.json"),path.join(appConfig.getUserDataPath(),"bgxiong_temp")),zipPath=path.join(tempPath,`bgxiong_${latestVersion}.zip`),extractPath=path.join(UPDATES_ROOT,`${latestVersion}`),sendProgress=(percent,message,status="progress")=>{event?.sender&&event.sender.send("update-progress",{percent:percent,message:message,status:status,source:"update"})};try{fs.existsSync(UPDATES_ROOT)||fs.mkdirSync(UPDATES_ROOT,{recursive:!0}),fs.existsSync(tempPath)||fs.mkdirSync(tempPath,{recursive:!0}),sendProgress(0,"正在下载更新包..."),await this._downloadFile(downloadUrl,zipPath,progress=>{sendProgress(progress,`下载中 ${progress}%`)}),sendProgress(100,"正在解压..."),fs.existsSync(extractPath)&&await fs.remove(extractPath),await extract(zipPath,{dir:extractPath}),fs.remove(zipPath),sendProgress(100,"解压完成 清理旧版本...","done");const allItemsInUpdatesRoot=await fs.readdir(UPDATES_ROOT);for(const item of allItemsInUpdatesRoot){const fullPath=path.join(UPDATES_ROOT,item);(await fs.stat(fullPath)).isDirectory()&&item!==latestVersion&&await fs.remove(fullPath)}sendProgress(100,"正在安装新版文件...请勿中断...","done");const subDirectories=(await fs.readdir(extractPath,{withFileTypes:!0})).filter(item=>item.isDirectory()).map(dirent=>dirent.name),platformKeyword=process.platform;let targetDirName=null,unwantedDirName=null;if("win32"===platformKeyword?(targetDirName=subDirectories.find(dir=>dir.toLowerCase().includes("win")),unwantedDirName=subDirectories.find(dir=>dir.toLowerCase().includes("mac"))):"darwin"===platformKeyword&&(targetDirName=subDirectories.find(dir=>dir.toLowerCase().includes("mac")),unwantedDirName=subDirectories.find(dir=>dir.toLowerCase().includes("win"))),unwantedDirName){const unwantedFullPath=path.join(extractPath,unwantedDirName);await fs.remove(unwantedFullPath)}if(sendProgress(100,"正在更新文件...请勿中断...","done"),await fs.writeJson(VERSION_FILE,{version:latestVersion,updateTime:(new Date).toISOString(),MD5:MD5,downloadUrl:downloadUrl,path:extractPath}),sendProgress(100,"最后一步，马上完成升级...","done"),appConfig.setVersion(latestVersion),forceOverlay){if(sendProgress(0,"开始覆盖安装，请勿中断..."),0===subDirectories.length);else{const targetSourcePath=path.join(extractPath,targetDirName),appInstallPath=app.getAppPath();if(!fs.existsSync(targetSourcePath))throw new Error(`强制覆盖失败：目标平台目录不存在 -> ${targetSourcePath}`);await fs.copy(targetSourcePath,appInstallPath,{overwrite:!0,recursive:!0}),sendProgress(100,"强制覆盖安装完成，准备重启","done")}const BGXIONG_CONFIG_FILE=path.join(UPDATES_ROOT,"bgxiong-config.json"),INSTALL_CONFIG_FILE=path.join(app.getAppPath(),"config.json");fs.existsSync(BGXIONG_CONFIG_FILE)&&await fs.copy(BGXIONG_CONFIG_FILE,INSTALL_CONFIG_FILE,{overwrite:!0})}return sendProgress(100,"升级完成，准备重启","done"),!0}catch(error){return!forceOverlay&&fs.existsSync(extractPath)&&await fs.remove(extractPath).catch(e=>{}),event?.sender&&event.sender.send("update-progress",{status:"error",message:`升级失败：${error.message}`,source:"update"}),!1}}_downloadFile(url,dest,onProgress){return new Promise((resolve,reject)=>{const request=(url.startsWith("https")?https:http).get(url,response=>{if(200!==response.statusCode)return reject(new Error(`HTTP ${response.statusCode}`));const totalBytes=parseInt(response.headers["content-length"],10)||0;let downloadedBytes=0;const fileStream=fs.createWriteStream(dest);response.on("data",chunk=>{if(downloadedBytes+=chunk.length,totalBytes>0&&"function"==typeof onProgress){const percent=Math.round(downloadedBytes/totalBytes*100);onProgress(percent)}}),response.pipe(fileStream),fileStream.on("finish",()=>{fileStream.close(),resolve()}),fileStream.on("error",err=>{fs.unlink(dest,()=>{}),reject(err)})});request.on("error",reject),request.setTimeout(6e4,()=>{request.destroy(),reject(new Error("下载超时"))})})}_verifyFileMd5(filePath,expectedMd5){return new Promise(resolve=>{if(!expectedMd5)return resolve(!0);const hash=crypto.createHash("md5"),stream=fs.createReadStream(filePath);stream.on("data",data=>hash.update(data)),stream.on("end",()=>{const actualMd5=hash.digest("hex").toLowerCase();resolve(actualMd5===expectedMd5.toLowerCase())}),stream.on("error",()=>resolve(!1))})}}module.exports=new HotUpdater;