from gi.repository import Adw, Gtk, GLib, Gio
from .host_info import HostInfo
import subprocess, re

class ChangeVersionWorker:
	process = None
	callback = None
	error_callback = None
	loading_status = None
	did_error = False
	
	@classmethod
	def update_status(this, package_ratio, complete, total):
		final_ratio = (package_ratio + complete) / (total or 1)
		
		print(f"fr: {final_ratio:.2f}")
		print("r:", package_ratio, ", c:", complete, ", t:", total)
		print("=======================================")
		
		if not this.loading_status is None:	
			GLib.idle_add(lambda *_: this.loading_status.progress_bar.set_fraction(final_ratio))
			
	@classmethod
	def change_version_thread(this, should_mask, package, commit):
		try:
			cmd = ['flatpak-spawn', '--host', 'pkexec', 'sh', '-c']
			
			installation = package.info['installation']
			real_installation = ""
			if installation == "user" or installation == "system":
				real_installation = f"--{installation}"
			else:
				real_installation = f"--installation={installation}"
				
			suffix = ""
			unmask_cmd = f"flatpak mask --remove {real_installation} {package.info['id']} && "
			change_version_cmd = f"flatpak update {package.info['ref']} --commit={commit} {real_installation} -y"
			mask_cmd = f" && flatpak mask {real_installation} {package.info['id']}"
			if package.is_masked:
				suffix += unmask_cmd
				
			suffix += change_version_cmd
			if should_mask:
				suffix += mask_cmd
				
			cmd.append(suffix)
			this.process = subprocess.Popen(cmd, stdout=subprocess.PIPE,  stderr=subprocess.PIPE, text=True)
			percent_pattern = r'\d{1,3}%'
			amount_pattern = r'(\d+)/(\d+)'
			for line in this.process.stdout:
				line = line.strip()
				percent_match = re.search(percent_pattern, line)
				if percent_match:
					ratio = int(percent_match.group()[0:-1]) / 100.0
					amount_match = re.search(amount_pattern, line)
					if amount_match:
						amount = amount_match.group().split('/')
						complete = int(amount[0]) - 1
						total = int(amount[1])
						this.update_status(ratio, complete, total)
					else:
						this.update_status(ratio, 0, 1)
						
			this.process.wait(timeout=10)
			if error := this.process.communicate()[1].strip():
				this.on_error(_("Error occurred while changing version"), error)
				
		except subprocess.TimeoutExpired as te:
			this.process.terminate()
			this.on_error(_("Error occurred while changing version"), _("Failed to exit cleanly"))
			
		except Exception as e:
			this.process.terminate()
			this.on_error(_("Error occurred while changing version"), str(e))
		
	@classmethod
	def cancel(this):
		if this.process is None:
			return
			
		try:
			this.process.terminate()
			this.process.wait(timeout=10)
		except Exception as e:
			this.on_error(_("Could not cancel version change"), str(e))
			
	@classmethod
	def on_done(this, *args):
		this.process = None
		HostInfo.main_window.remove_refresh_lockout("changing version")
		if not this.loading_status is None:
			this.loading_status.progress_bar.set_fraction(0.0)
			
		if not this.callback is None:
			this.callback(this.did_error)
			
	@classmethod
	def on_error(this, user_facing_label, error_message):
		this.did_error = True
		if not this.error_callback is None:
			this.error_callback(user_facing_label, error_message)
			
	@classmethod
	def change_version(this, should_mask, package, commit, loading_status=None, callback=None, error_callback=None):
		if not this.process is None:
			this.on_error(_("Could not change version"), _("Another package is changing version."))
			return False
			
		this.loading_status = loading_status
		this.callback = callback
		this.error_callback = error_callback
		HostInfo.main_window.add_refresh_lockout("changing version")
		Gio.Task.new(None, None, this.on_done).run_in_thread(lambda *_: this.change_version_thread(should_mask, package, commit))
		return True
