"""
   @Author      : HuangJian
   @Description : 上架日期补充表
   @SourceTable  :
                  ① ods_asin_detail / ods_self_asin_detail（当期抓取上架时间）
                  ② dim_keepa_asin_info(us) / ods_asin_keep_date(其他站点)（keepa上架时间兜底）
                  ③ dim_asin_launchtime_info（历史数据）
   @SinkTable   : dim_asin_launchtime_info
   @CreateTime  : 2023/12/12 11:20
   @UpdateTime  : 2026/06/12
"""
import os
import sys

sys.path.append(os.path.dirname(sys.path[0]))
from utils.common_util import CommonUtil, DateTypes
from utils.spark_util import SparkUtil
from pyspark.sql.window import Window
from pyspark.sql import functions as F
from pyspark.sql.types import StringType
from yswg_utils.common_udf import udf_handle_string_null_value as NullUDF
from utils.redis_utils import RedisUtils


class DimAsinLaunchtimeInfo(object):

    def __init__(self, site_name, date_type, date_info):
        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.hive_table = "dim_asin_launchtime_info"
        self.partition_dict = {"site_name": site_name}
        self.partitions_num = CommonUtil.reset_partitions(site_name, 50)
        self.spark.udf.register('u_handle_string_num_value', NullUDF, StringType())

    def _read_current_asin(self):
        """Step1: 读取当期 asin 及抓取上架时间"""
        if self.date_type == 'all':
            print("==================执行全量数据整合=================")
            sql = f"""
                select asin,
                       u_handle_string_num_value(launch_time) as crawl_asin_launch_time,
                       date_format(updated_at, '{CommonUtil._date_time_format}') as updated_time,
                       concat(site_name,',',date_type,',',date_info) as period_label
                from ods_asin_detail where site_name = '{self.site_name}'
                union
                select asin,
                       u_handle_string_num_value(launch_time) as crawl_asin_launch_time,
                       date_format(created_at, '{CommonUtil._date_time_format}') as updated_time,
                       concat(site_name,',',date_type,',',date_info) as period_label
                from ods_self_asin_detail where site_name = '{self.site_name}'
            """
        elif self.date_type in (DateTypes.week.name, DateTypes.month.name, DateTypes.month_week.name, 'month_aba_me'):
            sql = f"""
                select asin,
                       u_handle_string_num_value(launch_time) as crawl_asin_launch_time,
                       date_format(updated_at, '{CommonUtil._date_time_format}') as updated_time,
                       concat(site_name,',',date_type,',',date_info) as period_label
                from ods_asin_detail
                where site_name = '{self.site_name}'
                  and date_type = '{self.date_type}'
                  and date_info = '{self.date_info}'
            """
        elif self.date_type == DateTypes.day.name:
            sql = f"""
                select asin,
                       u_handle_string_num_value(launch_time) as crawl_asin_launch_time,
                       date_format(created_at, '{CommonUtil._date_time_format}') as updated_time,
                       concat(site_name,',',date_type,',',date_info) as period_label
                from ods_self_asin_detail
                where site_name = '{self.site_name}'
                  and date_type = '{self.date_type}'
                  and date_info = '{self.date_info}'
            """
        print(sql)
        return self.spark.sql(sql).cache()

    def _read_history(self):
        """Step2(辅助): 读取历史已整合上架日期"""
        sql = f"""
            select asin,
                   crawl_asin_launch_time,
                   '1970-01-01 00:00:00' as updated_time,
                   appear_period_label as period_label
            from dim_asin_launchtime_info
            where site_name='{self.site_name}'
        """
        print(sql)
        return self.spark.sql(sql)

    def _read_keepa(self):
        """Step3(辅助): 读取 keepa 上架时间"""
        if self.site_name == 'us':
            sql = f"""select asin,
                            keepa_launch_time as keepa_asin_launch_time,
                            updated_time,
                            1 as keepa_crawl_flag
                        from dim_keepa_asin_info
                        where site_name='{self.site_name}'"""
            print(sql)
            return self.spark.sql(sql).cache()
        else:
            sql = f"""select asin,
                            launch_time as keepa_asin_launch_time,
                            updated_at as updated_time,
                            1 as keepa_crawl_flag
                        from ods_asin_keep_date
                        where site_name='{self.site_name}'
                        and state = 3"""
            print(sql)
            w = Window.partitionBy('asin').orderBy(F.col('updated_time').desc_nulls_last())
            return (
                self.spark.sql(sql)
                .withColumn('_rn', F.row_number().over(w))
                .filter(F.col('_rn') == 1)
                .drop('_rn')
                .cache()
            )

    def run(self):
        # Step1: 当期 asin 抓取上架时间
        df_current = self._read_current_asin()

        # Step2: 与历史合并去重（all 模式已含全量，无需合并历史）
        if self.date_type != 'all':
            df_current = df_current.unionByName(self._read_history()).cache()

        # 去重：优先保留非空 crawl_asin_launch_time，再取最新 updated_time
        w = Window.partitionBy('asin').orderBy(
            F.col('crawl_asin_launch_time').isNull().cast('int').asc(),
            F.col('updated_time').desc_nulls_last()
        )
        df_merged = (
            df_current
            .withColumn('_rn', F.row_number().over(w))
            .filter(F.col('_rn') == 1)
            .drop('_rn')
        )

        # Step3: 关联 keepa，填充 crawl_asin_launch_time 为空的 asin
        df_keepa = self._read_keepa()
        df_result = df_merged.join(
            df_keepa.select('asin', 'keepa_asin_launch_time', 'keepa_crawl_flag'),
            on='asin', how='left'
        )

        df_save = df_result.select(
            F.col('asin'),
            F.to_date(F.coalesce(F.col('crawl_asin_launch_time'), F.col('keepa_asin_launch_time'))).cast('string').alias('asin_launch_time'),
            F.to_date(F.col('crawl_asin_launch_time')).cast('string').alias('crawl_asin_launch_time'),
            F.to_date(F.col('keepa_asin_launch_time')).cast('string').alias('keepa_asin_launch_time'),
            F.col('period_label').alias('appear_period_label'),
            F.when(F.col('keepa_crawl_flag') == 1, F.lit(1)).otherwise(F.lit(0)).alias('keepa_crawl_flag'),
            F.lit(self.site_name).alias('site_name')
        ).repartition(self.partitions_num)

        CommonUtil.save_or_update_table(
            spark_session=self.spark,
            hive_tb_name=self.hive_table,
            partition_dict=self.partition_dict,
            df_save=df_save,
            drop_exist_tmp_flag=False
        )
        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)
    lock_name = "dim_asin_launchtime_info"
    expire = 120 * 60 if date_type == "all" else 30 * 60
    lock_flag = RedisUtils.acquire_redis_lock(lock_name, expire_time=expire, retry_flag=True, retry_time=10 * 60)
    if lock_flag:
        try:
            DimAsinLaunchtimeInfo(site_name, date_type, date_info).run()
        finally:
            RedisUtils.release_redis_lock(lock_name)
