"""
DriverFactory class
NOTE: Change this class as you add support for:
1. SauceLabs/BrowserStack
2. More browsers like Opera
"""
import dotenv,os,sys,requests,json
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.chrome import service
from selenium.webdriver.remote.webdriver import RemoteConnection
from appium import webdriver as mobile_webdriver
from conf import remote_credentials
from conf import opera_browser_conf

class DriverFactory():
    
    def __init__(self,browser='ff',browser_version=None,os_name=None):
        "Constructor for the Driver factory"
        self.browser=browser
        self.browser_version=browser_version
        self.os_name=os_name

        
    def get_web_driver(self,remote_flag,os_name,os_version,browser,browser_version,remote_project_name,remote_build_name):
        "Return the appropriate driver"
        if (remote_flag.lower() == 'y'):
            try:
                if remote_credentials.REMOTE_BROWSER_PLATFORM == 'BS':
                    web_driver = self.run_browserstack(os_name,os_version,browser,browser_version,remote_project_name,remote_build_name)
                else:
                    web_driver = self.run_sauce_lab(os_name,os_version,browser,browser_version)
                    
            except Exception as e:
                print("\nException when trying to get remote webdriver:%s"%sys.modules[__name__])
                print("Python says:%s"%str(e))
                print("SOLUTION: It looks like you are trying to use a cloud service provider (BrowserStack or Sauce Labs) to run your test. \nPlease make sure you have updated ./conf/remote_credentials.py with the right credentials and try again. \nTo use your local browser please run the test with the -M N flag.\n")
                
        elif (remote_flag.lower() == 'n'):
                web_driver = self.run_local(os_name,os_version,browser,browser_version)       
        else:
            print("DriverFactory does not know the browser: ",browser)
            web_driver = None

        return web_driver   
    

    def run_browserstack(self,os_name,os_version,browser,browser_version,remote_project_name,remote_build_name):
        "Run the test in browser stack when remote flag is 'Y'"
        #Get the browser stack credentials from browser stack credentials file
        USERNAME = remote_credentials.USERNAME
        PASSWORD = remote_credentials.ACCESS_KEY
        if browser.lower() == 'ff' or browser.lower() == 'firefox':
            desired_capabilities = DesiredCapabilities.FIREFOX            
        elif browser.lower() == 'ie':
            desired_capabilities = DesiredCapabilities.INTERNETEXPLORER
        elif browser.lower() == 'chrome':
            desired_capabilities = DesiredCapabilities.CHROME            
        elif browser.lower() == 'opera':
            desired_capabilities = DesiredCapabilities.OPERA        
        elif browser.lower() == 'safari':
            desired_capabilities = DesiredCapabilities.SAFARI
        desired_capabilities['os'] = os_name
        desired_capabilities['os_version'] = os_version
        desired_capabilities['browser_version'] = browser_version
        if remote_project_name is not None:
            desired_capabilities['project'] = remote_project_name
        if remote_build_name is not None:
            desired_capabilities['build'] = remote_build_name+"_"+str(datetime.now().strftime("%c"))

        return webdriver.Remote(RemoteConnection("http://%s:%s@hub-cloud.browserstack.com/wd/hub"%(USERNAME,PASSWORD),resolve_ip= False),
            desired_capabilities=desired_capabilities)
    

    def run_sauce_lab(self,os_name,os_version,browser,browser_version):
        "Run the test in sauce labs when remote flag is 'Y'"
        #Get the sauce labs credentials from sauce.credentials file
        USERNAME = remote_credentials.USERNAME
        PASSWORD = remote_credentials.ACCESS_KEY
        if browser.lower() == 'ff' or browser.lower() == 'firefox':
            desired_capabilities = DesiredCapabilities.FIREFOX            
        elif browser.lower() == 'ie':
            desired_capabilities = DesiredCapabilities.INTERNETEXPLORER
        elif browser.lower() == 'chrome':
            desired_capabilities = DesiredCapabilities.CHROME            
        elif browser.lower() == 'opera':
            desired_capabilities = DesiredCapabilities.OPERA        
        elif browser.lower() == 'safari':
            desired_capabilities = DesiredCapabilities.SAFARI
        desired_capabilities['version'] = browser_version
        desired_capabilities['platform'] = os_name + ' '+os_version
        
        
        return webdriver.Remote(command_executor="http://%s:%s@ondemand.saucelabs.com:80/wd/hub"%(USERNAME,PASSWORD),
                desired_capabilities= desired_capabilities)


    def run_local(self,os_name,os_version,browser,browser_version):
        "Return the local driver"
        local_driver = None
        if browser.lower() == "ff" or browser.lower() == 'firefox':
            local_driver = webdriver.Firefox()    
        elif  browser.lower() == "ie":
            local_driver = webdriver.Ie()
        elif browser.lower() == "chrome":
            local_driver = webdriver.Chrome()
        elif browser.lower() == "opera":
            opera_options = None
            try:
                opera_browser_location = opera_browser_conf.location
                options = webdriver.ChromeOptions()
                options.binary_location = opera_browser_location # path to opera executable
                local_driver = webdriver.Opera(options=options)
                    
            except Exception as e:
                print("\nException when trying to get remote webdriver:%s"%sys.modules[__name__])
                print("Python says:%s"%str(e))
                if  'no Opera binary' in str(e):
                     print("SOLUTION: It looks like you are trying to use Opera Browser. Please update Opera Browser location under conf/opera_browser_conf.\n")
        elif browser.lower() == "safari":
            local_driver = webdriver.Safari()

        return local_driver


    def run_mobile(self,mobile_os_name,mobile_os_version,device_name,app_package,app_activity,remote_flag,device_flag,app_name,app_path):
        "Setup mobile device"
        #Get the remote credentials from remote_credentials file
        USERNAME = remote_credentials.USERNAME
        PASSWORD = remote_credentials.ACCESS_KEY
        desired_capabilities = {}
        desired_capabilities['platformName'] = mobile_os_name
        desired_capabilities['platformVersion'] = mobile_os_version
        desired_capabilities['deviceName'] = device_name

        if (remote_flag.lower() == 'y'):
            desired_capabilities['idleTimeout'] = 300
            desired_capabilities['name'] = 'Appium Python Test'
            try:
                if remote_credentials.REMOTE_BROWSER_PLATFORM == 'SL':
                    self.sauce_upload(app_path,app_name) #Saucelabs expects the app to be uploaded to Sauce storage everytime the test is run
                    #Checking if the app_name is having spaces and replacing it with blank
                    if ' ' in app_name:
                        app_name = app_name.replace(' ','')                        
                    desired_capabilities['app'] = 'sauce-storage:'+app_name
                    desired_capabilities['autoAcceptAlert']= 'true'                      
                    driver = mobile_webdriver.Remote(command_executor="http://%s:%s@ondemand.saucelabs.com:80/wd/hub"%(USERNAME,PASSWORD),
                        desired_capabilities= desired_capabilities)
                else:
                    desired_capabilities['realMobile'] = 'true'                   
                    desired_capabilities['app'] = self.browser_stack_upload(app_name,app_path) #upload the application to the Browserstack Storage                                      
                    driver = mobile_webdriver.Remote(command_executor="http://%s:%s@hub.browserstack.com:80/wd/hub"%(USERNAME,PASSWORD),
                        desired_capabilities= desired_capabilities)
            except Exception as e:
                print ('\033[91m'+"\nException when trying to get remote webdriver:%s"%sys.modules[__name__]+'\033[0m')
                print ('\033[91m'+"Python says:%s"%str(e)+'\033[0m')
                print ('\033[92m'+"SOLUTION: It looks like you are trying to use a cloud service provider (BrowserStack or Sauce Labs) to run your test. \nPlease make sure you have updated ./conf/remote_credentials.py with the right credentials and try again. \nTo use your local browser please run the test with the -M N flag.\n"+'\033[0m')
        else:
            try:
                desired_capabilities['appPackage'] = app_package
                desired_capabilities['appActivity'] = app_activity
                if device_flag.lower() == 'y':
                    driver = mobile_webdriver.Remote('http://localhost:4723/wd/hub', desired_capabilities)
                else:                    
                    desired_capabilities['app'] = os.path.join(app_path,app_name)
                    driver = mobile_webdriver.Remote('http://localhost:4723/wd/hub', desired_capabilities)
            except Exception as e:
                print ('\033[91m'+"\nException when trying to get remote webdriver:%s"%sys.modules[__name__]+'\033[0m')
                print ('\033[91m'+"Python says:%s"%str(e)+'\033[0m')
                print ('\033[92m'+"SOLUTION: It looks like you are trying to run test cases with Local Appium Setup. \nPlease make sure to run Appium Server and try again.\n"+'\033[0m')

        return driver	


    def sauce_upload(self,app_path,app_name):  
        "Upload the apk to the sauce temperory storage"
        USERNAME = remote_credentials.USERNAME
        PASSWORD = remote_credentials.ACCESS_KEY
        result_flag=False
        try:
            headers = {'Content-Type':'application/octet-stream'}                   
            params = os.path.join(app_path,app_name)                         
            fp = open(params,'rb')
            data = fp.read()   
            fp.close()
            #Checking if the app_name is having spaces and replacing it with blank
            if ' ' in app_name:
                app_name = app_name.replace(' ','')
                print ("The app file name is having spaces, hence replaced the white spaces with blank in the file name:%s"%app_name)                                                      
            response = requests.post('https://saucelabs.com/rest/v1/storage/%s/%s?overwrite=true'%(USERNAME,app_name),headers=headers,data=data,auth=(USERNAME,PASSWORD))
            if response.status_code == 200:
                result_flag=True
                print ("App successfully uploaded to sauce storage")
        except Exception as e:
            print (str(e))     

        return result_flag

    def browser_stack_upload(self,app_name,app_path):
        "Upload the apk to the BrowserStack storage if its not done earlier"
        USERNAME = remote_credentials.USERNAME
        ACESS_KEY = remote_credentials.ACCESS_KEY        
        try:                                               
            #Upload the apk                         
            apk_file = os.path.join(app_path,app_name)                                
            files = {'file': open(apk_file,'rb')}
            post_response = requests.post("https://api.browserstack.com/app-automate/upload",files=files,auth=(USERNAME,ACESS_KEY))
            post_json_data = json.loads(post_response.text)
            #Get the app url of the newly uploaded apk
            app_url = post_json_data['app_url']            
        except Exception as e:
            print(str(e))
            
        return app_url
                

    def get_firefox_driver(self):
        "Return the Firefox driver"
        driver = webdriver.Firefox(firefox_profile=self.get_firefox_profile())

        return driver 


    def get_firefox_profile(self):
        "Return a firefox profile"

        return self.set_firefox_profile()

    
    def set_firefox_profile(self):
        "Setup firefox with the right preferences and return a profile"
        try:
            self.download_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),'..','downloads'))
            if not os.path.exists(self.download_dir):
                os.makedirs(self.download_dir)
        except Exception as e:
            print("Exception when trying to set directory structure")
            print(str(e))
            
        profile = webdriver.firefox.firefox_profile.FirefoxProfile()
        set_pref = profile.set_preference
        set_pref('browser.download.folderList', 2)
        set_pref('browser.download.dir', self.download_dir)
        set_pref('browser.download.useDownloadDir', True)
        set_pref('browser.helperApps.alwaysAsk.force', False)
        set_pref('browser.helperApps.neverAsk.openFile', 'text/csv,application/octet-stream,application/pdf')
        set_pref('browser.helperApps.neverAsk.saveToDisk', 'text/csv,application/vnd.ms-excel,application/pdf,application/csv,application/octet-stream')
        set_pref('plugin.disable_full_page_plugin_for_types', 'application/pdf')
        set_pref('pdfjs.disabled',True)

        return profile