# ----------------------------------------------------------------------------------------
# DriverFactory
#
# Class for creating a browser instance
# The options are:
# - Local browser (on windows)
# - Local browser (on Linux)
# - Local browser (on Mac)
#
# OS detection is automatic
#
# Chrome driver locations:
# - Linux, assume the chrome driver is already installed as part of docker container
# - Mac, assume the chrome driver is already part of the path
# - Windows, chrome driver exe is part of the python package
#
# Chrome downloads directory:
# - This is set to $ROBOTOUT/downloads
# -----------------------------------------------------------------------------------------

from robot.api import logger
from accelarobotkeywords.config.seleniumsettings import SeleniumSettings
from accelarobotkeywords.config.operatingsystems import OperatingSystem
from selenium import webdriver
from os import path
from accelarobotkeywords.framework.framework import Framework
from accelarobotkeywords.config.displaysettings import DisplaySettings
from accelarobotkeywords.config.browserlocations import BrowserLocations
from accelarobotkeywords.config.desiredcapabilities import DesiredCapabilities
from robot.libraries.BuiltIn import BuiltIn
from selenium.webdriver.chrome.service import Service
# from selenium.webdriver.common.by import By
import os

class DriverFactory:

    @staticmethod
    def create_browser():
        """Creates a chrome instance and returns the driver object"""
        logger.debug("Checking the value of IsLamdaTest tag")
        remote_browser = "No"
        global variables
        variables = BuiltIn().get_variables()
        try:
            testTags = variables['@{TEST_TAGS}']
            for s in range(len(testTags)):
                if testTags[s].split(":")[0] == "IsLamdaTest":
                    print("Value of IsLamdaTest is: {}".format(testTags[s].split(":")[1]))
                    remote_browser = testTags[s].split(":")[1]
        except:
            logger.info("TEST_TAGS are not set")
        try:
            browser_locations = os.environ.get('BROWSER_LOCATION')
            logger.debug('BROWSER_LOCATION is ' + str(browser_locations))
            if BrowserLocations.get_browser_location(browser_locations) == BrowserLocations.Remote:
                desired_capabilities = os.environ.get('BROWSER_CAPABILITIES')
                job_name = os.environ.get('REPORTTITLE')
                build_number = os.environ.get('JENKINSBUILDNUMBER')
                logger.debug('BROWSER_CAPABILITIES is ' + str(desired_capabilities))
                if DesiredCapabilities.get_browser_desired_capabilities(desired_capabilities) is not None:
                    customcapabilities = DesiredCapabilities.get_browser_desired_capabilities(desired_capabilities)
                    if job_name is None or build_number is None:
                        customcapabilities['build'] = variables['${TEST_NAME}']
                    else:
                        customcapabilities['build'] = "%s%s" % (job_name,build_number)
                    customcapabilities['name'] = variables['${TEST_NAME}']
                    driver = webdriver.Remote(
                            desired_capabilities=customcapabilities,
                            command_executor=DriverFactory.__get_grid_url()
                        )
                else:
                    raise Exception('Browser capabilitise not defined')
            else:
                logger.debug("Creating chrome instance")
                system = OperatingSystem.get_operating_system()
                if system == OperatingSystem.Windows:
                    driver = DriverFactory.__create_local_windows()
                elif system == OperatingSystem.Linux:
                    driver = DriverFactory.__create_local_linux()
                elif system == OperatingSystem.Mac:
                    driver = DriverFactory.__create_local_mac()
                else:
                    raise Exception("Unsupported browser location and operating system")
                browser_name = SeleniumSettings.get_browser_name().lower()
                network_logger_enabled = os.environ.get('NETWORK_LOGGER_ENABLED', '').upper() == 'TRUE'
                if network_logger_enabled and browser_name in ('chrome', 'edge'):
                    try:
                        driver.execute_cdp_cmd("Network.enable", {})
                        logger.info("CDP Network domain enabled for NetworkLogger")
                    except Exception as e:
                        logger.warning("Failed to enable CDP Network domain: %r", e)
                driver.implicitly_wait(SeleniumSettings.get_implicit_wait())
                driver.set_script_timeout(SeleniumSettings.get_script_wait())
                driver.set_page_load_timeout(SeleniumSettings.get_page_load_wait())

            return driver
        except Exception as e:
            logger.error("Error creating chrome instance: %s" % e)
            raise

    @staticmethod
    def __create_local_windows():
        """Creates chrome instance on local windows"""
        try:
            if (SeleniumSettings.get_browser_name() == 'edge'):
                options = DriverFactory.__create_edge_options()
                driver_path = DriverFactory.__get_driver_location(OperatingSystem.Windows)
                driver = webdriver.Edge(service=Service(driver_path), options=options)
                logger.info("Edge driver instance created for Windows")
            elif (SeleniumSettings.get_browser_name() == 'firefox'):
                driver_path = DriverFactory.__get_driver_location(OperatingSystem.Windows)
                driver = webdriver.Firefox(service=Service(driver_path))
                logger.info("Firefox driver instance created for Windows")
            else:
                options = DriverFactory.__create_options()
                driver_path = DriverFactory.__get_driver_location(OperatingSystem.Windows)
                driver = webdriver.Chrome(service=Service(driver_path), options=options)
                logger.info("Chrome driver instance created for Windows")
            return driver
        except Exception as e:
            logger.error("Unable to create chrome driver on Windows: %s" % e)
            raise

    @staticmethod
    def __create_local_linux():
        """Creates chrome instance on local linux"""
        try:
            options = DriverFactory.__create_options()
            driver_path = DriverFactory.__get_driver_location(OperatingSystem.Linux)
            driver = webdriver.Chrome(service=Service(driver_path), options=options)
            logger.info("Chrome driver instance created for linux")
            return driver
        except Exception as e:
            logger.error("Unable to create chrome driver on Linux: %s" % e)
            raise

    @staticmethod
    def __create_local_mac():
        """Creates chrome instance on local mac"""
        try:
            if (SeleniumSettings.get_browser_name() == 'edge'):
                options = DriverFactory.__create_edge_options()
                driver_path = DriverFactory.__get_driver_location(OperatingSystem.Mac)
                driver = webdriver.Edge(service=Service(driver_path), options=options)
                logger.info("Edge driver instance created for mac")
            else:
                options = DriverFactory.__create_options()
                driver_path = DriverFactory.__get_driver_location(OperatingSystem.Mac)
                driver = webdriver.Chrome(service=Service(driver_path), options=options)
                logger.info("Chrome driver instance created for Mac")
            return driver
        except Exception as e:
            logger.error("Unable to create chrome driver on Mac: %s" % e)
            raise

    @staticmethod
    def __create_options():
        """Sets up the chrome options"""
        options = webdriver.ChromeOptions()
        downloads = Framework.get_downloads_path()
        options.add_argument("--allow-running-insecure-content")
        options.add_argument("--disable-plugins")
        options.add_argument("--disable-extensions")
        options.add_argument("--disable-infobars")
        options.add_argument("--start-maximized")
        options.add_argument("--no-sandbox")
        options.add_argument("--disable-background-networking")
        preferences = {
            "download.default_directory": downloads,
            "download.prompt_for_download": False,
            "profile.default_content_setting_values.automatic_downloads": 1,
            "credentials_enable_service": False,
            "profile.password_manager_enabled": False
        }
        system = OperatingSystem.get_operating_system()
        if system == OperatingSystem.Linux:
            preferences["download.prompt_for_download"] = False
            preferences["download.directory_upgrade"] = True
            width = DisplaySettings.get_width()
            height = DisplaySettings.get_height()
            options.add_argument("--window-size=%s,%s" % (width, height))
        options.add_experimental_option("prefs", preferences)
        options.add_experimental_option('useAutomationExtension', False)
        options.add_experimental_option('excludeSwitches', ['enable-automation'])
        network_logger_enabled = os.environ.get('NETWORK_LOGGER_ENABLED', '').upper() == 'TRUE'
        if network_logger_enabled:
            options.set_capability('goog:loggingPrefs', {'performance': 'ALL'})
            logger.info("Performance logging enabled")
        else:
            logger.info("Performance logging disabled")
        zap_proxy_enabled = os.environ.get('ZAP_ENABLED')
        if zap_proxy_enabled != None:
            if zap_proxy_enabled.upper() == "TRUE":
                zap_proxy_host = os.environ.get('PROXY_HOST')
                zap_proxy_port = os.environ.get('PROXY_PORT')
                zap_proxy_host_port = "http://" + zap_proxy_host + ":" + str(zap_proxy_port)
                zap_proxy_host_port_https = "https://" + zap_proxy_host + ":" + str(zap_proxy_port)
                options.add_argument('--proxy-server=%s' % zap_proxy_host_port)
                options.add_argument('ignore-certificate-errors')
        return options
    
    @staticmethod
    def __create_edge_options():
        """Sets up the edge options"""
        options = webdriver.EdgeOptions()
        options.add_argument("--start-maximized") 
        options.add_argument("--disable-extensions")
        options.add_argument("--disable-popup-blocking") 
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option("useAutomationExtension", False)
        prefs = {
            "download.default_directory": os.path.join(os.path.expanduser("~"), "Downloads"),
            "profile.default_content_settings.popups": 0,
            "credentials_enable_service": False,
            "profile.password_manager_enabled": False
        }
        options.add_experimental_option("prefs", prefs)
        return options

    @staticmethod
    def __get_driver_location(system):
        """Gets the location of the driver executable"""
        if system == OperatingSystem.Linux:
            driver_path = '/usr/bin/chromedriver'
            logger.info('Driver path: %s' % driver_path)
            return driver_path
        elif system == OperatingSystem.Mac:
            drivers_dir_path = path.join(path.dirname(path.realpath(__file__)), 'drivers')
            if SeleniumSettings.get_browser_name() == 'edge':
                driver_path = path.join(drivers_dir_path, 'msedgedriver')
            else:
                driver_path = path.join(drivers_dir_path, 'chromedriver')
            logger.info('Driver path: %s' % driver_path)
            return driver_path
        elif system == OperatingSystem.Windows:
            drivers_dir_path = path.join(path.dirname(path.realpath(__file__)), 'drivers')
            if SeleniumSettings.get_browser_name() == 'edge':
                driver_path = path.join(drivers_dir_path, 'msedgedriver.exe')
            elif SeleniumSettings.get_browser_name() == 'firefox':
                driver_path = path.join(drivers_dir_path, 'geckodriver.exe')
            else:
                driver_path = path.join(drivers_dir_path, 'chromedriver.exe')
            logger.info('Driver path: %s' % driver_path)
            return driver_path
        else:
            raise Exception("Driver path cannot be created for current OS")

    @staticmethod
    def __get_capabilities():
        """Returns the capabilities for the remote driver"""
        desired_cap = {
            "build": variables['${SUITE NAME}'],
            "name": variables['${TEST_NAME}'],
            "platformName": "Windows 10",
            "browserName": "Chrome",
            "version": "91.0",
            "resolution": "1440x900"
        }
        return desired_cap

    @staticmethod
    def __get_grid_url():
        """Gets Grid url of the lamda test server"""
        # username: Username can be found at automation dashboard
        username = "mvishwanath"
        # accessToken:  AccessToken can be generated from automation dashboard or profile section
        accessToken = "KwTF60t5OSIHXKsmBee4ZaVYXZykzqMXcQQYN4PcmKZi0j60Um"
        # gridUrl: gridUrl can be found at automation dashboard
        gridUrl = "hub.lambdatest.com/wd/hub"

        # URL: https://{username}:{accessToken}@hub.lambdatest.com/wd/hub
        url = "https://" + username + ":" + accessToken + "@" + gridUrl
        return url
