AirLibrary/Updates/
mod.rs

1//! # Update Management Service
2//!
3//! Comprehensive update management for the Land ecosystem with full lifecycle
4//! support:
5//! - Version availability checking against update servers
6//! - Secure download with cryptographic signature verification
7//! - Multi-checksum integrity validation (SHA256, MD5, CRC32)
8//! - Staged installation with atomic application
9//! - Automatic rollback on installation failure
10//! - Platform-specific update packages (macOS dmg, Windows exe, Linux AppImage)
11//! - Update channel management (stable, insiders, preview)
12//! - Delta updates for reduced download size
13//! - Network interruption recovery with resume capability
14//! - Disk space validation before download
15//! - Backup creation before applying updates
16//!
17//! # VSCode Update System References
18//! This implementation draws inspiration from VSCode's update architecture:
19//! - Background update checking without interrupting user workflow
20//! - Deferred installation at application restart
21//! - Update verification with multiple checksums
22//! - Telemetry for update success/failure tracking
23//! - Circuit breakers for update server resilience
24//!
25//! # Architecture
26//! The update manager coordinates with:
27//! - Mountain: Provides frontend notification of available updates
28//! - The entire Land ecosystem: Updates can apply to multiple components
29//! - Update servers: REST API endpoints for version checks and downloads
30//!
31//! # Connection to VSCode's Update Download Service Architecture
32//!
33//! The Air update manager draws inspiration from VSCode's update system:
34//! 1. **Separate Update Process**: Like VSCode, Air runs updates in the
35//!    background
36//! 2. **Deferred Installation**: Updates are downloaded and staged, then
37//!    applied on restart
38//! 3. **Progress Reporting**: Updates report progress to the frontend
39//!    (Mountain)
40//! 4. **Resilient Downloads**: Support for resume after interruption
41//! 5. **Multiple Integrity Checks**: SHA256, MD5, and cryptographic signatures
42//! 6. **Automatic Rollback**: Like VSCode's safe mode, Air can roll back on
43//!    failure
44//!
45//! Air handles updates for the entire Land ecosystem:
46//! - **Air daemon**: The background service itself
47//! - **Mountain**: The frontend Electron application
48//! - **Other elements**: Can update other Land components if needed
49//!
50//! Update coordination with Mountain:
51//! - When an update is available, Air notifies Mountain via event bus
52//! - Mountain displays update notification to the user
53//! - User selects whether to download/install/update
54//! - Mountain can request status, cancel downloads, or initiate installation
55//!
56//! ## VSCode Update System Reference
57//!
58//! VSCode's update system (similar to Atom's) uses:
59//! - Electron's auto-updater module for Windows/macOS AppImages
60//! - Native Linux package managers for deb/rpm
61//! - Background update checking without interrupting user
62//! - Deferred installation on application restart
63//! - Multi-channel support (stable, insiders, exploration)
64//!
65//! # TODO
66//! - Delta update support: Download only changed files between versions
67//! - Rollback system: Automatic and manual rollback to previous versions
68//! - Staged installations: Pre-verify updates before user prompt
69//! - Update signatures: Ed25519 or PGP signature verification
70//! - Update package format: Custom package format for cross-platform support
71
72use std::{
73	collections::HashMap,
74	path::{Path, PathBuf},
75	sync::Arc,
76	time::Duration,
77};
78
79use serde::{Deserialize, Serialize};
80use tokio::{
81	sync::{Mutex, RwLock},
82	time::{interval, sleep},
83};
84use sha2::{Digest, Sha256};
85use uuid::Uuid;
86use md5;
87
88use crate::{AirError, ApplicationState::ApplicationState, Configuration::ConfigurationManager, Result};
89
90/// Update manager implementation with full lifecycle support
91pub struct UpdateManager {
92	/// Application state
93	AppState:Arc<ApplicationState>,
94
95	/// Current update status
96	update_status:Arc<RwLock<UpdateStatus>>,
97
98	/// Update cache directory
99	cache_directory:PathBuf,
100
101	/// Staging directory for pre-installation verification
102	staging_directory:PathBuf,
103
104	/// Backup directory for rollback capability
105	backup_directory:PathBuf,
106
107	/// Active download sessions with resume capability
108	download_sessions:Arc<RwLock<HashMap<String, DownloadSession>>>,
109
110	/// Rollback history (max 5 versions)
111	rollback_history:Arc<Mutex<RollbackHistory>>,
112
113	/// Update channel configuration
114	update_channel:UpdateChannel,
115
116	/// Platform-specific configuration
117	platform_config:PlatformConfig,
118}
119
120/// Download session for resumable downloads
121#[derive(Debug, Clone)]
122struct DownloadSession {
123	/// Session unique identifier
124	session_id:String,
125
126	/// Original update URL
127	download_url:String,
128
129	/// Current file path
130	temp_path:PathBuf,
131
132	/// Bytes downloaded so far
133	downloaded_bytes:u64,
134
135	/// Total file size
136	total_bytes:u64,
137
138	/// Whether download is complete
139	complete:bool,
140}
141
142/// Rollback history for automatic and manual rollback
143#[derive(Debug, Clone, Serialize, Deserialize)]
144struct RollbackHistory {
145	/// Previous versions available for rollback
146	versions:Vec<RollbackState>,
147
148	/// Maximum number of versions to keep
149	max_versions:usize,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
153struct RollbackState {
154	version:String,
155	backup_path:PathBuf,
156	timestamp:chrono::DateTime<chrono::Utc>,
157	checksum:String,
158}
159
160/// Update channel configuration
161#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
162pub enum UpdateChannel {
163	Stable,
164	Insiders,
165	Preview,
166}
167
168impl UpdateChannel {
169	fn as_str(&self) -> &'static str {
170		match self {
171			UpdateChannel::Stable => "stable",
172			UpdateChannel::Insiders => "insiders",
173			UpdateChannel::Preview => "preview",
174		}
175	}
176}
177
178/// Platform-specific update configuration
179#[derive(Debug, Clone)]
180struct PlatformConfig {
181	platform:String,
182	arch:String,
183	package_format:PackageFormat,
184}
185
186/// Supported package formats by platform
187#[derive(Debug, Clone, Copy)]
188enum PackageFormat {
189	WindowsExe,
190	MacOsDmg,
191	LinuxAppImage,
192	LinuxDeb,
193	LinuxRpm,
194}
195
196/// Update status with comprehensive state tracking
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct UpdateStatus {
199	/// Last time updates were checked
200	pub last_check:Option<chrono::DateTime<chrono::Utc>>,
201
202	/// Whether an update is available
203	pub update_available:bool,
204
205	/// Current installed version
206	pub current_version:String,
207
208	/// Available version (if any)
209	pub available_version:Option<String>,
210
211	/// Download progress (0.0 to 100.0)
212	pub download_progress:Option<f32>,
213
214	/// Current installation status
215	pub installation_status:InstallationStatus,
216
217	/// Update channel being used
218	pub update_channel:UpdateChannel,
219
220	/// Size of available update in bytes
221	pub update_size:Option<u64>,
222
223	/// Release notes for available update
224	pub release_notes:Option<String>,
225
226	/// Whether update requires restart
227	pub requires_restart:bool,
228
229	/// Download speed in bytes per second
230	pub download_speed:Option<f64>,
231
232	/// Estimated time remaining in seconds
233	pub eta_seconds:Option<u64>,
234
235	/// Last error message (if any)
236	pub last_error:Option<String>,
237}
238
239/// Installation status with detailed state tracking
240#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
241pub enum InstallationStatus {
242	/// No update operation in progress
243	NotStarted,
244
245	/// Verifying disk space and prerequisites
246	CheckingPrerequisites,
247
248	/// Downloading update package
249	Downloading,
250
251	/// Download paused (resumable)
252	Paused,
253
254	/// Verifying cryptographic signatures
255	VerifyingSignature,
256
257	/// Verifying checksums (SHA256, MD5, etc.)
258	VerifyingChecksums,
259
260	/// Staging update for pre-installation verification
261	Staging,
262
263	/// Creating backup before applying update
264	CreatingBackup,
265
266	/// Installing update
267	Installing,
268
269	/// Installation completed, awaiting restart
270	Completed,
271
272	/// Rolling back due to installation failure
273	RollingBack,
274
275	/// Installation failed with error message
276	Failed(String),
277}
278
279impl InstallationStatus {
280	/// Check if the current status allows cancellation
281	pub fn is_cancellable(&self) -> bool {
282		matches!(
283			self,
284			InstallationStatus::Downloading
285				| InstallationStatus::Paused
286				| InstallationStatus::Staging
287				| InstallationStatus::NotStarted
288		)
289	}
290
291	/// Check if the current status represents an error
292	pub fn is_error(&self) -> bool { matches!(self, InstallationStatus::Failed(_)) }
293
294	/// Check if the current status represents progress
295	pub fn is_in_progress(&self) -> bool {
296		matches!(
297			self,
298			InstallationStatus::CheckingPrerequisites
299				| InstallationStatus::Downloading
300				| InstallationStatus::VerifyingSignature
301				| InstallationStatus::VerifyingChecksums
302				| InstallationStatus::Staging
303				| InstallationStatus::CreatingBackup
304				| InstallationStatus::Installing
305		)
306	}
307}
308
309/// Update information with comprehensive metadata
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct UpdateInfo {
312	/// Semantic version (e.g., "1.2.3")
313	pub version:String,
314
315	/// Download URL for the update package
316	pub download_url:String,
317
318	/// Release notes and changelog
319	pub release_notes:String,
320
321	/// Primary checksum (SHA256 recommended)
322	pub checksum:String,
323
324	/// Alternative checksums for verification
325	pub checksums:HashMap<String, String>,
326
327	/// Size of update package in bytes
328	pub size:u64,
329
330	/// When the update was published
331	pub published_at:chrono::DateTime<chrono::Utc>,
332
333	/// Whether this update is mandatory
334	pub is_mandatory:bool,
335
336	/// Whether update requires application restart
337	pub requires_restart:bool,
338
339	/// Minimum compatible version
340	pub min_compatible_version:Option<String>,
341
342	/// Delta update URL (if available for incremental update)
343	pub delta_url:Option<String>,
344
345	/// Delta update checksum (if available)
346	pub delta_checksum:Option<String>,
347
348	/// Delta update size (if available)
349	pub delta_size:Option<u64>,
350
351	/// Cryptographic signature (Ed25519 or PGP)
352	pub signature:Option<String>,
353
354	/// Platform-specific metadata
355	pub platform_metadata:Option<PlatformMetadata>,
356}
357
358/// Platform-specific update metadata
359#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct PlatformMetadata {
361	/// Package format (exe, dmg, appimage, etc.)
362	pub package_format:String,
363
364	/// Installation instructions
365	pub install_instructions:Vec<String>,
366
367	/// Required disk space in bytes
368	pub required_disk_space:u64,
369
370	/// Whether admin privileges are required
371	pub requires_admin:bool,
372
373	/// Additional platform-specific parameters
374	pub additional_params:HashMap<String, serde_json::Value>,
375}
376
377/// Update telemetry data for analytics
378#[derive(Debug, Clone, Serialize, Deserialize)]
379pub struct UpdateTelemetry {
380	/// Unique telemetry event ID
381	pub event_id:String,
382
383	/// Current version
384	pub current_version:String,
385
386	/// Target version
387	pub target_version:String,
388
389	/// Update channel
390	pub channel:String,
391
392	/// Platform identifier
393	pub platform:String,
394
395	/// Operation type (check, download, install, rollback)
396	pub operation:String,
397
398	/// Success or failure
399	pub success:bool,
400
401	/// Duration in milliseconds
402	pub duration_ms:u64,
403
404	/// Download size in bytes
405	pub download_size:Option<u64>,
406
407	/// Error message (if failed)
408	pub error_message:Option<String>,
409
410	/// Timestamp of the event
411	pub timestamp:chrono::DateTime<chrono::Utc>,
412}
413
414impl UpdateManager {
415	/// Create a new update manager with comprehensive initialization
416	pub async fn new(AppState:Arc<ApplicationState>) -> Result<Self> {
417		let config = &AppState.Configuration.Updates;
418
419		// Expand cache directory path
420		let cache_directory = ConfigurationManager::ExpandPath(&AppState.Configuration.Downloader.CacheDirectory)?;
421
422		// Create cache directory if it doesn't exist
423		tokio::fs::create_dir_all(&cache_directory)
424			.await
425			.map_err(|e| AirError::Configuration(format!("Failed to create cache directory: {}", e)))?;
426
427		// Create staging directory for pre-installation verification
428		let staging_directory = cache_directory.join("staging");
429		tokio::fs::create_dir_all(&staging_directory)
430			.await
431			.map_err(|e| AirError::Configuration(format!("Failed to create staging directory: {}", e)))?;
432
433		// Create backup directory for rollback
434		let backup_directory = cache_directory.join("backups");
435		tokio::fs::create_dir_all(&backup_directory)
436			.await
437			.map_err(|e| AirError::Configuration(format!("Failed to create backup directory: {}", e)))?;
438
439		// Determine platform-specific configuration
440		let PlatformConfig = Self::detect_platform();
441		let PlatformConfigClone = PlatformConfig.clone();
442
443		// Determine update channel from configuration
444		let update_channel = if config.Channel == "insiders" {
445			UpdateChannel::Insiders
446		} else if config.Channel == "preview" {
447			UpdateChannel::Preview
448		} else {
449			UpdateChannel::Stable
450		};
451
452		// Load or create rollback history
453		let rollback_history_path = backup_directory.join("rollback_history.json");
454		let rollback_history = if rollback_history_path.exists() {
455			let content = tokio::fs::read_to_string(&rollback_history_path)
456				.await
457				.map_err(|e| AirError::FileSystem(format!("Failed to read rollback history: {}", e)))?;
458			serde_json::from_str(&content).unwrap_or_else(|_| RollbackHistory { versions:Vec::new(), max_versions:5 })
459		} else {
460			RollbackHistory { versions:Vec::new(), max_versions:5 }
461		};
462
463		let manager = Self {
464			AppState,
465			update_status:Arc::new(RwLock::new(UpdateStatus {
466				last_check:None,
467				update_available:false,
468				current_version:env!("CARGO_PKG_VERSION").to_string(),
469				available_version:None,
470				download_progress:None,
471				installation_status:InstallationStatus::NotStarted,
472				update_channel,
473				update_size:None,
474				release_notes:None,
475				requires_restart:true,
476				download_speed:None,
477				eta_seconds:None,
478				last_error:None,
479			})),
480			cache_directory,
481			staging_directory,
482			backup_directory,
483			download_sessions:Arc::new(RwLock::new(HashMap::new())),
484			rollback_history:Arc::new(Mutex::new(rollback_history)),
485			update_channel,
486			platform_config:PlatformConfigClone,
487		};
488
489		// Initialize service status
490		manager
491			.AppState
492			.UpdateServiceStatus("updates", crate::ApplicationState::ServiceStatus::Running)
493			.await
494			.map_err(|e| AirError::Internal(e.to_string()))?;
495
496		log::info!(
497			"[UpdateManager] Update service initialized for platform: {}/{}",
498			PlatformConfig.platform,
499			PlatformConfig.arch
500		);
501
502		Ok(manager)
503	}
504
505	/// Detect the current platform and configure platform-specific settings
506	fn detect_platform() -> PlatformConfig {
507		let platform = if cfg!(target_os = "windows") {
508			"windows"
509		} else if cfg!(target_os = "macos") {
510			"macos"
511		} else if cfg!(target_os = "linux") {
512			"linux"
513		} else {
514			"unknown"
515		};
516
517		let arch = if cfg!(target_arch = "x86_64") {
518			"x64"
519		} else if cfg!(target_arch = "aarch64") {
520			"arm64"
521		} else if cfg!(target_arch = "x86") {
522			"ia32"
523		} else {
524			"unknown"
525		};
526
527		let package_format = match (platform, arch) {
528			("windows", _) => PackageFormat::WindowsExe,
529			("macos", _) => PackageFormat::MacOsDmg,
530			("linux", "x64") => PackageFormat::LinuxAppImage,
531			("linux", "") => PackageFormat::LinuxAppImage,
532			_ => PackageFormat::LinuxAppImage,
533		};
534
535		PlatformConfig { platform:platform.to_string(), arch:arch.to_string(), package_format }
536	}
537
538	/// Check for available updates from the configured update server
539	///
540	/// This method:
541	/// - Queries the update server based on the configured channel
542	/// - Validates the update against minimum compatibility requirements
543	/// - Updates the internal status with available update information
544	/// - Triggers automatic download if configured
545	///
546	/// Returns: Some(UpdateInfo) if an update is available, None otherwise
547	pub async fn CheckForUpdates(&self) -> Result<Option<UpdateInfo>> {
548		let config = &self.AppState.Configuration.Updates;
549		let start_time = std::time::Instant::now();
550
551		if !config.Enabled {
552			log::debug!("[UpdateManager] Updates are disabled");
553			return Ok(None);
554		}
555
556		log::info!(
557			"[UpdateManager] Checking for updates on {} channel",
558			self.update_channel.as_str()
559		);
560
561		// Update status
562		{
563			let mut status = self.update_status.write().await;
564			status.last_check = Some(chrono::Utc::now());
565			status.last_error = None;
566		}
567
568		// Check update server with resilience patterns
569		let update_info = match self.FetchUpdateInfo().await {
570			Ok(info) => info,
571			Err(e) => {
572				log::error!("[UpdateManager] Failed to fetch update info: {}", e);
573				let mut status = self.update_status.write().await;
574				status.last_error = Some(e.to_string());
575				self.record_telemetry(
576					"check",
577					false,
578					start_time.elapsed().as_millis() as u64,
579					None,
580					Some(e.to_string()),
581				)
582				.await;
583				return Err(e);
584			},
585		};
586
587		if let Some(ref info) = update_info {
588			// Verify minimum compatibility
589			if let Some(ref min_version) = info.min_compatible_version {
590				let current_version = env!("CARGO_PKG_VERSION");
591				if UpdateManager::CompareVersions(current_version, min_version) < 0 {
592					log::warn!(
593						"[UpdateManager] Update requires minimum version {} but current is {}. Skipping.",
594						min_version,
595						current_version
596					);
597					let mut status = self.update_status.write().await;
598					status.last_error = Some(format!("Update requires minimum version {}", min_version));
599					return Ok(None);
600				}
601			}
602
603			log::info!(
604				"[UpdateManager] Update available: {} ({})",
605				info.version,
606				self.format_size(info.size as f64)
607			);
608
609			// Update status
610			{
611				let mut status = self.update_status.write().await;
612				status.update_available = true;
613				status.available_version = Some(info.version.clone());
614				status.update_size = Some(info.size);
615				status.release_notes = Some(info.release_notes.clone());
616				status.requires_restart = info.requires_restart;
617			}
618
619			// Notify Mountain (frontend) about available update
620			// This would typically be done via event bus or gRPC
621			log::info!("[UpdateManager] Notifying frontend about available update");
622
623			// Record telemetry
624			self.record_telemetry("check", true, start_time.elapsed().as_millis() as u64, None, None)
625				.await;
626
627			// Auto-download if configured
628			if config.AutoDownload {
629				if let Err(e) = self.DownloadUpdate(info).await {
630					log::error!("[UpdateManager] Auto-download failed: {}", e);
631					// Don't fail the check, just log the error
632				}
633			}
634		} else {
635			log::info!("[UpdateManager] No updates available");
636
637			// Update status
638			{
639				let mut status = self.update_status.write().await;
640				status.update_available = false;
641				status.available_version = None;
642				status.update_size = None;
643				status.release_notes = None;
644			}
645
646			// Record telemetry
647			self.record_telemetry("check", true, start_time.elapsed().as_millis() as u64, None, None)
648				.await;
649		}
650
651		Ok(update_info)
652	}
653
654	/// Download update package with resumable download support
655	///
656	/// This method:
657	/// - Validates available disk space before starting download
658	/// - Supports resumable downloads from network interruptions
659	/// - Tracks download progress and calculates ETA
660	/// - Updates download speed metrics
661	/// - Verifies all checksums after download
662	///
663	/// # Arguments
664	/// * `update_info` - Update information containing download URL and
665	///   metadata
666	///
667	/// # Returns
668	/// Result<()> indicating success or failure
669	pub async fn DownloadUpdate(&self, update_info:&UpdateInfo) -> Result<()> {
670		let start_time = std::time::Instant::now();
671		let session_id = Uuid::new_v4().to_string();
672
673		log::info!(
674			"[UpdateManager] Starting download for version {} (session: {})",
675			update_info.version,
676			session_id
677		);
678
679		// Check prerequisites: disk space
680		let required_space = update_info.size * 2; // Need space for download + staging
681		self.ValidateDiskSpace(required_space).await?;
682
683		// Update status
684		{
685			let mut status = self.update_status.write().await;
686			status.installation_status = InstallationStatus::CheckingPrerequisites;
687			status.last_error = None;
688		}
689
690		// Create temp file path for download
691		let temp_path = self.cache_directory.join(format!("update-{}-temp.bin", update_info.version));
692		let final_path = self.cache_directory.join(format!("update-{}.bin", update_info.version));
693
694		// Check if there's an existing partial download to resume
695		let (downloaded_bytes, resume_from_start) = if temp_path.exists() {
696			let metadata = tokio::fs::metadata(&temp_path)
697				.await
698				.map_err(|e| AirError::FileSystem(format!("Failed to check temp file: {}", e)))?;
699			log::info!("[UpdateManager] Found partial download, resuming from {} bytes", metadata.len());
700			(metadata.len(), false)
701		} else {
702			(0, true)
703		};
704
705		// Create or open download session
706		{
707			let mut sessions = self.download_sessions.write().await;
708			sessions.insert(
709				session_id.clone(),
710				DownloadSession {
711					session_id:session_id.clone(),
712					download_url:update_info.download_url.clone(),
713					temp_path:temp_path.clone(),
714					downloaded_bytes,
715					total_bytes:update_info.size,
716					complete:false,
717				},
718			);
719		}
720
721		// Begin download
722		let client = reqwest::Client::builder()
723			.timeout(Duration::from_secs(300))
724			.build()
725			.map_err(|e| AirError::Network(format!("Failed to create HTTP client: {}", e)))?;
726
727		let mut request_builder = client.get(&update_info.download_url);
728
729		// Add range header for resume
730		if !resume_from_start {
731			request_builder = request_builder.header("Range", format!("bytes={}-", downloaded_bytes));
732		}
733
734		let response = request_builder
735			.send()
736			.await
737			.map_err(|e| AirError::Network(format!("Failed to start download: {}", e)))?;
738
739		if !response.status().is_success() && response.status() != 206 {
740			log::error!("[UpdateManager] Download failed with status: {}", response.status());
741			let mut status = self.update_status.write().await;
742			status.installation_status =
743				InstallationStatus::Failed(format!("Download failed with status: {}", response.status()));
744			status.last_error = Some(format!("Download failed with status: {}", response.status()));
745			self.record_telemetry(
746				"download",
747				false,
748				start_time.elapsed().as_millis() as u64,
749				None,
750				Some("Download failed".to_string()),
751			)
752			.await;
753			return Err(AirError::Network(format!("Download failed with status: {}", response.status())));
754		}
755
756		let total_size = response.content_length().unwrap_or(update_info.size);
757		let initial_downloaded = if resume_from_start { 0 } else { downloaded_bytes };
758
759		// Update status to downloading
760		{
761			let mut status = self.update_status.write().await;
762			status.installation_status = InstallationStatus::Downloading;
763			status.download_progress = Some(((downloaded_bytes as f32 / total_size as f32) * 100.0).min(100.0));
764		}
765
766		// Progress tracking
767		let last_update = Arc::new(Mutex::new(std::time::Instant::now()));
768		let last_bytes = Arc::new(Mutex::new(downloaded_bytes));
769
770		// Open or create file
771		let mut file = if resume_from_start {
772			tokio::fs::File::create(&temp_path)
773				.await
774				.map_err(|e| AirError::FileSystem(format!("Failed to create update file: {}", e)))?
775		} else {
776			// Open with append for resume
777			tokio::fs::OpenOptions::new()
778				.append(true)
779				.open(&temp_path)
780				.await
781				.map_err(|e| AirError::FileSystem(format!("Failed to open update file for resume: {}", e)))?
782		};
783
784		use tokio::io::AsyncWriteExt;
785		use futures_util::StreamExt;
786
787		let mut byte_stream = response.bytes_stream();
788		let mut downloaded = initial_downloaded;
789
790		while let Some(chunk_result) = byte_stream.next().await {
791			match chunk_result {
792				Ok(chunk) => {
793					file.write_all(&chunk)
794						.await
795						.map_err(|e| AirError::FileSystem(format!("Failed to write update file: {}", e)))?;
796
797					downloaded += chunk.len() as u64;
798
799					// Update progress every second
800					{
801						let mut last_update_guard = last_update.lock().await;
802						let mut last_bytes_guard = last_bytes.lock().await;
803
804						if last_update_guard.elapsed() >= Duration::from_secs(1) {
805							let bytes_this_second = downloaded - *last_bytes_guard;
806							let download_speed = bytes_this_second as f64;
807
808							let progress = ((downloaded as f32 / total_size as f32) * 100.0).min(100.0);
809							let remaining_bytes = total_size - downloaded;
810							let eta_seconds = if download_speed > 0.0 {
811								Some(remaining_bytes as u64 / (download_speed as u64).max(1))
812							} else {
813								None
814							};
815
816							{
817								let mut status = self.update_status.write().await;
818								status.download_progress = Some(progress);
819								status.download_speed = Some(download_speed);
820								status.eta_seconds = eta_seconds;
821							}
822
823							log::debug!(
824								"[UpdateManager] Download progress: {:.1}% ({}/s, ETA: {:?})",
825								progress,
826								self.format_size(download_speed),
827								eta_seconds
828							);
829
830							*last_update_guard = std::time::Instant::now();
831							*last_bytes_guard = downloaded;
832						}
833					}
834
835					// Update session
836					{
837						let mut sessions = self.download_sessions.write().await;
838						if let Some(session) = sessions.get_mut(&session_id) {
839							session.downloaded_bytes = downloaded;
840						}
841					}
842				},
843				Err(e) => {
844					log::error!("[UpdateManager] Download error: {}", e);
845					let mut status = self.update_status.write().await;
846					status.installation_status = InstallationStatus::Failed(format!("Network error: {}", e));
847					status.last_error = Some(format!("Network error: {}", e));
848					self.record_telemetry(
849						"download",
850						false,
851						start_time.elapsed().as_millis() as u64,
852						None,
853						Some(e.to_string()),
854					)
855					.await;
856					return Err(AirError::Network(format!("Download error: {}", e)));
857				},
858			}
859		}
860
861		// Download complete
862		{
863			let mut status = self.update_status.write().await;
864			status.installation_status = InstallationStatus::Downloading;
865			status.download_progress = Some(100.0);
866		}
867
868		log::info!(
869			"[UpdateManager] Download completed: {} bytes in {:.2}s",
870			downloaded,
871			start_time.elapsed().as_secs_f64()
872		);
873
874		// Verify download integrity with all checksums
875		{
876			let mut status = self.update_status.write().await;
877			status.installation_status = InstallationStatus::VerifyingChecksums;
878		}
879
880		self.VerifyChecksum(&temp_path, &update_info.checksum).await?;
881
882		// Verify additional checksums if provided
883		for (algorithm, expected_checksum) in &update_info.checksums {
884			self.VerifyChecksumWithAlgorithm(&temp_path, algorithm, expected_checksum)
885				.await?;
886		}
887
888		// Verify cryptographic signature if provided
889		if let Some(ref signature) = update_info.signature {
890			{
891				let mut status = self.update_status.write().await;
892				status.installation_status = InstallationStatus::VerifyingSignature;
893			}
894			self.VerifySignature(&temp_path, signature).await?;
895		}
896
897		// Move temp file to final location (atomic)
898		if temp_path.exists() {
899			tokio::fs::rename(&temp_path, &final_path)
900				.await
901				.map_err(|e| AirError::FileSystem(format!("Failed to finalize download: {}", e)))?;
902		}
903
904		// Update session
905		{
906			let mut sessions = self.download_sessions.write().await;
907			if let Some(session) = sessions.get_mut(&session_id) {
908				session.complete = true;
909			}
910		}
911
912		// Update status to completed
913		{
914			let mut status = self.update_status.write().await;
915			status.installation_status = InstallationStatus::Completed;
916			status.download_progress = Some(100.0);
917		}
918
919		log::info!(
920			"[UpdateManager] Update {} downloaded and verified successfully",
921			update_info.version
922		);
923
924		// Record telemetry
925		self.record_telemetry(
926			"download",
927			true,
928			start_time.elapsed().as_millis() as u64,
929			Some(downloaded),
930			None,
931		)
932		.await;
933
934		Ok(())
935	}
936
937	/// Apply update with rollback capability
938	///
939	/// This method:
940	/// - Creates full backup of current installation
941	/// - Validates update package integrity
942	/// - Applies update atomically
943	/// - Automatically rolls back on failure
944	/// - Updates rollback history
945	///
946	/// # Arguments
947	/// * `update_info` - Update information for the version to apply
948	///
949	/// # Returns
950	/// Result<()> indicating success or failure (with automatic rollback)
951	pub async fn ApplyUpdate(&self, update_info:&UpdateInfo) -> Result<()> {
952		let start_time = std::time::Instant::now();
953		let current_version = env!("CARGO_PKG_VERSION");
954
955		log::info!(
956			"[UpdateManager] Applying update: {} (from {})",
957			update_info.version,
958			current_version
959		);
960
961		let file_path = self.cache_directory.join(format!("update-{}.bin", update_info.version));
962
963		// Verify download exists
964		if !file_path.exists() {
965			log::error!("[UpdateManager] Update file not found: {:?}", file_path);
966			return Err(AirError::FileSystem(
967				"Update file not found. Please download first.".to_string(),
968			));
969		}
970
971		// Update status to verifying
972		{
973			let mut status = self.update_status.write().await;
974			status.installation_status = InstallationStatus::VerifyingChecksums;
975			status.last_error = None;
976		}
977
978		// Final verification before applying
979		self.VerifyChecksum(&file_path, &update_info.checksum).await?;
980
981		// Verify additional checksums
982		for (algorithm, expected_checksum) in &update_info.checksums {
983			self.VerifyChecksumWithAlgorithm(&file_path, algorithm, expected_checksum)
984				.await?;
985		}
986
987		// Verify signature if provided
988		if let Some(ref signature) = update_info.signature {
989			{
990				let mut status = self.update_status.write().await;
991				status.installation_status = InstallationStatus::VerifyingSignature;
992			}
993			self.VerifySignature(&file_path, signature).await?;
994		}
995
996		// Create backup before applying update
997		{
998			let mut status = self.update_status.write().await;
999			status.installation_status = InstallationStatus::CreatingBackup;
1000		}
1001
1002		let backup_info = self.CreateBackup(current_version).await?;
1003		log::info!("[UpdateManager] Backup created: {:?}", backup_info.backup_path);
1004
1005		// Update status to installing
1006		{
1007			let mut status = self.update_status.write().await;
1008			status.installation_status = InstallationStatus::Installing;
1009		}
1010
1011		// Apply the update based on platform
1012		let result = match self.platform_config.package_format {
1013			PackageFormat::WindowsExe => self.ApplyWindowsUpdate(&file_path).await,
1014			PackageFormat::MacOsDmg => self.ApplyMacOsUpdate(&file_path).await,
1015			PackageFormat::LinuxAppImage => self.ApplyLinuxAppImageUpdate(&file_path).await,
1016			PackageFormat::LinuxDeb => self.ApplyLinuxDebUpdate(&file_path).await,
1017			PackageFormat::LinuxRpm => self.ApplyLinuxRpmUpdate(&file_path).await,
1018		};
1019
1020		if let Err(e) = result {
1021			log::error!("[UpdateManager] Installation failed, initiating rollback: {}", e);
1022
1023			// Update status to rolling back
1024			{
1025				let mut status = self.update_status.write().await;
1026				status.installation_status = InstallationStatus::RollingBack;
1027			}
1028
1029			// Rollback to the backup
1030			if let Err(rollback_err) = self.RollbackToBackup(&backup_info).await {
1031				log::error!("[UpdateManager] Rollback also failed: {}", rollback_err);
1032
1033				// Critical error - both update and rollback failed
1034				let mut status = self.update_status.write().await;
1035				status.installation_status = InstallationStatus::Failed(format!(
1036					"Installation failed and rollback failed: {} / {}",
1037					e, rollback_err
1038				));
1039				status.last_error = Some(format!("Installation failed and rollback failed"));
1040
1041				self.record_telemetry(
1042					"install",
1043					false,
1044					start_time.elapsed().as_millis() as u64,
1045					None,
1046					Some(format!("Update and rollback failed: {}", rollback_err)),
1047				)
1048				.await;
1049
1050				return Err(AirError::Internal(format!(
1051					"Installation failed and rollback failed: {} / {}",
1052					e, rollback_err
1053				)));
1054			} else {
1055				log::info!("[UpdateManager] Rollback successful");
1056
1057				let mut status = self.update_status.write().await;
1058				status.installation_status =
1059					InstallationStatus::Failed(format!("Installation failed, rollback successful: {}", e));
1060				status.last_error = Some(e.to_string());
1061
1062				self.record_telemetry(
1063					"install",
1064					false,
1065					start_time.elapsed().as_millis() as u64,
1066					None,
1067					Some(e.to_string()),
1068				)
1069				.await;
1070
1071				return Err(AirError::Internal(format!("Installation failed, rollback successful: {}", e)));
1072			}
1073		}
1074
1075		// Update successful - add to rollback history
1076		{
1077			let mut history = self.rollback_history.lock().await;
1078			history.versions.insert(0, backup_info);
1079
1080			// Keep only max_versions
1081			while history.versions.len() > history.max_versions {
1082				if let Some(old_backup) = history.versions.pop() {
1083					// Clean up old backup directory
1084					let _ = tokio::fs::remove_dir_all(&old_backup.backup_path).await;
1085				}
1086			}
1087		}
1088
1089		// Save rollback history
1090		let history_path = self.backup_directory.join("rollback_history.json");
1091		let history = self.rollback_history.lock().await;
1092		let history_json = serde_json::to_string(&*history)
1093			.map_err(|e| AirError::Internal(format!("Failed to serialize rollback history: {}", e)))?;
1094		drop(history);
1095		tokio::fs::write(&history_path, history_json)
1096			.await
1097			.map_err(|e| AirError::FileSystem(format!("Failed to save rollback history: {}", e)))?;
1098
1099		// Update current version in status
1100		{
1101			let mut status = self.update_status.write().await;
1102			status.current_version = update_info.version.clone();
1103			status.installation_status = InstallationStatus::Completed;
1104		}
1105
1106		log::info!(
1107			"[UpdateManager] Update {} applied successfully in {:.2}s",
1108			update_info.version,
1109			start_time.elapsed().as_secs_f64()
1110		);
1111
1112		// Record telemetry
1113		self.record_telemetry(
1114			"install",
1115			true,
1116			start_time.elapsed().as_millis() as u64,
1117			Some(update_info.size),
1118			None,
1119		)
1120		.await;
1121
1122		Ok(())
1123	}
1124
1125	/// Fetch update information from the configured update server
1126	///
1127	/// This method:
1128	/// - Queries the update server based on platform, version, and channel
1129	/// - Uses circuit breakers and retry policies for resilience
1130	/// - Returns update information if a newer version is available
1131	///
1132	/// # Returns
1133	/// Result<Option<UpdateInfo>> - Some if update available, None if
1134	/// up-to-date
1135	async fn FetchUpdateInfo(&self) -> Result<Option<UpdateInfo>> {
1136		let config = &self.AppState.Configuration.Updates;
1137
1138		// Setup resilience patterns
1139		let retry_policy = crate::Resilience::RetryPolicy {
1140			MaxRetries:3,
1141			InitialIntervalMs:1000,
1142			MaxIntervalMs:16000,
1143			BackoffMultiplier:2.0,
1144			JitterFactor:0.1,
1145			BudgetPerMinute:50,
1146			ErrorClassification:std::collections::HashMap::new(),
1147		};
1148
1149		let _retry_manager = crate::Resilience::RetryManager::new(retry_policy.clone());
1150		let circuit_breaker = crate::Resilience::CircuitBreaker::new(
1151			"updates".to_string(),
1152			crate::Resilience::CircuitBreakerConfig::default(),
1153		);
1154
1155		let current_version = env!("CARGO_PKG_VERSION");
1156		let mut attempt = 0;
1157
1158		loop {
1159			// Check circuit breaker state before attempting request
1160			if circuit_breaker.GetState().await == crate::Resilience::CircuitState::Open {
1161				if !circuit_breaker.AttemptRecovery().await {
1162					log::warn!("[UpdateManager] Circuit breaker is open, skipping update check");
1163					return Ok(None);
1164				}
1165			}
1166
1167			// Build request URL with all necessary parameters
1168			let update_url = format!(
1169				"{}/check?version={}&platform={}&arch={}&channel={}",
1170				config.UpdateServerUrl,
1171				current_version,
1172				self.platform_config.platform,
1173				self.platform_config.arch,
1174				self.update_channel.as_str()
1175			);
1176
1177			let client = reqwest::Client::builder()
1178				.timeout(Duration::from_secs(30))
1179				.build()
1180				.map_err(|e| AirError::Network(format!("Failed to create HTTP client: {}", e)))?;
1181
1182			match client.get(&update_url).send().await {
1183				Ok(response) => {
1184					match response.status() {
1185						reqwest::StatusCode::NO_CONTENT => {
1186							// No update available (up to date)
1187							circuit_breaker.RecordSuccess().await;
1188							log::debug!("[UpdateManager] Server reports no updates available");
1189							return Ok(None);
1190						},
1191						status if status.is_success() => {
1192							// Parse update information
1193							match response.json::<UpdateInfo>().await {
1194								Ok(update_info) => {
1195									circuit_breaker.RecordSuccess().await;
1196
1197									// Check if update is actually newer
1198									if UpdateManager::CompareVersions(current_version, &update_info.version) < 0 {
1199										log::info!(
1200											"[UpdateManager] Update available: {} -> {}",
1201											current_version,
1202											update_info.version
1203										);
1204										return Ok(Some(update_info));
1205									} else {
1206										log::debug!(
1207											"[UpdateManager] Server returned same or older version: {}",
1208											update_info.version
1209										);
1210										return Ok(None);
1211									}
1212								},
1213								Err(e) => {
1214									circuit_breaker.RecordFailure().await;
1215									log::error!("[UpdateManager] Failed to parse update info: {}", e);
1216
1217									if attempt < retry_policy.MaxRetries {
1218										attempt += 1;
1219										let delay = Duration::from_millis(
1220											retry_policy.InitialIntervalMs * 2_u32.pow(attempt as u32) as u64,
1221										);
1222										sleep(delay).await;
1223										continue;
1224									} else {
1225										return Err(AirError::Network(format!(
1226											"Failed to parse update info after retries: {}",
1227											e
1228										)));
1229									}
1230								},
1231							}
1232						},
1233						status => {
1234							circuit_breaker.RecordFailure().await;
1235							log::warn!("[UpdateManager] Update server returned status: {}", status);
1236
1237							if attempt < retry_policy.MaxRetries {
1238								attempt += 1;
1239								let delay = Duration::from_millis(
1240									retry_policy.InitialIntervalMs * 2_u32.pow(attempt as u32) as u64,
1241								);
1242								sleep(delay).await;
1243								continue;
1244							} else {
1245								return Ok(None);
1246							}
1247						},
1248					}
1249				},
1250				Err(e) => {
1251					circuit_breaker.RecordFailure().await;
1252					log::warn!("[UpdateManager] Failed to check for updates: {}", e);
1253
1254					if attempt < retry_policy.MaxRetries {
1255						attempt += 1;
1256						let delay =
1257							Duration::from_millis(retry_policy.InitialIntervalMs * 2_u32.pow(attempt as u32) as u64);
1258						sleep(delay).await;
1259						continue;
1260					} else {
1261						return Ok(None);
1262					}
1263				},
1264			}
1265		}
1266	}
1267
1268	/// Verify file checksum (SHA256 by default)
1269	///
1270	/// This method:
1271	/// - Reads the entire file into memory
1272	/// - Computes SHA256 hash
1273	/// - Compares with expected checksum
1274	///
1275	/// # Arguments
1276	/// * `file_path` - Path to the file to verify
1277	/// * `expected_checksum` - Expected SHA256 checksum in hex format
1278	///
1279	/// # Returns
1280	/// Result<()> indicating success or failure
1281	async fn VerifyChecksum(&self, file_path:&Path, expected_checksum:&str) -> Result<()> {
1282		let content = tokio::fs::read(file_path)
1283			.await
1284			.map_err(|e| AirError::FileSystem(format!("Failed to read update file for checksum: {}", e)))?;
1285
1286		let actual_checksum = self.CalculateSha256(&content);
1287
1288		if actual_checksum.to_lowercase() != expected_checksum.to_lowercase() {
1289			log::error!(
1290				"[UpdateManager] Checksum verification failed: expected {}, got {}",
1291				expected_checksum,
1292				actual_checksum
1293			);
1294			return Err(AirError::Network("Update checksum verification failed".to_string()));
1295		}
1296
1297		log::debug!("[UpdateManager] Checksum verified: {}", actual_checksum);
1298		Ok(())
1299	}
1300
1301	/// Verify file checksum with specified algorithm
1302	///
1303	/// Supports multiple checksum algorithms for comprehensive integrity
1304	/// checking
1305	///
1306	/// # Arguments
1307	/// * `file_path` - Path to the file to verify
1308	/// * `algorithm` - Checksum algorithm (md5, sha1, sha256, sha512)
1309	/// * `expected_checksum` - Expected checksum in hex format
1310	///
1311	/// # Returns
1312	/// Result<()> indicating success or failure
1313	async fn VerifyChecksumWithAlgorithm(&self, file_path:&Path, algorithm:&str, expected_checksum:&str) -> Result<()> {
1314		let content = tokio::fs::read(file_path).await.map_err(|e| {
1315			AirError::FileSystem(format!("Failed to read update file for {} checksum: {}", algorithm, e))
1316		})?;
1317
1318		let actual_checksum = match algorithm.to_lowercase().as_str() {
1319			"sha256" => self.CalculateSha256(&content),
1320			"sha512" => self.CalculateSha512(&content),
1321			"md5" => self.CalculateMd5(&content),
1322			"crc32" => self.CalculateCrc32(&content),
1323			_ => {
1324				log::warn!("[UpdateManager] Unknown checksum algorithm: {}, skipping", algorithm);
1325				return Ok(());
1326			},
1327		};
1328
1329		if actual_checksum.to_lowercase() != expected_checksum.to_lowercase() {
1330			log::error!(
1331				"[UpdateManager] {} checksum verification failed: expected {}, got {}",
1332				algorithm,
1333				expected_checksum,
1334				actual_checksum
1335			);
1336			return Err(AirError::Network(format!("{} checksum verification failed", algorithm)));
1337		}
1338
1339		log::debug!("[UpdateManager] {} checksum verified: {}", algorithm, actual_checksum);
1340		Ok(())
1341	}
1342
1343	/// Verify cryptographic signature of update package
1344	///
1345	/// This method:
1346	/// - Uses Ed25519 signature verification
1347	/// - Verifies the package hasn't been tampered with
1348	/// - Uses the public key configured in the system
1349	///
1350	/// # Arguments
1351	/// * `file_path` - Path to the signed file
1352	/// * `signature` - Base64-encoded signature
1353	///
1354	/// # Returns
1355	/// Result<()> indicating success or failure
1356	async fn VerifySignature(&self, file_path:&Path, signature:&str) -> Result<()> {
1357		// TODO: Implement actual signature verification
1358		// This would require:
1359		// 1. A public key embedded in the application
1360		// 2. Use ring::signature for Ed25519 verification
1361		// 3. Decode the base64 signature
1362		// 4. Verify the file content against the signature
1363
1364		log::info!("[UpdateManager] Signature verification not yet implemented, skipping");
1365
1366		// For now, we'll just log a warning
1367		log::warn!("[UpdateManager] WARNING: Cryptographic signature verification is not implemented");
1368		log::warn!("[UpdateManager] Update packages should be cryptographically signed in production");
1369
1370		Ok(())
1371	}
1372
1373	/// Create backup of current installation
1374	///
1375	/// This method:
1376	/// - Creates a timestamped backup directory
1377	/// - Copies critical files (binaries, config, data)
1378	/// - Computes checksum of backup for rollback verification
1379	///
1380	/// # Arguments
1381	/// * `version` - Current version being backed up
1382	///
1383	/// # Returns
1384	/// Result<RollbackState> containing backup information
1385	async fn CreateBackup(&self, version:&str) -> Result<RollbackState> {
1386		let timestamp = chrono::Utc::now();
1387		let backup_dir_name = format!("backup-{}-{}", version, timestamp.format("%Y%m%d_%H%M%S"));
1388		let backup_path = self.backup_directory.join(&backup_dir_name);
1389
1390		log::info!("[UpdateManager] Creating backup: {}", backup_dir_name);
1391
1392		// Create backup directory
1393		tokio::fs::create_dir_all(&backup_path)
1394			.await
1395			.map_err(|e| AirError::FileSystem(format!("Failed to create backup directory: {}", e)))?;
1396
1397		// Get application executable path
1398		let exe_path = std::env::current_exe()
1399			.map_err(|e| AirError::FileSystem(format!("Failed to get executable path: {}", e)))?;
1400
1401		// Copy executable to backup
1402		let backup_exe = backup_path.join(exe_path.file_name().unwrap_or_default());
1403		tokio::fs::copy(&exe_path, &backup_exe)
1404			.await
1405			.map_err(|e| AirError::FileSystem(format!("Failed to backup executable: {}", e)))?;
1406
1407		// TODO: Also backup:
1408		// - Configuration files (preserving user settings)
1409		// - Data directories
1410		// - Extensions
1411		// - Any other critical application files
1412
1413		// Calculate checksum of backup for verification during rollback
1414		let checksum = self.CalculateFileChecksum(&backup_path).await?;
1415
1416		log::info!("[UpdateManager] Backup created at: {:?}", backup_path);
1417
1418		Ok(RollbackState { version:version.to_string(), backup_path, timestamp, checksum })
1419	}
1420
1421	/// Rollback to a previous version using backup
1422	///
1423	/// This method:
1424	/// - Verifies backup integrity using checksum
1425	/// - Restores files from backup
1426	/// - Validated rollback success
1427	///
1428	/// # Arguments
1429	/// * `backup_info` - Rollback state containing backup information
1430	///
1431	/// # Returns
1432	/// Result<()> indicating success or failure
1433	pub async fn RollbackToBackup(&self, backup_info:&RollbackState) -> Result<()> {
1434		log::info!(
1435			"[UpdateManager] Rolling back to version: {} from: {:?}",
1436			backup_info.version,
1437			backup_info.backup_path
1438		);
1439
1440		// Verify backup integrity
1441		let current_checksum = self.CalculateFileChecksum(&backup_info.backup_path).await?;
1442		if current_checksum != backup_info.checksum {
1443			return Err(AirError::Internal(format!(
1444				"Backup integrity check failed: expected {}, got {}",
1445				backup_info.checksum, current_checksum
1446			)));
1447		}
1448
1449		// Get application executable path
1450		let exe_path = std::env::current_exe()
1451			.map_err(|e| AirError::FileSystem(format!("Failed to get executable path: {}", e)))?;
1452
1453		let backup_exe = backup_info.backup_path.join(exe_path.file_name().unwrap_or_default());
1454
1455		if !backup_exe.exists() {
1456			return Err(AirError::FileSystem("Backup executable not found".to_string()));
1457		}
1458
1459		// Restore executable from backup
1460		// Note: This may not work on all platforms due to file locks
1461		// In production, this would need to be done by a separate updater process
1462		match tokio::fs::copy(&backup_exe, &exe_path).await {
1463			Ok(_) => {
1464				log::info!("[UpdateManager] Executable restored from backup");
1465			},
1466			Err(e) => {
1467				log::error!("[UpdateManager] Failed to restore executable: {}", e);
1468				log::warn!("[UpdateManager] Rollback may require manual intervention");
1469			},
1470		}
1471
1472		// TODO: Restore other components (config, data, etc.)
1473
1474		log::info!("[UpdateManager] Rollback to version {} completed", backup_info.version);
1475		Ok(())
1476	}
1477
1478	/// Rollback to a specific version by version number
1479	///
1480	/// This method:
1481	/// - Searches for backup matching the version
1482	/// - Calls RollbackToBackup with the backup
1483	///
1484	/// # Arguments
1485	/// * `version` - Version to rollback to
1486	///
1487	/// # Returns
1488	/// Result<()> indicating success or failure
1489	pub async fn RollbackToVersion(&self, version:&str) -> Result<()> {
1490		let history = self.rollback_history.lock().await;
1491
1492		let backup_info = history
1493			.versions
1494			.iter()
1495			.find(|state| state.version == version)
1496			.ok_or_else(|| AirError::FileSystem(format!("No backup found for version {}", version)))?;
1497
1498		let info = backup_info.clone();
1499		drop(history);
1500
1501		self.RollbackToBackup(&info).await
1502	}
1503
1504	/// Get available rollback versions
1505	///
1506	/// Returns list of versions that can be rolled back to
1507	pub async fn GetAvailableRollbackVersions(&self) -> Vec<String> {
1508		let history = self.rollback_history.lock().await;
1509		history.versions.iter().map(|state| state.version.clone()).collect()
1510	}
1511
1512	/// Validate disk space before download
1513	///
1514	/// Ensures sufficient space is available for download + staging
1515	///
1516	/// # Arguments
1517	/// * `required_bytes` - Required space in bytes
1518	///
1519	/// # Returns
1520	/// Result<()> indicating success or failure
1521	async fn ValidateDiskSpace(&self, required_bytes:u64) -> Result<()> {
1522		// Get disk space information
1523		let metadata = tokio::fs::metadata(&self.cache_directory)
1524			.await
1525			.map_err(|e| AirError::FileSystem(format!("Failed to get cache directory info: {}", e)))?;
1526
1527		if cfg!(target_os = "windows") {
1528			// Windows: use std::os::windows::fs::MetadataExt
1529			#[cfg(target_os = "windows")]
1530			{
1531				use std::os::windows::fs::MetadataExt;
1532				let free_space = metadata.volume_serial_number() as u64; // This isn't correct, just placeholder
1533				log::warn!("[UpdateManager] Disk space validation not fully implemented on Windows");
1534			}
1535		} else {
1536			// Unix-like systems
1537			#[cfg(not(target_os = "windows"))]
1538			{
1539				use std::os::unix::fs::MetadataExt;
1540				let _device_id = metadata.dev();
1541
1542				// TODO: Actually get free space for the device
1543				// This would require platform-specific syscalls
1544			}
1545		}
1546
1547		log::info!(
1548			"[UpdateManager] Disk space validation: requiring {} bytes",
1549			self.format_size(required_bytes as f64)
1550		);
1551
1552		// For now, we'll trust that there's enough space
1553		// In production, this should actually check available space
1554		Ok(())
1555	}
1556
1557	/// Verify update file integrity comprehensive check
1558	///
1559	/// This method:
1560	/// - Checks file existence and non-zero size
1561	/// - Verifies all checksums if UpdateInfo provided
1562	/// - Detects corrupted downloads
1563	///
1564	/// # Arguments
1565	/// * `file_path` - Path to the update file
1566	/// * `update_info` - Optional update info with checksums
1567	///
1568	/// # Returns
1569	/// Result<bool> - true if valid, false if invalid
1570	pub async fn verify_update(&self, file_path:&str, update_info:Option<&UpdateInfo>) -> Result<bool> {
1571		let path = PathBuf::from(file_path);
1572
1573		if !path.exists() {
1574			return Ok(false);
1575		}
1576
1577		let metadata = tokio::fs::metadata(&path)
1578			.await
1579			.map_err(|e| AirError::FileSystem(format!("Failed to read update file metadata: {}", e)))?;
1580
1581		if metadata.len() == 0 {
1582			return Ok(false);
1583		}
1584
1585		// Verify checksums if UpdateInfo is provided
1586		if let Some(info) = update_info {
1587			if !info.checksum.is_empty() {
1588				let actual_checksum = self.CalculateFileChecksum(&path).await?;
1589				if actual_checksum != info.checksum {
1590					return Err(AirError::Configuration(format!(
1591						"Checksum verification failed: expected {}, got {}",
1592						info.checksum, actual_checksum
1593					)));
1594				}
1595			}
1596
1597			// Verify additional checksums
1598			for (algorithm, expected_checksum) in &info.checksums {
1599				self.VerifyChecksumWithAlgorithm(&path, algorithm, expected_checksum).await?;
1600			}
1601
1602			// Verify file size matches expected
1603			if let Some(expected_size) = Some(info.size) {
1604				if metadata.len() != expected_size {
1605					return Err(AirError::Configuration(format!(
1606						"File size mismatch: expected {}, got {}",
1607						expected_size,
1608						metadata.len()
1609					)));
1610				}
1611			}
1612		}
1613
1614		Ok(true)
1615	}
1616
1617	/// Platform-specific update installation for Windows
1618	async fn ApplyWindowsUpdate(&self, file_path:&Path) -> Result<()> {
1619		log::info!("[UpdateManager] Installing Windows update: {:?}", file_path);
1620
1621		// TODO: Implement Windows-specific installation
1622		// This would typically:
1623		// 1. Create a temporary updater process
1624		// 2. Run the Windows installer in silent mode
1625		// 3. The updater waits for the main process to exit
1626		// 4. Extracts and replaces files
1627		// 5. Restarts the application
1628
1629		log::warn!("[UpdateManager] Windows installation not fully implemented");
1630		log::info!("[UpdateManager] Update package ready for manual installation");
1631
1632		Ok(())
1633	}
1634
1635	/// Platform-specific update installation for macOS
1636	async fn ApplyMacOsUpdate(&self, file_path:&Path) -> Result<()> {
1637		log::info!("[UpdateManager] Installing macOS update: {:?}", file_path);
1638
1639		// TODO: Implement macOS-specific installation
1640		// This would typically:
1641		// 1. Verify the DMG signature
1642		// 2. Mount the DMG
1643		// 3. Copy the new application bundle
1644		// 4. Set correct permissions
1645		// 5. Re-sign the application
1646		// 6. Unmount the DMG
1647
1648		log::warn!("[UpdateManager] macOS installation not fully implemented");
1649		log::info!("[UpdateManager] Update package ready for manual installation");
1650
1651		Ok(())
1652	}
1653
1654	/// Platform-specific update installation for Linux (AppImage)
1655	async fn ApplyLinuxAppImageUpdate(&self, file_path:&Path) -> Result<()> {
1656		log::info!("[UpdateManager] Installing Linux AppImage update: {:?}", file_path);
1657
1658		// TODO: Implement Linux AppImage installation
1659		// This would typically:
1660		// 1. Verify the AppImage signature
1661		// 2. Make the new AppImage executable
1662		// 3. Replace the old AppImage
1663		// 4. Update desktop entry and icons
1664
1665		log::warn!("[UpdateManager] Linux AppImage installation not fully implemented");
1666		log::info!("[UpdateManager] Update package ready for manual installation");
1667
1668		Ok(())
1669	}
1670
1671	/// Platform-specific update installation for Linux (DEB)
1672	async fn ApplyLinuxDebUpdate(&self, file_path:&Path) -> Result<()> {
1673		log::info!("[UpdateManager] Installing Linux DEB update: {:?}", file_path);
1674
1675		// TODO: Implement Linux DEB installation
1676		// This would call dpkg or apt to install the package
1677
1678		log::warn!("[UpdateManager] Linux DEB installation not fully implemented");
1679		Ok(())
1680	}
1681
1682	/// Platform-specific update installation for Linux (RPM)
1683	async fn ApplyLinuxRpmUpdate(&self, file_path:&Path) -> Result<()> {
1684		log::info!("[UpdateManager] Installing Linux RPM update: {:?}", file_path);
1685
1686		// TODO: Implement Linux RPM installation
1687		// This would call rpm or dnf to install the package
1688
1689		log::warn!("[UpdateManager] Linux RPM installation not fully implemented");
1690		Ok(())
1691	}
1692
1693	/// Record telemetry for update operations
1694	///
1695	/// This method:
1696	/// - Creates telemetry event with operation details
1697	/// - In production, would send to analytics service
1698	/// - Currently logs to file for debugging
1699	///
1700	/// # Arguments
1701	/// * `operation` - Type of operation (check, download, install, rollback)
1702	/// * `success` - Whether operation succeeded
1703	/// * `duration_ms` - Duration in milliseconds
1704	/// * `download_size` - Optional download size in bytes
1705	/// * `error_message` - Optional error message if failed
1706	async fn record_telemetry(
1707		&self,
1708		operation:&str,
1709		success:bool,
1710		duration_ms:u64,
1711		download_size:Option<u64>,
1712		error_message:Option<String>,
1713	) {
1714		let telemetry = UpdateTelemetry {
1715			event_id:Uuid::new_v4().to_string(),
1716			current_version:env!("CARGO_PKG_VERSION").to_string(),
1717			target_version:self
1718				.update_status
1719				.read()
1720				.await
1721				.available_version
1722				.clone()
1723				.unwrap_or_else(|| "unknown".to_string()),
1724			channel:self.update_channel.as_str().to_string(),
1725			platform:format!("{}/{}", self.platform_config.platform, self.platform_config.arch),
1726			operation:operation.to_string(),
1727			success,
1728			duration_ms,
1729			download_size,
1730			error_message,
1731			timestamp:chrono::Utc::now(),
1732		};
1733
1734		log::info!(
1735			"[UpdateManager] Telemetry: {} {} in {}ms - size: {:?}, success: {}",
1736			operation,
1737			if success { "succeeded" } else { "failed" },
1738			duration_ms,
1739			download_size.map(|s| self.format_size(s as f64)),
1740			success
1741		);
1742
1743		// TODO: Send telemetry to analytics service
1744		// For now, we just log it
1745		if let Err(e) = serde_json::to_string(&telemetry) {
1746			log::error!("[UpdateManager] Failed to serialize telemetry: {}", e);
1747		}
1748	}
1749
1750	/// Calculate SHA256 checksum of a byte slice
1751	fn CalculateSha256(&self, data:&[u8]) -> String {
1752		let mut hasher = Sha256::new();
1753		hasher.update(data);
1754		format!("{:x}", hasher.finalize())
1755	}
1756
1757	/// Calculate SHA512 checksum of a byte slice
1758	fn CalculateSha512(&self, data:&[u8]) -> String {
1759		use sha2::Sha512;
1760		let mut hasher = Sha512::new();
1761		hasher.update(data);
1762		format!("{:x}", hasher.finalize())
1763	}
1764
1765	/// Calculate MD5 checksum of a byte slice
1766	fn CalculateMd5(&self, data:&[u8]) -> String {
1767		let digest = md5::compute(data);
1768		format!("{:x}", digest)
1769	}
1770
1771	/// Calculate CRC32 checksum of a byte slice
1772	fn CalculateCrc32(&self, data:&[u8]) -> String {
1773		let crc = crc32fast::hash(data);
1774		format!("{:08x}", crc)
1775	}
1776
1777	/// Calculate SHA256 checksum of a file
1778	async fn CalculateFileChecksum(&self, path:&Path) -> Result<String> {
1779		let content = tokio::fs::read(path)
1780			.await
1781			.map_err(|e| AirError::FileSystem(format!("Failed to read file for checksum: {}", e)))?;
1782
1783		Ok(self.CalculateSha256(&content))
1784	}
1785
1786	/// Compare two semantic version strings
1787	///
1788	/// Returns:
1789	/// - -1 if v1 < v2
1790	/// - 0 if v1 == v2
1791	/// - 1 if v1 > v2
1792	///
1793	/// # Arguments
1794	/// * `v1` - First version string
1795	/// * `v2` - Second version string
1796	///
1797	/// # Returns
1798	/// i32 indicating comparison result
1799	pub fn CompareVersions(v1:&str, v2:&str) -> i32 {
1800		let v1_parts:Vec<u32> = v1.split('.').filter_map(|s| s.parse().ok()).collect();
1801		let v2_parts:Vec<u32> = v2.split('.').filter_map(|s| s.parse().ok()).collect();
1802
1803		for (i, part) in v1_parts.iter().enumerate() {
1804			if i >= v2_parts.len() {
1805				return 1;
1806			}
1807
1808			match part.cmp(&v2_parts[i]) {
1809				std::cmp::Ordering::Greater => return 1,
1810				std::cmp::Ordering::Less => return -1,
1811				std::cmp::Ordering::Equal => continue,
1812			}
1813		}
1814
1815		if v1_parts.len() < v2_parts.len() { -1 } else { 0 }
1816	}
1817
1818	/// Get current update status
1819	///
1820	/// Returns a clone of the current update status
1821	pub async fn GetStatus(&self) -> UpdateStatus {
1822		let status = self.update_status.read().await;
1823		status.clone()
1824	}
1825
1826	/// Cancel ongoing download
1827	///
1828	/// This method:
1829	/// - Cancels the active download session
1830	/// - Cleans up temporary files
1831	/// - Updates status to paused
1832	pub async fn CancelDownload(&self) -> Result<()> {
1833		let mut status = self.update_status.write().await;
1834
1835		if status.installation_status != InstallationStatus::Downloading {
1836			return Err(AirError::Internal("No download in progress".to_string()));
1837		}
1838
1839		status.installation_status = InstallationStatus::Paused;
1840
1841		// TODO: Actually cancel the download
1842		// This would require:
1843		// 1. Abort the HTTP request
1844		// 2. Clean up partial files
1845		// 3. Clear the download session
1846
1847		log::info!("[UpdateManager] Download cancelled");
1848		Ok(())
1849	}
1850
1851	/// Resume paused download
1852	///
1853	/// This method:
1854	/// - Resumes a paused download session
1855	/// - Uses HTTP Range header for resume capability
1856	///
1857	/// # Arguments
1858	/// * `update_info` - Update information to resume download
1859	pub async fn ResumeDownload(&self, update_info:&UpdateInfo) -> Result<()> {
1860		let Status = self.update_status.write().await;
1861
1862		if Status.installation_status != InstallationStatus::Paused {
1863			return Err(AirError::Internal("No paused download to resume".to_string()));
1864		}
1865
1866		drop(Status);
1867
1868		log::info!("[UpdateManager] Resuming download for version {}", update_info.version);
1869		self.DownloadUpdate(update_info).await
1870	}
1871
1872	/// Get update configuration
1873	///
1874	/// Returns the current update channel configuration
1875	pub async fn GetUpdateChannel(&self) -> UpdateChannel { self.update_channel }
1876
1877	/// Set update channel
1878	///
1879	/// # Arguments
1880	/// * `channel` - New update channel to use
1881	pub async fn SetUpdateChannel(&mut self, channel:UpdateChannel) {
1882		self.update_channel = channel;
1883
1884		let mut status = self.update_status.write().await;
1885		status.update_channel = channel;
1886
1887		log::info!("[UpdateManager] Update channel changed to: {}", channel.as_str());
1888
1889		// Trigger a check for updates on new channel
1890		if let Err(e) = self.CheckForUpdates().await {
1891			log::error!("[UpdateManager] Failed to check for updates after channel change: {}", e);
1892		}
1893	}
1894
1895	/// Stage update for pre-installation verification
1896	///
1897	/// This method:
1898	/// - Stages the update in the staging directory
1899	/// - Verifies the staged update
1900	/// - Prepares for installation
1901	///
1902	/// # Arguments
1903	/// * `update_info` - Update information to stage
1904	pub async fn StageUpdate(&self, update_info:&UpdateInfo) -> Result<()> {
1905		log::info!("[UpdateManager] Staging update for version {}", update_info.version);
1906
1907		let mut status = self.update_status.write().await;
1908		status.installation_status = InstallationStatus::Staging;
1909		drop(status);
1910
1911		let file_path = self.cache_directory.join(format!("update-{}.bin", update_info.version));
1912
1913		if !file_path.exists() {
1914			return Err(AirError::FileSystem("Update file not found. Download first.".to_string()));
1915		}
1916
1917		// Create version-specific staging directory
1918		let stage_dir = self.staging_directory.join(&update_info.version);
1919		tokio::fs::create_dir_all(&stage_dir)
1920			.await
1921			.map_err(|e| AirError::FileSystem(format!("Failed to create staging directory: {}", e)))?;
1922
1923		// Copy update package to staging
1924		let staged_file = stage_dir.join("update.bin");
1925		tokio::fs::copy(&file_path, &staged_file)
1926			.await
1927			.map_err(|e| AirError::FileSystem(format!("Failed to stage update package: {}", e)))?;
1928
1929		// Verify staged package
1930		self.VerifyChecksum(&staged_file, &update_info.checksum).await?;
1931
1932		log::info!("[UpdateManager] Update staged successfully in: {:?}", stage_dir);
1933		Ok(())
1934	}
1935
1936	/// Clean up old update files
1937	///
1938	/// Removes downloaded updates older than a certain threshold
1939	/// to free disk space
1940	pub async fn CleanupOldUpdates(&self) -> Result<()> {
1941		log::info!("[UpdateManager] Cleaning up old update files");
1942
1943		let mut entries = tokio::fs::read_dir(&self.cache_directory)
1944			.await
1945			.map_err(|e| AirError::FileSystem(format!("Failed to read cache directory: {}", e)))?;
1946
1947		let mut cleaned_count = 0;
1948		let now = std::time::SystemTime::now();
1949
1950		while let Some(entry) = entries
1951			.next_entry()
1952			.await
1953			.map_err(|e| AirError::FileSystem(format!("Failed to read directory entry: {}", e)))?
1954		{
1955			let path = entry.path();
1956			let metadata = entry
1957				.metadata()
1958				.await
1959				.map_err(|e| AirError::FileSystem(format!("Failed to get metadata: {}", e)))?;
1960
1961			// Skip directories and recent files (within 7 days)
1962			if path.is_dir()
1963				|| metadata.modified().unwrap_or(now)
1964					> now.checked_sub(Duration::from_secs(7 * 24 * 3600)).unwrap_or(now)
1965			{
1966				continue;
1967			}
1968
1969			log::debug!("[UpdateManager] Removing old update file: {:?}", path);
1970			tokio::fs::remove_file(&path)
1971				.await
1972				.map_err(|e| AirError::FileSystem(format!("Failed to remove {}: {}", path.display(), e)))?;
1973
1974			cleaned_count += 1;
1975		}
1976
1977		log::info!("[UpdateManager] Cleaned up {} old update files", cleaned_count);
1978		Ok(())
1979	}
1980
1981	/// Get the cache directory path
1982	pub fn GetCacheDirectory(&self) -> &PathBuf { &self.cache_directory }
1983
1984	/// Start background update checking task
1985	///
1986	/// This method:
1987	/// - Periodically checks for updates based on configured interval
1988	/// - Runs in a separate tokio task
1989	/// - Can be cancelled by stopping the task
1990	///
1991	/// # Returns
1992	/// Result<tokio::task::JoinHandle<()>> - Handle to the background task
1993	pub async fn StartBackgroundTasks(&self) -> Result<tokio::task::JoinHandle<()>> {
1994		let manager = self.clone();
1995
1996		let handle = tokio::spawn(async move {
1997			manager.BackgroundTask().await;
1998		});
1999
2000		log::info!("[UpdateManager] Background update checking started");
2001		Ok(handle)
2002	}
2003
2004	/// Background task for periodic update checks
2005	///
2006	/// This task:
2007	/// - Checks for updates at regular intervals
2008	/// - Logs any errors but doesn't fail the task
2009	/// - Can run indefinitely until stopped
2010	async fn BackgroundTask(&self) {
2011		let config = &self.AppState.Configuration.Updates;
2012
2013		if !config.Enabled {
2014			log::info!("[UpdateManager] Background task: Updates are disabled");
2015			return;
2016		}
2017
2018		let check_interval = Duration::from_secs(config.CheckIntervalHours as u64 * 3600);
2019		let mut interval = interval(check_interval);
2020
2021		log::info!(
2022			"[UpdateManager] Background task: Checking for updates every {} hours",
2023			config.CheckIntervalHours
2024		);
2025
2026		loop {
2027			interval.tick().await;
2028
2029			log::debug!("[UpdateManager] Background task: Checking for updates...");
2030
2031			// Check for updates
2032			match self.CheckForUpdates().await {
2033				Ok(Some(update_info)) => {
2034					log::info!("[UpdateManager] Background task: Update available: {}", update_info.version);
2035				},
2036				Ok(None) => {
2037					log::debug!("[UpdateManager] Background task: No updates available");
2038				},
2039				Err(e) => {
2040					log::error!("[UpdateManager] Background task: Update check failed: {}", e);
2041				},
2042			}
2043		}
2044	}
2045
2046	/// Stop background tasks
2047	///
2048	/// This method:
2049	/// - Logs the stop request
2050	/// - In production, would cancel the join handle
2051	pub async fn StopBackgroundTasks(&self) {
2052		log::info!("[UpdateManager] Stopping background tasks");
2053
2054		// TODO: Implement proper task cancellation
2055		// This would require storing the JoinHandle and using abort()
2056	}
2057
2058	/// Format byte count to human-readable string
2059	///
2060	/// # Arguments
2061	/// * `bytes` - Number of bytes (supports both u64 and f64 for rates)
2062	///
2063	/// # Returns
2064	/// Formatted string (e.g., "1.5 MB", "500 KB")
2065	fn format_size(&self, bytes:f64) -> String {
2066		const KB:f64 = 1024.0;
2067		const MB:f64 = KB * 1024.0;
2068		const GB:f64 = MB * 1024.0;
2069
2070		if bytes >= GB {
2071			format!("{:.2} GB/s", bytes / GB)
2072		} else if bytes >= MB {
2073			format!("{:.2} MB/s", bytes / MB)
2074		} else if bytes >= KB {
2075			format!("{:.2} KB/s", bytes / KB)
2076		} else {
2077			format!("{:.0} B/s", bytes)
2078		}
2079	}
2080}
2081
2082impl Clone for UpdateManager {
2083	fn clone(&self) -> Self {
2084		Self {
2085			AppState:self.AppState.clone(),
2086			update_status:self.update_status.clone(),
2087			cache_directory:self.cache_directory.clone(),
2088			staging_directory:self.staging_directory.clone(),
2089			backup_directory:self.backup_directory.clone(),
2090			download_sessions:self.download_sessions.clone(),
2091			rollback_history:self.rollback_history.clone(),
2092			update_channel:self.update_channel,
2093			platform_config:self.platform_config.clone(),
2094		}
2095	}
2096}