import platform
import subprocess
import time
import os
import json
import uuid
import requests
import hashlib
import traceback
from typing import Callable, List, Tuple

from DrissionPage import Chromium
from DrissionPage.items import MixTab
from DrissionPage.errors import ElementNotFoundError

from .const import BROWSER_PATH, DRIVER_PATH, SOCKET_PORT, logger

class SuperBrowserBridge:
    def __init__(self, user_info: dict):
        self.user_info = user_info
        self.browser_list: list = []
        self.driver: Chromium = None
        self.tab: MixTab = None
        self._is_windows: bool = platform.system() == 'Windows'
        self._is_mac: bool = platform.system() == 'Darwin'
        self._opt_id: int = 0
        self._opts: List[Tuple[int, Callable]] = []

    def add_opt(self, opt: Callable):
        """添加自动化操作"""
        self._opt_id += 1
        self._opts.append((self._opt_id, opt))

    def encrypt_sha1(self, fpath: str) -> str:
        with open(fpath, 'rb') as f:
            return hashlib.new('sha1', f.read()).hexdigest()
        
    def download_file(self, url, save_path):
        # 发送GET请求获取文件内容
        response = requests.get(url, stream=True)
        # 检查请求是否成功
        if response.status_code == 200:
            # 创建一个本地文件并写入下载的内容（如果文件已存在，将被覆盖）
            with open(save_path, 'wb') as f:
                for chunk in response.iter_content(chunk_size=1024):
                    if chunk:
                        f.write(chunk)
            logger.info(f"文件已成功下载并保存到：{save_path}")
        else:
            logger.error(f"下载失败，响应状态码为：{response.status_code}")

    def download_driver(self):
        if self._is_windows:
            config_url = "https://cdn-superbrowser-attachment.ziniao.com/webdriver/exe_32/config.json"
        elif self._is_mac:
            arch = platform.machine()
            if arch == 'x86_64':
                config_url = "https://cdn-superbrowser-attachment.ziniao.com/webdriver/mac/x64/config.json"
            elif arch == 'arm64':
                config_url = "https://cdn-superbrowser-attachment.ziniao.com/webdriver/mac/arm64/config.json"
            else:
                return
        else:
            return
        response = requests.get(config_url)
        # 检查请求是否成功
        if response.status_code == 200:
            # 获取文本内容
            txt_content = response.text
            config = json.loads(txt_content)
        else:
            logger.error(f"下载驱动失败，状态码：{response.status_code}")
            exit()
        if not os.path.exists(DRIVER_PATH):
            os.makedirs(DRIVER_PATH)

        # 获取文件夹中所有chromedriver文件
        driver_list = [filename for filename in os.listdir(DRIVER_PATH) if filename.startswith('chromedriver')]

        for item in config:
            filename = item['name']
            if self._is_windows:
                filename = filename + ".exe"
            local_file_path = os.path.join(DRIVER_PATH, filename)
            if filename in driver_list:
                # 判断sha1是否一致
                file_sha1 = self.encrypt_sha1(local_file_path)
                if file_sha1 == item['sha1']:
                    logger.info(f"驱动{filename}已存在，sha1校验通过...")
                else:
                    logger.info(f"驱动{filename}的sha1不一致，重新下载...")
                    self.download_file(item['url'], local_file_path)
                    # mac首次下载修改文件权限
                    if self._is_mac:
                        cmd = ['chmod', '+x', local_file_path]
                        subprocess.Popen(cmd)
            else:
                logger.info(f"驱动{filename}不存在，开始下载...")
                self.download_file(item['url'], local_file_path)
                # mac首次下载修改文件权限
                if self._is_mac:
                    cmd = ['chmod', '+x', local_file_path]
                    subprocess.Popen(cmd)

    def exit(self):
        """关闭客户端"""
        data = {"action": "exit", "requestId": str(uuid.uuid4())}
        data.update(self.user_info)
        logger.info('browser exit ...' + json.dumps(data, ensure_ascii=False))
        self.send_http(data)

    def update_core(self):
        """
        下载所有内核，打开店铺前调用，需客户端版本5.285.7以上
        因为http有超时时间，所以这个action适合循环调用，直到返回成功
        """
        data = {
            "action": "updataCore",
            "requestId": str(uuid.uuid4()),
        }
        data.update(self.user_info)
        while True:
            result = self.send_http(data)
            logger.info(result)
            if result is None:
                logger.info("等待客户端启动...")
                time.sleep(2)
                continue
            if result.get("statusCode") is None or result.get("statusCode") == -10003:
                logger.info("当前版本不支持此接口，请升级客户端")
                return
            elif result.get("statusCode") == 0:
                logger.info("更新内核完成")
                return
            else:
                logger.info(f"等待更新内核: {json.dumps(result)}")
                time.sleep(2)

    def send_http(self, data):
        try:
            url = 'http://127.0.0.1:{}'.format(SOCKET_PORT)
            response = requests.post(url, json.dumps(data).encode('utf-8'), timeout=120)
            return json.loads(response.text)
        except Exception as err:
            logger.error(err)

    def kill_process(self):
        if self._is_windows:
            os.system('taskkill /f /t /im SuperBrowser.exe')
        elif self._is_mac:
            os.system('killall ziniao')
            time.sleep(3)

    def start_browser(self):
        try:
            if self._is_windows:
                cmd = [BROWSER_PATH, '--run_type=web_driver', '--ipc_type=http', '--port=' + str(SOCKET_PORT)]
            elif self._is_mac:
                cmd = ['open', '-a', BROWSER_PATH, '--args', '--run_type=web_driver', '--ipc_type=http',
                    '--port=' + str(SOCKET_PORT)]
            else:
                logger.warning('platform not supported')
                exit()
            subprocess.Popen(cmd)
            time.sleep(5)
        except Exception as e:
            logger.error('start browser process failed')
        logger.info('browser launch')
    
    def get_browser_list(self) -> list:
        request_id = str(uuid.uuid4())
        data = {
            "action": "getBrowserList",
            "requestId": request_id
        }
        data.update(self.user_info)

        r = self.send_http(data)
        if str(r.get("statusCode")) == "0":
            logger.debug(r)
            return r.get("browserList")
        elif str(r.get("statusCode")) == "-10003":
            logger.error(f"login Err {json.dumps(r, ensure_ascii=False)}")
            exit()
        else:
            logger.error(f"Fail {json.dumps(r, ensure_ascii=False)} ")
            exit()

    def open_store(self, store_info, isWebDriverReadOnlyMode=0, isprivacy=0, isHeadless=0, cookieTypeSave=0, jsInfo=""):
        request_id = str(uuid.uuid4())
        data = {
            "action": "startBrowser"
            , "isWaitPluginUpdate": 0
            , "isHeadless": isHeadless
            , "requestId": request_id
            , "isWebDriverReadOnlyMode": isWebDriverReadOnlyMode
            , "cookieTypeLoad": 0
            , "cookieTypeSave": cookieTypeSave
            , "runMode": "1"
            , "isLoadUserPlugin": False
            , "pluginIdType": 1
            , "privacyMode": isprivacy
        }
        data.update(self.user_info)

        if store_info.isdigit():
            data["browserId"] = store_info
        else:
            data["browserOauth"] = store_info

        if len(str(jsInfo)) > 2:
            data["injectJsInfo"] = json.dumps(jsInfo)

        r = self.send_http(data)
        if str(r.get("statusCode")) == "0":
            return r
        elif str(r.get("statusCode")) == "-10003":
            logger.error(f"login Err {json.dumps(r, ensure_ascii=False)}")
            exit()
        else:
            logger.error(f"Fail {json.dumps(r, ensure_ascii=False)} ")
            exit()

    def close_store(self, browser_oauth):
        request_id = str(uuid.uuid4())
        data = {
            "action": "stopBrowser"
            , "requestId": request_id
            , "duplicate": 0
            , "browserOauth": browser_oauth
        }
        data.update(self.user_info)

        r = self.send_http(data)
        if str(r.get("statusCode")) == "0":
            return r
        elif str(r.get("statusCode")) == "-10003":
            logger.info(f"login Err {json.dumps(r, ensure_ascii=False)}")
            exit()
        else:
            logger.info(f"Fail {json.dumps(r, ensure_ascii=False)} ")
            exit()

    def get_driver(self, open_ret_json):
        core_type = open_ret_json.get('core_type')
        if core_type == 'Chromium' or core_type == 0:
            major = open_ret_json.get('core_version').split('.')[0]
            if self._is_windows:
                chrome_driver_path = os.path.join(DRIVER_PATH, 'chromedriver%s.exe') % major
            else:
                chrome_driver_path = os.path.join(DRIVER_PATH, 'chromedriver%s') % major
            logger.info(f"chrome_driver_path: {chrome_driver_path}")
            port = open_ret_json.get('debuggingPort')
            self.driver = Chromium(port)
            self.tab = self.driver.latest_tab
            logger.info('webdriver 初始化完毕')
        else:
            return None
        
    def _run_task(self, browser):
        """
        打开一个店铺运行脚本
        :param browser: 店铺信息
        """
        # 如果要指定店铺ID, 获取方法:登录紫鸟客户端->账号管理->选择对应的店铺账号->点击"查看账号"进入账号详情页->账号名称后面的ID即为店铺ID
        store_id = browser.get('browserOauth')
        store_name = browser.get("browserName")
        # 打开店铺
        logger.info(f"=====打开店铺：{store_name}=====")
        ret_json: dict = self.open_store(store_id)
        logger.info(ret_json)
        store_id = ret_json.get("browserOauth")
        if store_id is None:
            store_id = ret_json.get("browserId")
        # 使用驱动实例开启会话
        self.get_driver(ret_json)
        if self.driver is None:
            logger.info(f"=====关闭店铺：{store_name}=====")
            self.close_store(store_id)
            return

        # 获取ip检测页地址
        ip_check_url = ret_json.get("ipDetectionPage")
        if not ip_check_url:
            logger.error("ip检测页地址为空，请升级紫鸟浏览器到最新版")
            self.driver.quit()
            logger.info(f"=====关闭店铺：{store_name}=====")
            self.close_store(store_id)
            exit()
        # 执行脚本
        try:
            ip_usable = self.open_ip_check(ip_check_url)
            if ip_usable:
                logger.info("ip检测通过，打开店铺平台主页")
                self.tab.get(ret_json.get("launcherPage"))
                # 打开店铺平台主页后进行后续自动化操作
                for i, opt in self._opts:
                    if not callable(opt):
                        logger.error(f"{i}号任务不可调用")
                        continue
                    logger.info(f"开始执行{i}号任务")
                    try:
                        opt(self.tab, self.driver)  # 运行
                        logger.info(f"{i}号任务执行完毕")
                    except Exception as e:
                        logger.error(f"{i}号任务执行失败：{e}")
            else:
                logger.error("ip检测不通过，请检查")
        except:
            logger.error("脚本运行异常:" + traceback.format_exc())
        finally:
            self.driver.quit()
            logger.info(f"=====关闭店铺：{store_name}=====")
            self.close_store(store_id)

    def open_ip_check(self, ip_check_url):
        """
        打开ip检测页检测ip是否正常
        :param driver: driver实例
        :param ip_check_url ip检测页地址
        :return 检测结果
        """
        try:
            self.tab.get(ip_check_url)
            self.tab.ele('//button[contains(@class, "styles_btn--success")]')
            return True
        except ElementNotFoundError:
            logger.info("未找到ip检测成功元素")
            return False
        except Exception as e:
            logger.info("ip检测异常:" + traceback.format_exc())
            return False


    def init(self):
        self.download_driver()
        self.kill_process()
        self.start_browser()
        self.update_core()
        self.browser_list = self.get_browser_list()
        if not self.browser_list:
            logger.error("browser list is empty")
            exit()

    def run(self, sn: int = None):
        browsers = self.browser_list if sn is None else [self.browser_list[sn]]
        for browser in browsers:
            self._run_task(browser)
    