from gi.repository import Adw, Gtk, GLib, Gio
from src.host_info import HostInfo
from src.gtk.error_toast import ErrorToast
from src.snapshot_page.tar_worker import TarWorker
import os, subprocess, json
import gettext

_ = gettext.gettext


@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/snapshot_box.ui")
class SnapshotBox(Gtk.Box):
	__gtype_name__ = "SnapshotBox"
	gtc = Gtk.Template.Child

	title = gtc()
	date = gtc()
	version = gtc()
	apply_button = gtc()
	rename_button = gtc()
	rename_menu = gtc()
	rename_entry = gtc()
	apply_rename = gtc()
	trash_button = gtc()

	def create_json(self):
		try:
			data = {
				"snapshot_version": 1,
				"name": "",
			}
			with open(self.json_path, "w") as file:
				json.dump(data, file, indent=4)
				return None

		except Exception as e:
			self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)

	def update_json(self, key, value):
		try:
			with open(self.json_path, "r+") as file:
				data = json.load(file)
				data[key] = value
				file.seek(0)
				json.dump(data, file, indent=4)
				file.truncate()

		except Exception as e:
			self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)

	def load_from_json(self):
		if not os.path.exists(self.json_path):
			self.create_json()

		try:
			with open(self.json_path, "r") as file:
				data = json.load(file)
				name = data["name"]
				if name != "":
					self.title.set_label(GLib.markup_escape_text(name))
				else:
					self.title.set_label(_("No Name Set"))

		except Exception as e:
			self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)

	def on_rename(self, widget):
		if not self.valid_checker():
			return

		self.update_json("name", self.rename_entry.get_text().strip())
		self.load_from_json()
		self.rename_menu.popdown()

	def valid_checker(self, *args):
		text = self.rename_entry.get_text().strip()
		valid = not ("/" in text or "\0" in text) and len(text) > 0
		self.apply_rename.set_sensitive(valid)
		if valid:
			self.rename_entry.remove_css_class("error")
		else:
			self.rename_entry.add_css_class("error")

		return valid

	def on_trash(self, button):
		error = [None]
		path = f"{self.snapshots_path}{self.folder}"
		if self.snapshot_page.is_trash_dialog_open:
			return

		def thread(*args):
			try:
				subprocess.run(["gio", "trash", path], capture_output=True, text=True, check=True)
			except subprocess.CalledProcessError as cpe:
				error[0] = cpe.stderr
			except Exception as e:
				error[0] = str(e)

		def callback(*args):
			if not error[0] is None:
				self.toast_overlay.add_toast(ErrorToast(_("Could not trash snapshot"), error[0]).toast)
				return

			self.parent_page.on_trash()
			self.toast_overlay.add_toast(Adw.Toast.new(_("Trashed snapshot")))

		def on_response(_, response):
			self.snapshot_page.is_trash_dialog_open = False
			if response != "continue":
				return

			Gio.Task.new(None, None, callback).run_in_thread(thread)

		self.snapshot_page.is_trash_dialog_open = True
		dialog = Adw.AlertDialog(heading=_("Trash Snapshot?"), body=_("This snapshot will be sent to the trash"))
		dialog.add_response("cancel", _("Cancel"))
		dialog.add_response("continue", _("Trash"))
		dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
		dialog.connect("response", on_response)
		dialog.present(HostInfo.main_window)

	def get_fraction(self):
		loading_status = self.snapshot_page.snapshotting_status
		loading_status.progress_bar.set_fraction(self.worker.fraction)
		if self.worker.stop:
			self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.split_view)
			self.parent_page.set_snapshots(self.parent_page.package_or_folder, True)
			properties_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].properties_page
			properties_page.set_properties(properties_page.package, True)
			data_page = HostInfo.main_window.pages[HostInfo.main_window.user_data_row]
			data_page.start_loading()
			data_page.end_loading()
			if self.worker in self.snapshot_page.workers:
				self.snapshot_page.workers.remove(self.worker)

			return False  # Stop the timeout
		else:
			return True  # Continue the timeout

	def on_apply(self, button):
		def on_response(dialog, response):
			if response != "continue":
				return

			self.snapshot_page.snapshotting_status.title_label.set_label(_("Applying Snapshot"))
			self.snapshot_page.snapshotting_status.progress_label.set_visible(False)
			self.snapshot_page.snapshotting_status.progress_bar.set_fraction(0.0)
			self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.snapshotting_view)
			self.snapshot_page.workers.append(self.worker)
			self.worker.extract()
			GLib.timeout_add(200, self.get_fraction)

		has_data = os.path.exists(self.worker.new_path)
		dialog = Adw.AlertDialog(
			heading=_("Apply Snapshot?"),
			body=_("Any current user data for this app will be trashed") if has_data else "",
		)
		dialog.add_response("cancel", _("Cancel"))
		dialog.add_response("continue", _("Apply"))
		dialog.connect("response", on_response)
		dialog.present(HostInfo.main_window)

	def __init__(self, parent_page, folder, snapshots_path, toast_overlay, **kwargs):
		super().__init__(**kwargs)

		self.snapshot_page = parent_page.parent_page
		self.toast_overlay = toast_overlay
		self.app_id = snapshots_path.split("/")[-2].strip()
		self.worker = TarWorker(
			existing_path=f"{snapshots_path}{folder}",
			new_path=f"{HostInfo.home}/.var/app/{self.app_id}/",
			toast_overlay=self.toast_overlay,
		)

		split_folder = folder.split("_")
		if len(split_folder) < 2:
			return

		self.parent_page = parent_page
		self.folder = folder
		self.snapshots_path = snapshots_path
		self.epoch = int(split_folder[0])
		date_data = GLib.DateTime.new_from_unix_local(self.epoch).format("%x %X")
		self.date.set_label(date_data)
		self.version.set_label(_("Version: {}").format(split_folder[1].replace(".tar.zst", "")))
		self.json_path = f"{snapshots_path}{folder.replace('tar.zst', 'json')}"
		self.load_from_json()
		self.apply_button.connect("clicked", self.on_apply)
		self.apply_rename.connect("clicked", self.on_rename)
		self.rename_entry.connect("activate", self.on_rename)
		self.rename_entry.connect("changed", self.valid_checker)
		self.trash_button.connect("clicked", self.on_trash)
