#!/usr/bin/python3
# coding: utf-8

#################################################################
#
# Copyright (C) ИП Пуляев Григорий Васильевич, 2024
# email: rodegast@xmail.ru
# telegram: t.me/kybyc_meta_test
#
# Расширение добавляет поддержку браузера Mozilla Firefox
# в платформу автоматизированного тестирования Meta Test.
#
# Этот файл НЕ является частью платформы Meta Test.
# Лицензия расширения - Apache License 2.0
# https://www.apache.org/licenses/LICENSE-2.0
#
#################################################################

import os
from functools import lru_cache

from selenium import webdriver
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
from selenium.webdriver.firefox.service import Service as FirefoxService

from errors import InstallError, NetError
from core_brower import WebBrowser, install
from parameter import CONFIG, BROWSER_VERSION, ItemConfig
from utilites import Version, get_platform, get_api_json, rMkDir, x_path

__all__ = (
	"get_geckodriver",
)

def get_driver_version(version):
	# https://github.com/SeleniumHQ/selenium/blob/trunk/common/geckodriver/geckodriver-support.json
	version = Version(version)
	if version >= Version("128.0"):
		return "0.36.0"
	elif version >= Version("115.0"):
		return "0.35.0"
	elif version >= Version("102.0"):
		return "0.33.0"
	elif version >= Version("91.0"):
		return "0.31.0"
	elif version >= Version("78.0"):
		return "0.30.0"
	elif version >= Version("60.0"):
		return "0.29.1"
	elif version >= Version("57.0"):
		return "0.25.0"
	elif version >= Version("55.0"):
		return "0.20.1"
	elif version >= Version("53.0"):
		return "0.18.0"
	elif version >= Version("52.0"):
		return "0.17.0"
	raise InstallError("Для версии %s недоступен geckodriver"%version)

@lru_cache
def get_geckodriver(version, short_version, browser_name):
	"""
	Возвращает путь к файлу geckodriver-а
	"""
	if not CONFIG.GECKO_DRIVER_PATH or not os.path.isfile(CONFIG.GECKO_DRIVER_PATH):
		platform     = get_platform()
		install_path = os.path.join(CONFIG.BROWSERS_DIR, browser_name, short_version)
		driver_file  = os.path.join(install_path, "geckodriver")
		if platform == "win64":
			driver_file += ".exe"
		if not os.path.isfile(driver_file):
			rMkDir(os.path.join(CONFIG.BROWSERS_DIR, browser_name))
			driver_version = get_driver_version(version)
			if platform == "win64":
				url = "https://github.com/mozilla/geckodriver/releases/download/v%s/geckodriver-v%s-win64.zip"%(driver_version, driver_version)
				install(url, install_path, {"geckodriver.exe": driver_file})
			elif platform == "linux64":
				url = "https://github.com/mozilla/geckodriver/releases/download/v%s/geckodriver-v%s-linux64.tar.gz"%(driver_version, driver_version)
				install(url, install_path, {"geckodriver": driver_file})
			else:
				url = "https://github.com/mozilla/geckodriver/releases/download/v%s/geckodriver-v%s-macos.tar.gz"%(driver_version, driver_version)
				install(url, install_path, {"geckodriver": driver_file})
		CONFIG.GECKO_DRIVER_PATH = x_path(driver_file)
	return CONFIG.GECKO_DRIVER_PATH

class Firefox(WebBrowser):
	@lru_cache
	def installed_versions(self):
		"""
		Возвращает описание локально установленных браузеров
		{"версия браузера": "путь к исполняемому файлу"}
		"""
		platform = get_platform()
		if platform == "mac-x64":
			return self.mac_installed_version("/Applications/Firefox.app/Contents/MacOS/firefox")
		elif platform == "linux64":
			return self.linux_installed_version(("firefox", "firefox-esr"))
		return self.windows_installed_version((
			r"PROGRAMFILES\\Mozilla Firefox\\firefox.exe"
			, r"PROGRAMFILES(X86)\\Mozilla Firefox\\firefox.exe"
			, r"LOCALAPPDATA\\Mozilla Firefox\\firefox.exe"
			, r"PROGRAMW6432\\Mozilla Firefox\\firefox.exe"
		))
	
	def linux_install_browser(self, install_path):
		"""
		Скачиваем и устанавливает браузер для linux
		Возвращает путь к исполняемому файлу
		"""
		firefox_path = os.path.join(install_path, "firefox", "firefox")
		if not os.path.isfile(firefox_path):
			install([
				"https://archive.mozilla.org/pub/firefox/releases/%s/linux-x86_64/ru/firefox-%s.tar.xz"%(self.version, self.version)
				, "https://archive.mozilla.org/pub/firefox/releases/%s/linux-x86_64/ru/firefox-%s.tar.bz2"%(self.version, self.version)
			], install_path)
			x_path(firefox_path)
		return x_path(firefox_path)
	
	@lru_cache
	def available_versions(self):
		"""
		Возвращает список версий браузеров которые возможно установить
		"""
		if self.version.upper() in (BROWSER_VERSION.DEV, BROWSER_VERSION.BETA, BROWSER_VERSION.CANARY):
			endpoint = "https://product-details.mozilla.org/1.0/firefox_history_development_releases.json"
		else:
			endpoint = "https://product-details.mozilla.org/1.0/firefox_history_stability_releases.json"
		try:
			data = get_api_json(endpoint, timeout=CONFIG.REMOTE_TIMEOUT)
		except NetError:
			raise InstallError("Не удалось получить список доступных браузеров firefox:\n "+endpoint)
		except Exception as err:
			return []
		return list(data)
	
	def make_options(self):
		"""
		Возвращает объект конфигурации драйвера
		"""
		options = webdriver.FirefoxOptions()
		options.page_load_strategy = CONFIG.PAGE_LOAD_STRATEGY.lower()
		options.set_preference("dom.webdriver.enabled", False)
		options.set_preference("useAutomationExtension", False)
		options.set_preference("dom.webcomponents.enabled", True)
		options.set_preference("dom.webcomponents.shadowdom.enabled", True)
		options.set_preference("dom.webcomponents.customelements.enabled", True)
		
		if self.profile_dir:
			profile = FirefoxProfile(profile_directory=self.profile_dir)
		else:
			profile = FirefoxProfile()
		
		platform = get_platform()
		if platform == "linux64":
			user_agent = "Mozilla/5.0 (X11; Linux i686; rv:%s) Gecko/20100101 Firefox/%s"
		elif platform == "mac-x64":
			user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 14.7; rv:%s) Gecko/20100101 Firefox/%s"
		else:
			user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:%s) Gecko/20100101 Firefox/%s"
		profile.set_preference(
			"general.useragent.override", user_agent%(self.short_version, self.short_version)
		)
		profile.set_preference("intl.accept_languages", CONFIG.BROWSER_LANG)
		profile.set_preference("general.smoothScroll", False)
		
		options.profile = profile
		options.add_argument("--disable-updater")
		if self.download_dir:
			options.enable_downloads = True
			profile.accept_untrusted_certs = True
			profile.set_eneble_native_elements = False
			profile.assume_untrusted_cert_issuer = False
			
			options.set_preference("pdfjs.disabled", True)
			options.set_preference("plugin.scan.Acrobat", "99.0")
			options.set_preference("plugin.scan.plid.all", False)
			options.set_preference("browser.download.folderList", 2)
			options.set_preference("browser.download.panel.shown", False)
			options.set_preference("browser.download.useToolkitUI", False)
			options.set_preference("browser.helperApps.alwaysAsk.force", False)
			options.set_preference("browser.download.manager.useWindow", False)
			options.set_preference("browser.download.animateNotifications", False)
			options.set_preference("browser.download.manager.closeWhenDone", True)
			options.set_preference("browser.download.manager.alertOnEXEOpen", False)
			options.set_preference("browser.download.manager.showWhenStarting", False)
			options.set_preference("browser.download.manager.focusWhenStarting", False)
			options.set_preference("browser.download.manager.showAlertOnComplete", False)
			
			if not self.is_local_browser:
				options.set_capability("se:downloadsEnabled", True)
			else:
				options.set_preference("browser.download.useDownloadDir", True)
				options.set_preference("browser.download.dir", self.download_dir)
		else:
			options.enable_downloads = False
		
		if CONFIG.HEADLESS and Version(self.version) > Version(55):
			options.add_argument("--headless")
		if CONFIG.PROXY:
			host, port = CONFIG.PROXY.split(":")
			profile.set_preference("network.proxy.type", 1)
			profile.set_preference("network.proxy.http", host)
			profile.set_preference("network.proxy.http_port", int(port))
			profile.set_preference("network.proxy.socks", host)
			profile.set_preference("network.proxy.socks_port", int(port))
			profile.set_preference("network.proxy.socks_remote_dns", False)
			profile.set_preference("network.proxy.ssl", host)
			profile.set_preference("network.proxy.ssl_port", int(port))
			profile.set_preference("general.useragent.override","whater_useragent")
		if CONFIG.INCOGNITO:
			options.add_argument("--private")
			options.set_preference("browser.private.browsing.autostart", True)
		if self.cache_dir and not CONFIG.INCOGNITO:
			profile.set_preference("browser.cache.disk.enable", True)
			profile.set_preference("browser.cache.disk.parent_directory", self.cache_dir)
		else:
			profile.set_preference("browser.cache.disk.enable", False)
			profile.set_preference("browser.cache.memory.enable", True)
		if self.is_local_browser and self.binary_location:
			if self.binary_location:
				options.binary_location = self.binary_location
				options.firefox_binary  = self.binary_location
			options.executable_path = self.driver_path
		else:
			options.set_capability("browserName", self.browser_name)
			options.set_capability("browserVersion", self.version)
			options.set_capability("acceptInsecureCerts", True)
		profile.update_preferences()
		return options
	
	def make_service(self):
		"""
		Возвращает объект обслуживания
		"""
		return FirefoxService(self.driver_path)
	
	@property
	def web_driver_class(self):
		"""
		Возвращает класс веб-драйвера
		"""
		return webdriver.Firefox
	
	@property
	def browser_name(self):
		"""
		Возвращает имя браузера
		"""
		return "firefox"
	
	@property
	def driver_path(self):
		"""
		Возвращает путь к файлу драйвера
		"""
		return get_geckodriver(self.version, self.short_version, self.browser_name)
	
	@property
	def user_local_path(self):
		"""
		Путь к исполняемому файлу браузера указанный пользователем
		"""
		return CONFIG.FIREFOX_LOCAL_PATH
	
	@property
	def user_version(self):
		"""
		Версия браузера которую указал пользователь в качестве используемой
		"""
		return CONFIG.FIREFOX_VERSION
	
	@property
	def short_version(self):
		"""
		Возвращает сокращённую версию браузера
		"""
		return ".".join([self.version.split(".")[0], "0"])

ItemConfig.registrator(
	ItemConfig("GECKO_DRIVER_PATH",    str, "", "Путь к geckodriver")
	, ItemConfig("FIREFOX_VERSION",    str, "", "FIREFOX: Версия firefox")
	, ItemConfig("FIREFOX_LOCAL_PATH", str, "", "FIREFOX: Путь к исполняемому файлу firefox")
)
