import os
from playwright.sync_api import sync_playwright
from secure_db_client import get_remote_engine
import pandas as pd
import urllib.parse
import json
import traceback
import time
'商机探测器。下载bsr分类数据'


class One688LoginSpider(object):
    def __init__(self, site='us'):
        self.site = site
        self.data = None
        self.post_url = '/ox-api/graphql'
        month = time.strftime("%m")
        yaer = time.strftime('%Y', time.localtime(time.time()))
        self.y_w = f"{yaer}-{month}"
        self.mysql_connect()

    def mysql_connect(self):
        self.engine_us_mysql = get_remote_engine(
            site_name='us',  # -> database "selection"
            db_type='mysql',  # -> 服务端 alias "mysql"
        )
        self.engine_pg = get_remote_engine(
            site_name=self.site,  # -> database "selection"
            db_type='postgresql_15_outer',  # -> 服务端 alias "mysql"
        )
        return self.engine_us_mysql

    def print_request_finished(self, request):
        # 拦截请求获取数据，只接受 data.niches 形态的 graphql 响应，避免被同 URL 的其他响应覆盖
        if self.post_url in request.url:
            resp = request.response()
            if resp:
                try:
                    body = resp.json()
                    if isinstance(body, dict) and isinstance(body.get('data'), dict) and 'niches' in body['data']:
                        self.data = body
                        print(self.data)
                except Exception as e:
                    print('拦截url报错：', e, f"\n{traceback.format_exc()}")

    def select_category_json(self):
        sql = 'SELECT category_json,id FROM seller_product_opportunity_syn where state=1'
        engine_mysql = self.mysql_connect()
        df_category_json = engine_mysql.read_sql(sql)
        category_data_list = list(df_category_json['category_json'] + '|=|=|' + df_category_json['id'].astype("U"))
        data_list = []
        for i in category_data_list:
            data = i.split('|=|=|')
            data_list.append(data)
        return data_list

    def get_category(self):
        data_list = self.select_category_json()
        for data in data_list:
            item = json.loads(data[0])
            print('请求分类：', item)
            category_name = item['browseNodeName']
            item_list = []
            item_list.append(item)
            for i in range(3):
                try:
                    base_url = "https://sellercentral.amazon.com/opportunity-explorer/search?mons_redirect=stck_reroute&browsenodes="
                    json_data_item = json.dumps(item_list)
                    encoded_data = urllib.parse.quote(json_data_item)
                    full_url = f"{base_url}{encoded_data}"
                    print('分类url：', full_url)
                    self.post_url = '/ox-api/graphql'
                    self.crawl(full_url)
                    if self.data:
                        self.mysql_connect()
                        category_data_list = []
                        for i in self.data['data']['niches']:
                            i_json_data = json.dumps(i)
                            print('json_data:::', i_json_data)
                            niche_title = i['nicheTitle']
                            product_count = i['nicheSummary']['productCount']
                            search_volume_t360 = i['nicheSummary']['searchVolumeT360']
                            search_volume_t90 = i['nicheSummary']['searchVolumeT90']
                            search_volume_growth_t180 = i['nicheSummary']['searchVolumeGrowthT180']
                            search_volume_growth_t90 = i['nicheSummary']['searchVolumeGrowthT90']
                            minimum_units_sold_t360 = i['nicheSummary']['minimumUnitsSoldT360']
                            maximum_units_sold_t360 = i['nicheSummary']['maximumUnitsSoldT360']
                            minimum_average_units_sold_t360 = i['nicheSummary']['minimumAverageUnitsSoldT360']
                            maximum_average_units_sold_t360 = i['nicheSummary']['maximumAverageUnitsSoldT360']
                            minimum_price = i['nicheSummary']['minimumPrice']
                            maximum_price = i['nicheSummary']['maximumPrice']
                            avg_price = i['nicheSummary']['avgPrice']
                            return_rate_t360 = i['nicheSummary']['returnRateT360']
                            search_volume_growth_t360 = i['nicheSummary']['searchVolumeGrowthT360']
                            searchTerm_list = []
                            for i in i['topSearchTermMetrics']:
                                searchTerm_list.append(i['searchTerm'])
                            if searchTerm_list:
                                popular_search = ' && '.join(searchTerm_list)
                            else:
                                popular_search = None
                            category_data_list.append(
                                [category_name, i_json_data, niche_title, product_count, search_volume_t360,
                                 search_volume_t90, search_volume_growth_t180, search_volume_growth_t90,
                                 minimum_units_sold_t360, maximum_units_sold_t360, minimum_average_units_sold_t360,
                                 maximum_average_units_sold_t360, minimum_price, maximum_price,
                                 avg_price, return_rate_t360, search_volume_growth_t360, self.site, self.y_w,
                                 popular_search])

                        df_category_data = pd.DataFrame(data=category_data_list,
                                                        columns=['category_name', 'category_data_json', 'niche_title',
                                                                 'product_count',
                                                                 'search_volume_t360', 'search_volume_t90',
                                                                 'search_volume_growth_t180',
                                                                 'search_volume_growth_t90', 'minimum_units_sold_t360',
                                                                 'maximum_units_sold_t360',
                                                                 'minimum_average_units_sold_t360',
                                                                 'maximum_average_units_sold_t360',
                                                                 'minimum_price', 'maximum_price', 'avg_price',
                                                                 'return_rate_t360', 'search_volume_growth_t360',
                                                                 'site', 'date_info', 'search_term'])
                        self.engine_pg.to_sql(df_category_data,'seller_product_opportunity', if_exists='append')
                        print('存储成功：', len(category_data_list))
                        with self.engine_us_mysql.begin() as conn:
                            sql_update = f"update seller_product_opportunity_syn set state=3 where id={int(data[1])};"
                            print('修改状态：', sql_update)
                            conn.execute(sql_update)
                        self.data = None
                    break
                except Exception as e:
                    print("并等待5s继续", e, f"\n{traceback.format_exc()}")
                    time.sleep(10)
                    print('报错重试')
            time.sleep(10)

        self.save_workflow_everyday()

    def save_workflow_everyday(self):
        self.mysql_connect()
        workflow_everyday_list = [
            [self.site, self.y_w, '商机探测器抓取完成', 3, 'seller_product_opportunity', 'month',
             '商机探测器', '是']]
        df_seller_asin_account = pd.DataFrame(data=workflow_everyday_list,
                                              columns=['site_name', 'report_date', 'status', 'status_val',
                                                       'table_name', 'date_type', 'page', 'is_end'])
        self.engine_us_mysql.to_sql(df_seller_asin_account,'workflow_everyday',  if_exists='append'
                                      )

    def crawl(self, url):
        # 请求 指定的 URL（监听器在 main 里只注册一次，避免重复绑定累积）
        # goto 前清空旧响应，避免被上一次的数据污染
        self.data = None
        self.page.goto(url)
        # 轮询等待 niches 响应到达，最多 30 秒；拿到立即返回，不再固定 sleep 25 秒
        end_time = time.time() + 30
        while time.time() < end_time:
            if self.data is not None:
                return
            self.page.wait_for_timeout(500)
        print('等待 niches 响应超时')

    def run(self):
        self.page.goto('https://sellercentral.amazon.com/gp/homepage.html/ref=xx_home_logo_xx?')
        self.page.wait_for_timeout(20000)
        # # 开始持续拦截请求
        self.get_category()

    def main(self):
        # 启动前杀掉已有 Edge 进程，防止 user_data_dir 被锁
        os.system("taskkill /F /IM msedge.exe")
        time.sleep(2)
        # 初始化
        with sync_playwright() as _playwright:
            browser = _playwright.chromium.launch_persistent_context(
                # 保持免登录 profile（参考 dow_category_edge.py:71）
                user_data_dir=r"C:\Users\FLA主账号客服维权使用\AppData\Local\Microsoft\Edge\User Data",
                # 通过 channel 指定使用 Edge，无需写死 exe 路径
                channel="msedge",
                accept_downloads=True,
                headless=False,
                bypass_csp=True,
                locale='en-GB',
                ignore_https_errors=True,
                no_viewport=True,
                slow_mo=10,
                args=[
                    # 跳过自动化检测
                    '--disable-blink-features=AutomationControlled',
                    # 选 Default profile（与 user_data_dir 配合）
                    '--profile-directory=Default',
                    # Windows 下移除 Linux 参数，减少启动崩概率
                    '--disable-gpu',
                    # 降低"首次运行/恢复弹窗/扩展"对启动的干扰
                    '--no-first-run',
                    '--no-default-browser-check',
                    '--disable-extensions',
                    '--disable-notifications',
                    # 避免 DevToolsActivePort 问题（让系统随机分配端口）
                    '--remote-debugging-port=0',
                    # 保留后台节流相关（一般没问题）
                    '--disable-backgrounding-occluded-windows',
                    '--disable-renderer-backgrounding',
                    '--disable-background-timer-throttling',
                    '--disable-features=CalculateNativeWinOcclusion',
                    '--remote-allow-origins=*',
                ]
            )

            self.page = browser.new_page()
            # 拦截 graphql 响应：监听器只注册一次（防止累积）
            self.page.on("requestfinished", self.print_request_finished)
            js = """
                    Object.defineProperties(navigator, {webdriver:{get:()=>undefined}});
                    """
            self.page.add_init_script(js)
            self.page.evaluate_handle('''() =>{ window.chrome = { runtime: {}, }; }''')
            self.page.evaluate_handle(
                '''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
            # 模拟浏览器参数
            self.page.locator("body").click()
            js = """
                    Object.defineProperties(navigator, {webdriver:{get:()=>undefined}});
                    """
            self.page.add_init_script(js)

            self.run()
            self.page.close()


if __name__ == '__main__':
    One688 = One688LoginSpider()
    One688.main()
