from gi.repository import Adw, Gtk, GLib, Gio
from src.host_info import HostInfo
from src.gtk.error_toast import ErrorToast
from src.remotes_page.remote_row import RemoteRow
from src.remotes_page.add_remote_dialog import AddRemoteDialog
from src.gtk.loading_status import LoadingStatus
import subprocess
import gettext

_ = gettext.gettext


class NewRemoteRow(Adw.ActionRow):
	__gtype_name__ = "NewRemoteRow"

	def idle_stuff(self, *args):
		self.set_title(self.info["title"])
		self.set_subtitle(self.info["description"])
		self.add_suffix(Gtk.Image.new_from_icon_name("warehouse:plus-large-symbolic"))

	def __init__(self, info, **kwargs):
		super().__init__(**kwargs)
		self.info = info
		GLib.idle_add(self.idle_stuff)
		self.set_activatable(True)


@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/remotes_page.ui")
class RemotesPage(Adw.NavigationPage):
	# Preselected Remotes
	new_remotes = [
		{
			"title": "AppCenter",
			"name": "appcenter",
			"link": "https://flatpak.elementary.io/repo.flatpakrepo",
			"description": _("The open source, pay-what-you-want app store from elementary"),
		},
		{
			"title": "Flathub",
			"name": "flathub",
			"link": "https://dl.flathub.org/repo/flathub.flatpakrepo",
			"description": _("Central repository of Flatpak applications"),
		},
		{
			"title": "Flathub beta",
			"name": "flathub-beta",
			"link": "https://flathub.org/beta-repo/flathub-beta.flatpakrepo",
			"description": _("Beta builds of Flatpak applications"),
		},
		{
			"title": "Fedora",
			"name": "fedora",
			"link": "oci+https://registry.fedoraproject.org",
			"description": _("Flatpaks packaged by Fedora Linux"),
		},
		{
			"title": "GNOME Nightly",
			"name": "gnome-nightly",
			"link": "https://nightly.gnome.org/gnome-nightly.flatpakrepo",
			"description": _("The latest beta GNOME Apps and Runtimes"),
		},
		{
			"title": "WebKit Developer SDK",
			"name": "webkit-sdk",
			"link": "https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo",
			"description": _("Central repository of the WebKit Developer and Runtime SDK"),
		},
	]

	__gtype_name__ = "RemotesPage"
	gtc = Gtk.Template.Child

	search_button = gtc()
	search_bar = gtc()
	search_entry = gtc()
	toast_overlay = gtc()
	stack = gtc()
	current_remotes_group = gtc()
	show_disabled_button = gtc()
	show_disabled_button_content = gtc()
	new_remotes_group = gtc()
	file_remote_row = gtc()
	custom_remote_row = gtc()
	none_visible = gtc()
	status_stack = gtc()
	loading_view = gtc()
	adding_view = gtc()
	main_view = gtc()

	no_results = gtc()
	no_remotes = gtc()
	content_page = gtc()

	# Referred to in the main window
	# It is used to determine if a new page should be made or not
	# This must be set to the created object from within the class's __init__ method
	instance = None
	page_name = "remotes"

	def start_loading(self):
		self.search_button.set_active(False)
		self.status_stack.set_visible_child(self.loading_view)
		self.total_disabled = 0
		for row in self.current_remote_rows:
			self.current_remotes_group.remove(row)

		self.current_remote_rows.clear()

	def end_loading(self):
		show_disabled = self.show_disabled_button.get_active()
		self.show_disabled_button.set_visible(False)
		total_visible = 0
		for installation, remotes in HostInfo.remotes.items():
			for remote in remotes:
				row = RemoteRow(self, installation, remote)
				self.current_remote_rows.append(row)
				self.current_remotes_group.add(row)
				if row.remote.disabled:
					self.total_disabled += 1
					self.show_disabled_button.set_visible(True)
					if show_disabled:
						total_visible += 1
					else:
						row.set_visible(False)
				else:
					total_visible += 1

		self.none_visible.set_visible(total_visible == 0)

		if len(self.current_remote_rows) == 0:
			self.no_remotes.set_visible(True)
			self.none_visible.set_visible(False)
		else:
			self.no_remotes.set_visible(False)

		GLib.idle_add(lambda *_: self.status_stack.set_visible_child(self.main_view))

	def none_visible_handler(self):
		any_visible = False
		for row in self.current_remote_rows:
			if row.get_visible():
				any_visible = True
				break

		self.none_visible.set_visible(not any_visible)

	def filter_remote(self, row):
		self.filter_setting.set_boolean("show-apps", True)
		self.filter_setting.set_boolean("show-runtimes", True)
		self.filter_setting.set_string("remotes-list", f"{row.remote.name}<>{row.installation};")
		self.filter_setting.reset("runtimes-list")
		packages_page = self.main_window.pages[self.main_window.packages_row]
		packages_page.filters_page.generate_filters()
		packages_page.apply_filters()
		GLib.idle_add(lambda *_: self.main_window.activate_row(self.main_window.packages_row))
		GLib.idle_add(lambda *args: packages_page.packages_toast_overlay.add_toast(Adw.Toast(title=_("Showing all packages from {}").format(row.remote.title))))

	def remove_remote(self, row):
		error = [None]

		def thread(*args):
			install = row.installation
			cmd = ["flatpak", "remote-delete", row.remote.name, "--force"]
			if install == "user" or install == "system":
				cmd.append(f"--{install}")
			else:
				cmd.append(f"--installation={install}")

			try:
				subprocess.run(cmd, check=True, capture_output=True, text=True)
			except subprocess.CalledProcessError as cpe:
				error[0] = cpe.stderr
			except Exception as e:
				error[0] = e

		def callback(*args):
			if error[0]:
				self.toast_overlay.add_toast(ErrorToast(_("Could not remove remote"), str(error[0])).toast)
			else:
				filters_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].filters_page
				filters_page.settings.reset("remotes-list")
				filters_page.all_remotes_switch.set_active(False)
				# filters_page.packages_page.apply_filters()
				self.main_window.refresh_handler()
				self.toast_overlay.add_toast(Adw.Toast(title=_("Removed {}").format(row.remote.title)))

		def on_response(_, response):
			if response != "continue":
				return

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

		dialog = Adw.AlertDialog(
			heading=_("Remove {}?").format(row.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(row.remote.name)
		)
		dialog.add_response("cancel", _("Cancel"))
		dialog.add_response("continue", _("Remove"))
		dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
		dialog.connect("response", on_response)
		dialog.present(self.main_window)

	def on_search(self, entry):
		text = entry.get_text().lower()
		total = 0
		show_disabled = self.show_disabled_button.get_active()

		for row in self.current_remote_rows:
			title_match = text in row.get_title().lower()
			subtitle_match = text in row.get_subtitle().lower()
			visible = (title_match or subtitle_match) and (show_disabled or not row.remote.disabled)
			total += visible
			row.set_visible(visible)

		if text == "":
			self.stack.set_visible_child(self.content_page)
			return

		self.stack.set_visible_child(self.content_page if total > 0 else self.no_results)

	def local_file_handler(self, path):
		try:
			name = path.split("/")[-1].split(".")[0]
			info = {
				"title": name.title(),
				"name": name,
				"description": "local file",
				"link": path,
			}
			AddRemoteDialog(self.main_window, self, info).present(self.main_window)
		except Exception as e:
			self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(e)).toast)

	def file_callback(self, chooser, result):
		try:
			file = chooser.open_finish(result)
			path = file.get_path()
			self.local_file_handler(path)
		except GLib.GError as ge:
			if "Dismissed by user" in str(ge):
				return
			self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(ge)).toast)
		except Exception as e:
			self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(e)).toast)

	def add_file_handler(self):
		file_filter = Gtk.FileFilter(name=_("Flatpak Repos"))
		file_filter.add_suffix("flatpakrepo")
		filters = Gio.ListStore.new(Gtk.FileFilter)
		filters.append(file_filter)
		file_chooser = Gtk.FileDialog()
		file_chooser.set_filters(filters)
		file_chooser.set_default_filter(file_filter)
		file_chooser.open(self.main_window, None, self.file_callback)

	def show_disabled_handler(self, button):
		show_disabled = button.get_active()
		self.show_disabled_button_content.set_icon_name(
			"warehouse:eye-open-negative-filled-symbolic" if show_disabled else "warehouse:eye-not-looking-symbolic"
		)
		total_visible = 0
		for row in self.current_remote_rows:
			if row.remote.disabled:
				if show_disabled:  # show disabled
					row.set_visible(True)
					total_visible += 1
				else:
					row.set_visible(False)
			else:
				total_visible += 1

		self.none_visible.set_visible(total_visible == 0)

	def new_custom_handler(self, *args):
		AddRemoteDialog(self.main_window, self).present(self.main_window)

	def __init__(self, main_window, **kwargs):
		super().__init__(**kwargs)

		# Extra Object Creation
		self.__class__.instance = self
		self.main_window = main_window
		self.search_bar.set_key_capture_widget(main_window)
		self.current_remote_rows = []
		self.filter_setting = Gio.Settings.new("io.github.flattool.Warehouse.filter")
		self.total_disabled = 0

		# Connections
		self.file_remote_row.connect("activated", lambda *_: self.add_file_handler())
		self.custom_remote_row.connect("activated", self.new_custom_handler)
		self.search_entry.connect("search-changed", self.on_search)
		self.show_disabled_button.connect("toggled", self.show_disabled_handler)

		# Apply
		self.adding_view.set_content(LoadingStatus(_("Adding Remote"), _("This should only take a moment")))
		self.loading_view.set_content(LoadingStatus(_("Loading Remotes"), _("This should only take a moment")))
		for item in self.new_remotes:
			row = NewRemoteRow(item)
			row.connect("activated", lambda *_, remote_info=item: AddRemoteDialog(main_window, self, remote_info).present(main_window))
			self.new_remotes_group.add(row)
