"""
   @Author      : CT
   @Description : 信息库月度整合脚本
                  - 从 dwt_flow_asin 读取当月符合条件的ASIN及详情字段
                  - 过滤禁用分类，筛选信息库候选ASIN（月销>=50，asin_type in (0,1)）
                  - is_new_flag / is_ascending_flag：已废弃，不再计算，Hive里固定写NULL（保留字段位避免破坏历史表结构）
                  - 关联ODS评论（ods_asin_detail）
                  - Hive dwt_ai_asin 备份；PG表 {site}_ai_asin_detail 且只导出增量：跟 Doris selection.{site}_ai_asin_analyze_detail 反连接，
                    已经在里面（已完成AI分析）的不用再导出
"""
import os
import sys

sys.path.append(os.path.dirname(sys.path[0]))

from pyspark.sql import functions as F
from pyspark.sql.window import Window
from pyspark.storagelevel import StorageLevel
from utils.spark_util import SparkUtil
from utils.DorisHelper import DorisHelper
from utils.common_util import CommonUtil
from utils.hdfs_utils import HdfsUtils
from utils.db_util import DBUtil


class DwtAiAsin(object):

    def __init__(self, site_name="us", date_type="month", date_info="2024-10"):
        self.site_name = site_name
        self.date_type = date_type
        self.date_info = date_info
        app_name = f"{self.__class__.__name__}:{site_name}:{date_type}:{date_info}"
        self.spark = SparkUtil.get_spark_session(app_name)

        self.df_base_asin = self.spark.sql("select 1+1")
        self.df_ods_detail = self.spark.sql("select 1+1")
        self.df_save      = self.spark.sql("select 1+1")

    def run(self):
        self.read_data()
        self.handle_data()
        self.save_data()

    def read_data(self):
        # ── 1. 当月流量选品──
        sql1 = f"""
            SELECT
                asin,
                asin_weight                                     AS weight,
                asin_bought_month                               AS bought_month,
                asin_category_desc                              AS category,
                asin_img_url                                    AS img,
                asin_title                                      AS title,
                asin_brand_name                                 AS brand,
                account_name,
                account_id                                      AS seller_id,
                seller_country_name                              AS fb_country_name,
                asin_buy_box_seller_type                        AS buy_box_seller_type,
                asin_launch_time                                AS launch_time,
                asin_img_num                                    AS img_num,
                CASE WHEN variation_num > 0 THEN 1 ELSE 0 END  AS variation_flag,
                variation_num,
                asin_ao_val                                     AS ao_val,
                category_first_id,
                category_id                                     AS category_current_id,
                parent_asin,
                first_category_rank                             AS bsr_rank,
                asin_price                                      AS price,
                asin_rating                                     AS rating,
                asin_total_comments                             AS total_comments,
                asin_describe                                   AS describe,
                asin_bought_mom                                 AS bought_month_mom,
                asin_bought_yoy                                 AS bought_month_yoy
            FROM dwt_flow_asin
            WHERE site_name        = '{self.site_name}'
              AND date_type        = '{self.date_type}'
              AND date_info        = '{self.date_info}'
              AND asin_type        IN (0, 1)
              AND asin_bought_month >= 50
        """
        self.df_base_asin = self.spark.sql(sql1).repartition(40, 'asin')

        # 禁用分类过滤（category_id + category 双重）：category_disable_config是多模块共用配置表，
        # 必须按module过滤只取信息库自己的配置，不然会连流量选品等其他模块的禁用分类一起过滤掉
        conn_info = DBUtil.get_connection_info("mysql", "us")
        sql_cat_id = f"""
            SELECT DISTINCT category_id FROM category_full_name a
            WHERE EXISTS (
                SELECT 1 FROM category_disable_config b
                WHERE b.id_path IS NOT NULL
                  AND a.id_path LIKE CONCAT(b.id_path, '%')
                  AND a.site = b.site
                  AND b.module = '信息库:名称前缀筛选'
            ) AND a.site = '{self.site_name}'
        """
        df_filter_cat_id = SparkUtil.read_jdbc_query(
            self.spark, conn_info["url"], conn_info["pwd"], conn_info["username"], sql_cat_id
        )

        sql_cat_desc = f"""
            SELECT DISTINCT name_path AS category FROM category_disable_config
            WHERE site = '{self.site_name}' AND module = '信息库:名称前缀筛选'
        """
        df_filter_cat_desc = SparkUtil.read_jdbc_query(
            self.spark, conn_info["url"], conn_info["pwd"], conn_info["username"], sql_cat_desc
        )

        self.df_base_asin = self.df_base_asin \
            .join(df_filter_cat_id, self.df_base_asin['category_current_id'] == df_filter_cat_id['category_id'], 'left_anti') \
            .join(df_filter_cat_desc, 'category', 'left_anti') \
            .repartition(40, 'asin') \
            .persist(StorageLevel.DISK_ONLY)
        print(f"当月信息库ASIN：{self.df_base_asin.count()}")

        # ── 2. ODS 评论/产品详情（取 updated_at 最新一条）──
        sql_ods = f"""
            SELECT asin, review_json_list, product_json, product_detail_json, updated_at
            FROM ods_asin_detail
            WHERE site_name = '{self.site_name}'
              AND date_type = '{self.date_type}'
              AND date_info = '{self.date_info}'
        """
        window_ods = Window.partitionBy('asin').orderBy(F.col('updated_at').desc_nulls_last())
        self.df_ods_detail = self.spark.sql(sql_ods) \
            .withColumn('rn', F.row_number().over(window_ods)) \
            .filter('rn = 1').drop('rn', 'updated_at') \
            .repartition(40, 'asin').persist(StorageLevel.DISK_ONLY)

    def handle_data(self):
        # 汇总关联
        self.df_save = self.df_base_asin \
            .join(self.df_ods_detail, 'asin', 'left')

    def save_data(self):
        # 字段对齐 us_ai_asin_detail
        self.df_save = self.df_save.select(
            F.col('asin'),
            F.col('weight'),
            F.col('bought_month'),
            F.col('category'),
            F.col('img'),
            F.col('title'),
            F.col('brand'),
            F.col('account_name'),
            F.lit(None).alias('account_addr'),   # 废弃字段
            F.col('buy_box_seller_type'),
            F.col('launch_time'),
            F.col('img_num'),
            F.col('variation_flag'),
            F.col('variation_num'),
            F.col('ao_val'),
            F.col('category_first_id'),
            F.col('category_current_id'),
            F.col('parent_asin'),
            F.col('bsr_rank'),
            F.col('price'),
            F.col('rating'),
            F.col('total_comments'),
            F.col('seller_id'),
            F.col('fb_country_name'),
            F.col('review_json_list'),
            F.lit(None).alias('launch_time_type'),  # 废弃字段
            F.col('describe'),
            F.col('product_json'),
            F.col('product_detail_json'),
            F.col('bought_month_mom'),
            F.col('bought_month_yoy'),
            F.lit(None).alias('is_new_flag'),        # 废弃字段
            F.lit(None).alias('is_ascending_flag'),  # 同上
            F.lit(0).alias('chat_flag'),
            # 分区字段必须在最后
            F.lit(self.site_name).alias('site_name'),
            F.lit(self.date_type).alias('date_type'),
            F.lit(self.date_info).alias('date_info'),
        ).repartition(10).persist(StorageLevel.DISK_ONLY)
        print(f"最终输出ASIN数量：{self.df_save.count()}")

        # ── Hive 写入（备份，含分区字段）──
        hive_tb   = "dwt_ai_asin"
        hdfs_path = CommonUtil.build_hdfs_path(hive_tb, partition_dict={
            "site_name": self.site_name,
            "date_type": self.date_type,
            "date_info": self.date_info,
        })
        HdfsUtils.delete_file_in_folder(hdfs_path)
        print(f"写入 Hive {hive_tb}，路径：{hdfs_path}")
        self.df_save.write.saveAsTable(
            name=hive_tb, format='hive', mode='append',
            partitionBy=['site_name', 'date_type', 'date_info']
        )

        # ── PG 写入 {site}_ai_asin_detail，只导出增量：不在 Doris selection.{site}_ai_asin_analyze_detail 里的才需要──
        sql_analyzed = f"SELECT asin FROM `selection`.`{self.site_name}_ai_asin_analyze_detail`"
        df_analyzed_asin = DorisHelper.spark_import_with_sql(self.spark, sql_analyzed).repartition(40, 'asin')
        # persist：下面先count()校验数量、再write()真正落库，两次action共用同一份物化结果，
        # 不然anti join(含一次Doris JDBC读取)会被重新算一遍
        df_to_export = self.df_save.drop(
            'date_type', 'date_info', 'account_addr', 'launch_time_type', 'is_new_flag', 'is_ascending_flag'
        ).join(df_analyzed_asin, 'asin', 'left_anti') \
            .persist(StorageLevel.DISK_ONLY)
        export_count = df_to_export.count()
        print(f"待导出PG的增量ASIN数量：{export_count}")

        pg_con = DBUtil.get_connection_info("postgresql", "us")
        export_pg_tb = f"{self.site_name}_ai_asin_detail"
        try:
            if export_count == 0:
                print("没有需要导出到PG的增量ASIN，跳过写入")
                return
            df_to_export.write.format("jdbc") \
                .option("url", pg_con["url"]) \
                .option("dbtable", export_pg_tb) \
                .option("user", pg_con["username"]) \
                .option("password", pg_con["pwd"]) \
                .mode("append") \
                .save()
            print(f"PG {export_pg_tb} 增量写入完成")
            CommonUtil.send_wx_msg(['chenyuanjie', 'tanbin'], 'ASIN信息库月度数据写入PG成功',
                                    f'详情：{export_pg_tb} {self.site_name} {self.date_type} {self.date_info}')
        except Exception as e:
            print("An error occurred while writing to PG:", str(e))
            CommonUtil.send_wx_msg(['chenyuanjie'], '⚠ ASIN信息库月度数据写入PG失败',
                                    f'详情：{export_pg_tb} {self.site_name} {self.date_type} {self.date_info}')


if __name__ == "__main__":
    site_name = sys.argv[1]
    date_type = sys.argv[2]
    date_info = sys.argv[3]
    handle_obj = DwtAiAsin(site_name=site_name, date_type=date_type, date_info=date_info)
    handle_obj.run()
