from gi.repository import GLib, Gio
from src.host_info import HostInfo
import subprocess, re
import gettext

_ = gettext.gettext


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 = []

			installation = package.info["installation"]
			real_installation = ""
			if installation == "user":
				real_installation = f"--{installation}"
			elif installation == "system":
				cmd.append("pkexec")
				real_installation = f"--{installation}"
			else:
				real_installation = f"--installation={installation}"

			cmd += ["sh", "-c"]

			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:
			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
