"""
   @Author      : hejiangming
   @Description : 店铺分类统计表
                  店铺集合(主表)恢复为 feedback 口径，不再以 dwt_flow_asin 为主表。
                  flow 只覆盖流量选品有 asin 的店铺(约 77 万)，当主表会把 feedback 全量
                  219 万店里的 2/3 挡在报表外，2025-10 起的分区就是这么变成 70 万行的。
                  本次只换数据源(ODS -> DIM)，聚合与三个占比公式沿用原逻辑不动。
   @SourceTable :
                  1.dim_fb_detail / ods_seller_account_feedback  (店铺集合，按月门控)
                  2.dim_fb_asin_info                             (店铺-ASIN 关系)
                  3.dwt_flow_asin                                (asin_is_new，按 asin 取值)
                  4.dim_cal_asin_history_detail                  (一级分类 + 市场总量分母)
                  5.dim_bsr_category_tree                        (一级分类名称)
   @SinkTable   :
                  1.dwt_fb_category_report
   @CreateTime  : 2023/07/18 17:33
   @UpdateTime  : 2026/08/17 17:59
"""

import os
import sys

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

from pyspark.sql import functions as F
from utils.common_util import CommonUtil
from utils.spark_util import SparkUtil
from utils.hdfs_utils import HdfsUtils


class DwtFbCategoryReport(object):
    def __init__(self, site_name, date_type, date_info):
        super().__init__()
        self.site_name = site_name
        self.date_type = date_type
        self.date_info = date_info
        self.hive_tb = f"dwt_fb_category_report"
        self.partition_dict = {
            "site_name": site_name,
            "date_type": date_type,
            "date_info": date_info
        }
        # 落表路径校验
        self.hdfs_path = CommonUtil.build_hdfs_path(self.hive_tb, partition_dict=self.partition_dict)

        # 创建spark_session对象相关
        app_name = f"{self.__class__.__name__}:{site_name}:{date_info}"
        self.spark = SparkUtil.get_spark_session(app_name)

        # 初始化全局df
        self.df_fb_asin_info = self.spark.sql(f"select 1+1;")   # 主表店铺 x 店铺-ASIN 关系
        self.df_flow_asin = self.spark.sql(f"select 1+1;")      # asin -> is_asin_new
        self.df_asin_history = self.spark.sql(f"select 1+1;")
        self.df_cate_name = self.spark.sql(f"select 1+1;")
        self.df_fb_cate_asin_cal = self.spark.sql(f"select 1+1;")
        self.df_fb_asin_cal = self.spark.sql(f"select 1+1;")
        self.df_bsr_asin_cal = self.spark.sql(f"select 1+1;")
        self.df_result_cal = self.spark.sql(f"select 1+1;")

    def read_data(self):
        # 旧代码：以 flow 为主表，seller 集合直接取 dwt_flow_asin.account_id
        # 换掉的原因见文件头 —— flow 只覆盖有流量选品 asin 的店铺，当主表会丢掉 2/3 的店
        # sql = f"""
        # select account_id as seller_id, asin,
        #        asin_is_new as is_asin_new,
        #        category_first_id as bsr_cate_1_id
        # from dwt_flow_asin
        # where site_name = '{self.site_name}' and date_type = '{self.date_type}'
        #   and date_info = '{self.date_info}' and account_id is not null
        # """

        # 店铺集合(主表)：2026-06 起读 dim_fb_detail，之前的月份回退读 ODS
        # 门控与取数口径对齐 dwt_fb_base_report，保证两张报表的店铺集合一致
        if self.date_info >= '2026-06':
            print("获取 dim_fb_detail（店铺集合）")
            sql = f"""
            select seller_id
            from dim_fb_detail
            where site_name = '{self.site_name}'
              and date_type = '{self.date_type}'
              and date_info = '{self.date_info}'
            """
        else:
            # length(seller_id) > 2 是脏数据过滤，与 dim_fb_detail 内部口径一致
            print("获取 ods_seller_account_feedback（店铺集合）")
            sql = f"""
            select distinct seller_id
            from ods_seller_account_feedback
            where site_name = '{self.site_name}'
              and date_type = '{self.date_type}'
              and date_info = '{self.date_info}'
              and length(seller_id) > 2
            """
        df_fb_seller = self.spark.sql(sqlQuery=sql)
        print(sql)

        # 店铺-ASIN 关系：dim_fb_asin_info 从 2026-06 起有分区，历史月读 2026-06
        # 替代原来的 ods_seller_asin_account —— 那张表读取时带 created_at <= cal_date 过滤，
        # 爬虫先删后增会刷新 created_at，跨月重跑时活跃店铺被误筛成 0 商品；dim 层已去掉该过滤
        asin_info_date = self.date_info if self.date_info >= '2026-06' else '2026-06'
        print(f"获取 dim_fb_asin_info（店铺-ASIN 关系，date_info={asin_info_date}）")
        sql = f"""
        select seller_id, asin
        from dim_fb_asin_info
        where site_name = '{self.site_name}'
          and date_type = '{self.date_type}'
          and date_info = '{asin_info_date}'
        """
        df_fb_asin = self.spark.sql(sqlQuery=sql)
        print(sql)

        # 主表店铺 left join 店铺-ASIN 关系：店铺一个不丢，没有 asin 的店铺 asin 为 null
        self.df_fb_asin_info = df_fb_seller.join(df_fb_asin, on='seller_id', how='left')
        self.df_fb_asin_info = self.df_fb_asin_info.drop_duplicates(['seller_id', 'asin']).cache()

        # asin_is_new 从 flow 按 asin 取值（不是按店铺，不影响店铺集合）
        # 用 flow 已算好的值而不是 UDF 重算，口径与 dwt_fb_base_report.fb_new_asin_num 对齐
        print("获取 dwt_flow_asin（asin_is_new）")
        sql = f"""
        select asin, asin_is_new as is_asin_new
        from dwt_flow_asin
        where site_name = '{self.site_name}'
          and date_type = '{self.date_type}'
          and date_info = '{self.date_info}'
        """
        self.df_flow_asin = self.spark.sql(sqlQuery=sql).drop_duplicates(['asin'])
        print(sql)

        # dim_cal_asin_history_detail: 两个用途
        # 1. 给上面的 seller-asin 补一级分类（asin 全历史维表，覆盖率高于 flow 当月快照）
        # 2. 按一级分类统计全量 asin 数，作为市场占比 fb_market_rate 的分母
        print("获取 dim_cal_asin_history_detail (bsr_cate_1_id + bsr_asin_num)")
        sql = f"""
            select asin, category_first_id as bsr_cate_1_id
            from dim_cal_asin_history_detail
            where site_name = '{self.site_name}'
        """
        self.df_asin_history = self.spark.sql(sqlQuery=sql).cache()
        print(sql)

        # 一级分类名称
        print("获取 dim_bsr_category_tree")
        sql = f"""
            select category_id as bsr_cate_1_id, en_name as bsr_cate_1_name
            from big_data_selection.dim_bsr_category_tree
            where site_name = '{self.site_name}'
            and category_parent_id = 0
        """
        self.df_cate_name = self.spark.sql(sqlQuery=sql)
        print(sql)

    def run(self):
        # 读取数据
        self.read_data()
        # 逻辑处理
        self.handle_fb_agg()
        # 字段处理
        self.sava_data()

    def handle_fb_agg(self):
        # 旧代码：df_fb_asin_info 直接来自 flow，已自带 is_asin_new 和 bsr_cate_1_id，不用再 join
        # self.df_fb_cate_asin_cal = self.df_fb_asin_info

        # 现在 df_fb_asin_info 只有 seller_id + asin，按 asin 补一级分类和新品标记
        self.df_fb_cate_asin_cal = self.df_fb_asin_info \
            .join(self.df_asin_history, on='asin', how='left') \
            .join(self.df_flow_asin, on='asin', how='left')

        # 分类取不到的 asin 归到 '无'，与原逻辑一致
        self.df_fb_cate_asin_cal = self.df_fb_cate_asin_cal.na.fill({'bsr_cate_1_id': '无'})

        # 按 seller_id + 一级分类聚合: 分类下 asin 数 + 新品数
        self.df_fb_cate_asin_cal = self.df_fb_cate_asin_cal.groupby(['seller_id', 'bsr_cate_1_id']).agg(
            F.count("asin").alias("fb_cate_asin_num"),
            F.sum("is_asin_new").alias("fb_cate_new_asin_num"),
        )

        # 店铺总 asin 数：数 dim_fb_asin_info 的关系条数，与 dwt_fb_base_report.fb_asin_total 同口径
        # count 会跳过 null，所以没有任何 asin 的店铺这里是 0
        self.df_fb_asin_cal = self.df_fb_asin_info.groupby(['seller_id']).agg(
            F.count("asin").alias("fb_asin_num"))

        # BSR 分类市场总量(仍用 dim_cal_asin_history_detail 全量 asin, 作为市场占比分母)
        self.df_bsr_asin_cal = self.df_asin_history.groupby(['bsr_cate_1_id']).agg(
            F.count("asin").alias("bsr_asin_num"))

        # 合并取到卖家分类的asin数量 和 bsr分类asin数量
        self.df_result_cal = self.df_fb_cate_asin_cal \
            .join(self.df_fb_asin_cal, on='seller_id', how='left') \
            .join(self.df_bsr_asin_cal, on='bsr_cate_1_id', how='left')

        # 分类店铺产品占比
        self.df_result_cal = self.df_result_cal.withColumn("fb_cate_asin_rate",
                                                           F.ceil((F.col("fb_cate_asin_num") / F.col(
                                                               "fb_asin_num") * 10000)) / 10000)

        # 分类店铺新品占比
        self.df_result_cal = self.df_result_cal.withColumn("fb_cate_new_asin_rate",
                                                           F.ceil((F.col("fb_cate_new_asin_num") / F.col(
                                                               "fb_asin_num") * 10000)) / 10000)

        # 分类市场占比
        self.df_result_cal = self.df_result_cal.withColumn("fb_market_rate",
                                                           F.ceil((F.col("fb_cate_asin_num") / F.col(
                                                               "bsr_asin_num") * 10000)) / 10000)

    def sava_data(self):
        df_save = self.df_result_cal.join(
            self.df_cate_name, on='bsr_cate_1_id', how='left'
        )

        # 未匹配到的分类名称置为无
        df_save = df_save.na.fill({'bsr_cate_1_name':'无'})

        df_save = df_save.select(
            F.col('seller_id'),
            F.col('bsr_cate_1_id'),
            F.col('bsr_cate_1_name'),
            F.col('fb_cate_asin_num'),
            F.col('fb_cate_new_asin_num'),
            F.col('fb_asin_num'),
            F.col('bsr_asin_num'),
            F.col('fb_cate_asin_rate'),
            F.col('fb_cate_new_asin_rate'),
            F.col('fb_market_rate'),
            F.date_format(F.current_timestamp(), 'yyyy-MM-dd HH:mm:SS').alias('created_time'),
            F.date_format(F.current_timestamp(), 'yyyy-MM-dd HH:mm:SS').alias('updated_time'),
            F.lit(self.site_name).alias('site_name'),
            F.lit(self.date_type).alias('date_type'),
            F.lit(self.date_info).alias('date_info')
        )

        print(f"清除hdfs目录中:{self.hdfs_path}")
        HdfsUtils.delete_file_in_folder(self.hdfs_path)

        df_save = df_save.repartition(1)
        partition_by = ["site_name", "date_type", "date_info"]
        print(f"当前存储的表名为：{self.hive_tb},分区为{partition_by}", )
        df_save.write.saveAsTable(name=self.hive_tb, format='hive', mode='append', partitionBy=partition_by)
        print("success")


if __name__ == '__main__':
    site_name = CommonUtil.get_sys_arg(1, None)
    date_type = CommonUtil.get_sys_arg(2, None)
    date_info = CommonUtil.get_sys_arg(3, None)  # 参数3：年-周/年-月/年-季/年-月-日, 比如: 2022-1
    handle_obj = DwtFbCategoryReport(site_name=site_name, date_type=date_type, date_info=date_info)
    handle_obj.run()
