import os
import sys
import django
import requests
import time
import logging
import lazy_logger

from django.conf import settings

from collections import OrderedDict

from bs4 import BeautifulSoup

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.wait import WebDriverWait

from pyvirtualdisplay import Display


logger = logging.getLogger(__name__)


if not settings.configured:
    """setting the Django env()"""
    logger.debug('settings.configured: {0}'.format(settings.configured))
    sys.path.append(os.path.dirname(os.getcwd()))
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'radar.settings.local')
    django.setup() #settings.configure(default_settings, **options)
    logger.debug('settings.configured: {0}'.format(settings.configured))


class Crawlers(object):
    """
    Creates a new instance of the Service
    please run 'cntlm -v' if execute Crawler before. 
    
    :Args:
     - driver_path : Path to PhantomJS binary
     - log_path : Path to PhantomJS's log
     - executable_path : Path to PhantomJS binary
     - port : Port the service is running on 
     - service_args : A List of other command line options to pass to PhantomJS
    """

    def __init__(self, account, password, url, category, download_path, log_path, virtual=False, *args, **kwargs):
        super(Crawlers, self).__init__()
        self.args = args
        self.kwargs = kwargs
        self.BASE_DIR = settings.BASE_DIR
        self.category = str(category).upper()
        self.download_path = download_path
        self.download_path_temp = os.path.join(self.download_path, category)
        self.log_path = log_path
        self.virtual = virtual
        self.account = account
        self.password = password
        self.url = url
        self.version = ''
        
        lazy_logger.log_to_console(logger)
        lazy_logger.log_to_rotated_file(logger=logger, file_name=self.log_path)

    @property
    def driver(self):
        return self._driver

    @property
    def _driver(self):
        if self.virtual:
            self.display = Display(visible=0, size=(1024, 786))
            self.display.start()
        
        service_args = [
        '--webdriver-loglevel=ERROR'  # only record ERROR message
        '--proxy=127.0.0.1:3128',
        ]
        
        # setting Chrome option
        os.environ['webdriver.chrome.driver'] = self.driver_path
        prefs = {'download.default_directory': self.download_path_temp}
        proxy = '127.0.0.1:3128'
        
        chrome_options = Options()
        chrome_options.add_experimental_option('prefs', prefs)
        chrome_options.add_argument('--proxy-server=http://%s' % proxy)

        driver = webdriver.Chrome(
            executable_path=self.driver_path, # 設定 dirver 路徑
            service_log_path=self.logs_path, # 設定 log 路徑位置
            chrome_options=chrome_options, # 設定下載路徑等等
            #service_args=service_args,
        )
        return driver

    @property
    def logs_path(self):
        logs_path = os.path.join(self.BASE_DIR, 'logs', 'ghostdriver.log')
        if not os.path.isdir(os.path.dirname(logs_path)): os.mkdir(os.path.dirname(logs_path))
        return self.kwargs.get('logs_path', logs_path)

    @property
    def driver_path(self):
        def_driver_path = os.path.join(self.BASE_DIR, 'nimbus', 'selenium-2.27', 'mac', 'chromedriver')
        driver_path = self.kwargs.get('driver_path', def_driver_path)
        
        if not os.path.exists(driver_path):
            raise Exception(driver_path + ' Not Exist')
        return driver_path

    @property
    def _nimbus_login(self):
        """中文說明
        這個爬蟲一定要先透過認證登入，但因為都在 matrix 頁面處理東西
        所以這個方法是運作到進入 matrix 頁面接著回傳 object。
        """
        driver = self.driver
        wait = WebDriverWait(driver, 3600)

        try:
            logger.info('login APPLE Nimbus Authenticate System.')
            driver.get('https://nimbus.apple.com/')
            wait.until(
                lambda driver: driver.find_element_by_id('accountname'),
                'FAIL to find account.'
            )
            driver.find_element_by_id('accountname').send_keys(self.account)
            driver.find_element_by_id("accountpassword").send_keys(self.password)
            driver.find_element_by_id("continueFieldbutton").click()
            time.sleep(5)

            logger.info('switch to Nimbus Build Matrix page.')
            driver.get(self.url)
        except Exception as e:
            raise e

        try:
            c = 0
            while bool(driver.find_element_by_id('project-list')):
                c += 1
                logger.info('try again, connect to {0}, time:{1}.'.format(self.url, c))
                driver.get(self.url)
                time.sleep(5)
        except Exception as e:
            logger.info('success connect to Nimbus Build Matrix page.')
        return driver

    @property
    def get_version(self):
        """中文說明
        取得 Build Matrix 版本
        """
        driver = self._nimbus_login
        wait = WebDriverWait(driver, 3600)
        try:
            self.version = self.logged_status(driver=driver, wait=wait)
        except Exception as e:
            raise e
        finally:
            driver.close()
            if self.virtual: self.display.stop()
        return self.version
    
    def logged_status(self, driver, wait):
        logger.info('waitting for logged status.')
        wait.until(
            lambda driver: driver.find_element_by_xpath(
                "(//div[@id='topnav']/ul[@id='user-box'])"
            ), 
            "Find the element user_box fail."
        )
        time.sleep(10)
        
        logger.info('get version.')
        request_count = 0
        check_level = 0
        version = ''
        while 1:
            content = driver.find_element_by_xpath(
                "//div[@id='topnav']/ul[@id='user-box']"
            ).text
            content = content.split('\n')
            logger.info('status: {0}.'.format(repr(content[1])))
            
            if content[1] == u"What's New": #帳號列出怪怪字元
                return 255

            content = driver.find_element_by_xpath(  #BM 版本號
                "//div[@id='build-controller-container']"
                "/div[@id='sheet-actions-container']/ul[@id='sheet-tabs']"
            ).text
            logger.info('status: {0}.'.format(repr(content)))
            
            if content == '':
                logger.info('version status NULL, Retry.')
            elif content.split('\n')[0] == 'Current':
                logger.info('versin empty, Retry')
            else:
                check_level = 0
                if request_count == 0:   #TODO確認版本
                    driver.find_element_by_xpath(
                        "//div[@id='build-controller-container']/div[@id='sheet-actions-container']"
                        "/ul[@id='sheet-tabs']"
                    ).click()
                    logger.info('switch to history version.')
                elif request_count > 0:
                    content = content.split('\n')
                    content = str(content[0]).split('-')
                    content = str(content[0]).split('(')
                    content = str(content[1]).split(')')
                    logger.info('version: {0}.'.format(content[0]))
                    if request_count == 2:
                        version = content[0] 
                        return version
                request_count += 1
            time.sleep(10)
            if check_level == 4:
                version = None
                return version
            check_level += 1
        version = None
        return veriosn
    
    def download_matrix(self, version):
        """中文說明
        下載 Build Matrix 檔案
        流程：
            1. 確認資料夾是否為空（清除）
            2. 下載
            3. 完成下載確認是否有檔案
            4. 關閉瀏覽器
        回傳值一定要是一個 OrderedDict()
        """
        try:
            self.remove_file()  # clean .xlsx file.
        except Exception as e:
            raise e

        driver = self._nimbus_login
        wait = WebDriverWait(driver, 60)

        try:
            self.version = self.logged_status(driver=driver, wait=wait)  #先執行前置作業，確認版本，沒變跳出。
            if self.version == version:
                logger.info('version not change')
                ret = OrderedDict((('ret', 255), ('status', 'version not change.'), ('version', self.version)))
                driver.close()
                driver.quit()
                if self.virtual: self.display.stop()
                return ret
            if self.version == None:
                logger.info('version is None, maybe Build matrix not online.')
                ret = OrderedDict((('ret', 255), ('status', 'version is None, maybe Build matrix not online.'), ('version', self.version)))
                driver.close()
                driver.quit()
                if self.virtual: self.display.stop()
                return ret
        except Exception as e:
            raise e

        logger.info('check export button.')
        wait.until(
            lambda driver: driver.find_element_by_xpath(
                "//div[@id='sheet-actions-container']"
                "/ul[@id='sheet-actions']"
            ),
            'Find export button fail.'
        )
        try:
            while 1:
                logger.info('click export button.')
                driver.find_element_by_xpath(
                    "//div[@id='sheet-actions-container']/ul[@id='sheet-actions']"
                ).click()
                time.sleep(3)
                driver.find_element_by_link_text('Export').click()
                
                logger.info('check download button.') #有時再轉出會出現錯誤，去偵測這個錯誤並且重新開始下載（設定 60s）原因
                content = wait.until(
                    lambda driver: driver.find_element_by_xpath(
                        "//button[@class='btn ng-binding btn-primary']"
                    ),
                    'click download and export button pass.'
                )
                if bool(content) == True:
                    time.sleep(5)
                    driver.find_element_by_xpath(
                        "//button[@class='btn ng-binding btn-primary']"
                    ).click()
                    logger.info('retry click export button.')
        except Exception as e:
            time.sleep(5) #稍微 delay 再下載
            logger.info('continue to start download.')

        try:
            wait.until(
                lambda driver: driver.find_element_by_xpath(
                    "//button[@class='btn btn-primary btn-lg btn-block']"
                ),
                'find download button fail.'
            )

            time.sleep(10) #稍微 delay 再點選
            logger.info('click the download button.')
            driver.find_element_by_xpath(
                "//button[@class='btn btn-primary btn-lg btn-block']"
            ).click()
            
            time.sleep(5)
            download_count = 0
            while self.download_exist() == None:
                download_count += 1
                logger.info('waitting download...{0}'.format(download_count))
                time.sleep(5)
                if download_count >= 10:
                    raise ValueError('download too long, fail.')
        except Exception as e:
            raise e
        finally:
            driver.close()
            driver.quit()
            if self.virtual: self.display.stop()

        logger.info('success downolad.')
        ret = OrderedDict((('ret', 0), ('status', 'success downolad.'), ('version', self.version)))
        return ret

    def download_exist(self, ext='.xlsx'):
        template = self.download_path_temp
        for f in os.listdir(template):
            extension = os.path.splitext(f)
            if extension[1] == ext:
                logger.info('file: {0}.'.format(os.path.join(template, f)))
                return f
            elif extension[1] == ext +', inline':
                os.rename(os.path.join(template, f), os.path.join(template, f.replace(', inline', '')))
                logger.info('file: {0}.'.format(os.path.join(template, f.replace(', inline', ''))))
                return f.replace(', inline','')
        return None

    def move_file(self):
        pass

    def remove_file(self, ext='.xlsx'):
        template = self.download_path_temp
        for f in os.listdir(template):
            extension = os.path.splitext(f)
            if extension[1] == ext:
                logger.info('clean: {0}.'.format(f))
                os.remove(os.path.join(template, f))
        return 'clean ok.'

    @property
    def msg(self):
        logger.info('sendding msg now')

    def __del__(self):
        pass

