"""
   @Author      : CT
   @Description : 年度ASIN聚合数据导出ES
   @SourceTable :
                  dwt_ai_asin_year (年度聚合数据)
                  dim_asin_profit_rate_info (利润率数据)

   @SinkIndex   : {site_name}_ai_asin_analyze_detail_last365_day
   @CreateTime  : 2026/03/13
"""
import os
import sys

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

from utils.common_util import CommonUtil
from utils.spark_util import SparkUtil
from utils.es_util import EsUtils
from datetime import datetime
from pyspark.sql import functions as F


class EsAiAsinYear(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
        self.spark = SparkUtil.get_spark_session(f"{self.__class__.__name__}")

        self.es_client = EsUtils.get_es_client()
        self.es_index = f"{self.site_name}_ai_asin_analyze_detail_last365_day"
        self.es_pipeline = f"{self.site_name}_ai_analyze_pipeline"
        self.es_options = self.get_es_options(self.es_index, self.es_pipeline)

        self.df_year = self.spark.sql("select 1+1;")
        self.df_profit_rate = self.spark.sql("select 1+1;")
        self.df_save = self.spark.sql("select 1+1;")

    @staticmethod
    def get_index_body():
        return {
            "settings": {
                "number_of_shards": "3",
                "number_of_replicas": "1",
                "analysis": {
                    "filter": {
                        "en_snowball": {
                            "type": "snowball",
                            "language": "English"
                        },
                        "en_synonym": {
                            "type": "synonym_graph",
                            "synonyms_path": "analysis/synonyms_en.txt",
                            "updateable": "true"
                        }
                    },
                    "analyzer": {
                        "en_analyzer": {
                            "type": "custom",
                            "tokenizer": "standard",
                            "filter": ["lowercase", "en_snowball"]
                        },
                        "en_search_analyzer": {
                            "tokenizer": "standard",
                            "filter": ["lowercase", "en_synonym", "en_snowball"]
                        }
                    },
                    "normalizer": {
                        "lowercase_normalizer": {
                            "type": "custom",
                            "char_filter": [],
                            "filter": ["lowercase"]
                        }
                    }
                }
            },
            "mappings": {
                "properties": {
                    "account_addr":          {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "account_name":          {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "analyze_id":            {"type": "integer"},
                    "ao_val":                {"type": "scaled_float", "scaling_factor": 100.0},
                    "appearance":            {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "asin":                  {"type": "keyword", "index": True},
                    "bought_month":          {"type": "integer"},
                    "bought_month_1":        {"type": "integer"},
                    "bought_month_2":        {"type": "integer"},
                    "bought_month_3":        {"type": "integer"},
                    "bought_month_4":        {"type": "integer"},
                    "bought_month_5":        {"type": "integer"},
                    "bought_month_6":        {"type": "integer"},
                    "bought_month_7":        {"type": "integer"},
                    "bought_month_8":        {"type": "integer"},
                    "bought_month_9":        {"type": "integer"},
                    "bought_month_10":       {"type": "integer"},
                    "bought_month_11":       {"type": "integer"},
                    "bought_month_12":       {"type": "integer"},
                    "bought_month_mom":      {"type": "scaled_float", "scaling_factor": 100.0},
                    "bought_month_sum":      {"type": "integer"},
                    "bought_month_yoy":      {"type": "scaled_float", "scaling_factor": 100.0},
                    "brand":                 {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "bsr_rank":              {"type": "integer"},
                    "buy_box_seller_type":   {"type": "keyword"},
                    "category":              {"type": "keyword"},
                    "category_current_id":   {"type": "keyword"},
                    "category_id":           {"type": "keyword"},
                    "color":                 {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "crowd":                 {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "date_info":             {"type": "keyword"},
                    "describe":              {"type": "keyword"},
                    "fb_country_name":       {"type": "keyword"},
                    "festival":              {"type": "keyword"},
                    "function":              {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "img":                   {"type": "keyword"},
                    "img_num":               {"type": "integer"},
                    "is_ascending_flag":     {"type": "integer"},
                    "is_new_flag":           {"type": "integer"},
                    "label_content":         {"type": "text", "fields": {"keyword": {"type": "keyword", "normalizer": "lowercase_normalizer"}}},
                    "launch_time":           {"type": "keyword"},
                    "launch_time_type":      {"type": "integer"},
                    "last_year_extra":       {"type": "object", "properties": {"asin": {"type": "keyword"}, "is_stable_flag": {"type": "integer"}, "is_periodic_flag": {"type": "integer"}, "is_ascending_flag": {"type": "integer"}, "max_bought_month_arr": {"type": "integer"}}},
                    "material":              {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "multi_color_content":   {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "multi_color_flag":      {"type": "keyword"},
                    "package_quantity":      {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "package_quantity_arr":  {"type": "integer"},
                    "package_quantity_flag": {"type": "keyword"},
                    "parent_asin":           {"type": "keyword"},
                    "price":                 {"type": "scaled_float", "scaling_factor": 100.0},
                    "price_mom":             {"type": "float"},
                    "price_yoy":             {"type": "float"},
                    "profit_key":            {"type": "keyword"},
                    "profit_rate_extra":     {"type": "object", "properties": {"profit_key": {"type": "keyword"}, "ocean_profit": {"type": "float"}, "air_profit": {"type": "float"}}},
                    "q1_bought_month":       {"type": "integer"},
                    "q2_bought_month":       {"type": "integer"},
                    "q3_bought_month":       {"type": "integer"},
                    "q4_bought_month":       {"type": "integer"},
                    "rating":                {"type": "scaled_float", "scaling_factor": 100.0},
                    "scene_comment":         {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "scene_title":           {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "seller_id":             {"type": "keyword"},
                    "shape":                 {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "short_desc":            {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "site_name":             {"type": "keyword"},
                    "size":                  {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "theme":                 {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "title":                 {"type": "text", "analyzer": "en_analyzer", "search_analyzer": "en_search_analyzer"},
                    "title_len":             {"type": "integer"},
                    "title_pic_content":     {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "title_pic_flag":        {"type": "keyword"},
                    "title_word_content":    {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "title_word_flag":       {"type": "keyword"},
                    "together_asin":         {"type": "keyword"},
                    "total_appear_month":    {"type": "integer"},
                    "total_appear_month_str":{"type": "keyword"},
                    "total_comments":        {"type": "integer"},
                    "uses":                  {"type": "keyword", "normalizer": "lowercase_normalizer"},
                    "variation_flag":        {"type": "integer"},
                    "variation_num":         {"type": "integer"},
                    "weight":                {"type": "keyword", "normalizer": "lowercase_normalizer"}
                }
            }
        }

    @staticmethod
    def get_es_options(index_name, pipeline_id):
        return {
            "es.nodes": EsUtils.__es_ip__,
            "es.port": EsUtils.__es_port__,
            "es.net.http.auth.user": EsUtils.__es_user__,
            "es.net.http.auth.pass": EsUtils.__es_passwd__,
            "es.mapping.id": "asin",
            "es.resource": f"{index_name}/_doc",
            "es.batch.write.refresh": "false",
            "es.batch.write.retry.wait": "60s",
            "es.batch.size.entries": "5000",
            "es.nodes.wan.only": "false",
            "es.batch.write.concurrency": "40",
            "es.write.operation": "index",
            "es.ingest.pipeline": f"{pipeline_id}"
        }

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

    def read_data(self):
        # 1. 读取年度聚合数据
        sql1 = f"""
            select
                account_addr,
                account_name,
                analyze_id,
                ao_val,
                appearance,
                asin,
                bought_month,
                bought_month_1,
                bought_month_2,
                bought_month_3,
                bought_month_4,
                bought_month_5,
                bought_month_6,
                bought_month_7,
                bought_month_8,
                bought_month_9,
                bought_month_10,
                bought_month_11,
                bought_month_12,
                bought_month_mom,
                bought_month_sum,
                bought_month_yoy,
                brand,
                bsr_rank,
                buy_box_seller_type,
                category,
                category_current_id,
                category_id,
                color,
                crowd,
                `describe`,
                fb_country_name,
                festival,
                `function`,
                img,
                img_num,
                is_ascending_flag,
                is_new_flag,
                label_content,
                launch_time,
                launch_time_type,
                material,
                multi_color_content,
                multi_color_flag,
                package_quantity,
                package_quantity_arr,
                package_quantity_flag,
                parent_asin,
                price,
                price_mom,
                price_yoy,
                profit_key,
                q1_bought_month,
                q2_bought_month,
                q3_bought_month,
                q4_bought_month,
                rating,
                scene_comment,
                scene_title,
                seller_id,
                shape,
                short_desc,
                size,
                theme,
                title,
                title_len,
                title_pic_content,
                title_pic_flag,
                title_word_content,
                title_word_flag,
                together_asin,
                total_appear_month_str,
                total_comments,
                uses,
                variation_flag,
                variation_num,
                weight,
                site_name,
                date_info
            from dwt_ai_asin_year
            where site_name = '{self.site_name}'
              and date_type = '{self.date_type}'
              and date_info = '{self.date_info}'
        """
        self.df_year = self.spark.sql(sql1).repartition(40, 'asin').cache()
        print("年度聚合数据读取完成，示例数据如下：")
        self.df_year.show(10, truncate=True)

        # 2. 读取利润率数据
        sql2 = f"""
            select asin, price, ocean_profit, air_profit
            from dim_asin_profit_rate_info
            where site_name = '{self.site_name}'
        """
        self.df_profit_rate = self.spark.sql(sql2).repartition(40, 'asin').cache()
        print("利润率数据读取完成，示例数据如下：")
        self.df_profit_rate.show(10, truncate=True)

    def handle_data(self):
        # 组装profit_rate_extra结构体
        df_profit = self.df_profit_rate.withColumn(
            "profit_rate_extra",
            F.when(
                F.col("ocean_profit").isNull() & F.col("air_profit").isNull(),
                F.lit(None)
            ).otherwise(
                F.struct(
                    F.col("ocean_profit").alias("ocean_profit"),
                    F.col("air_profit").alias("air_profit")
                )
            )
        ).drop("ocean_profit", "air_profit")

        # package_quantity_arr: 字符串转int数组
        # total_appear_month: total_appear_month_str逗号切分后的int数组
        self.df_save = self.df_year.join(
            df_profit, on=["asin", "price"], how="left"
        ).withColumn(
            "package_quantity_arr",
            F.expr("transform(split(package_quantity_arr, ','), x -> cast(x as int))")
        ).withColumn(
            "total_appear_month",
            F.expr("transform(split(total_appear_month_str, ','), x -> cast(x as int))")
        ).select(
            "account_addr",
            "account_name",
            "analyze_id",
            "ao_val",
            "appearance",
            "asin",
            "bought_month",
            "bought_month_1",
            "bought_month_2",
            "bought_month_3",
            "bought_month_4",
            "bought_month_5",
            "bought_month_6",
            "bought_month_7",
            "bought_month_8",
            "bought_month_9",
            "bought_month_10",
            "bought_month_11",
            "bought_month_12",
            "bought_month_mom",
            "bought_month_sum",
            "bought_month_yoy",
            "brand",
            "bsr_rank",
            "buy_box_seller_type",
            "category",
            "category_current_id",
            "category_id",
            "color",
            "crowd",
            "date_info",
            F.col("describe"),
            "fb_country_name",
            "festival",
            F.col("function"),
            "img",
            "img_num",
            "is_ascending_flag",
            "is_new_flag",
            "label_content",
            "launch_time",
            "launch_time_type",
            "material",
            "multi_color_content",
            "multi_color_flag",
            "package_quantity",
            "package_quantity_arr",
            "package_quantity_flag",
            "parent_asin",
            "price",
            "price_mom",
            "price_yoy",
            "profit_key",
            "profit_rate_extra",
            "q1_bought_month",
            "q2_bought_month",
            "q3_bought_month",
            "q4_bought_month",
            "rating",
            "scene_comment",
            "scene_title",
            "seller_id",
            "shape",
            "short_desc",
            "site_name",
            "size",
            "theme",
            "title",
            "title_len",
            "title_pic_content",
            "title_pic_flag",
            "title_word_content",
            "title_word_flag",
            "together_asin",
            "total_appear_month",
            "total_appear_month_str",
            "total_comments",
            "uses",
            "variation_flag",
            "variation_num",
            "weight"
        ).cache()

        print("数据处理完成，示例数据如下：")
        self.df_save.show(10, truncate=True)

        self.df_year.unpersist()
        self.df_profit_rate.unpersist()

    def save_data(self):
        EsUtils.create_index(self.es_index, self.es_client, self.get_index_body())
        try:
            self.df_save.repartition(40).write.format("org.elasticsearch.spark.sql") \
                .options(**self.es_options) \
                .mode("append") \
                .save()
            print(f"ES {self.es_index} 索引更新完毕！")
            CommonUtil.send_wx_msg(['chenyuanjie'], 'ES年度ASIN数据更新完成', f'索引：{self.es_index} {self.site_name} {self.date_type} {self.date_info}')
        except Exception as e:
            print("An error occurred while writing to Elasticsearch:", str(e))
            CommonUtil.send_wx_msg(['chenyuanjie'], '\u26A0 ES数据更新失败', f'失败索引：{self.es_index}')


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)

    assert site_name is not None, "site_name 不能为空！"
    assert date_type is not None, "date_type 不能为空！"
    assert date_info is not None, "date_info 不能为空！"

    print("开始执行时间：", datetime.now().strftime("%Y-%m-%d %H:%M"))
    obj = EsAiAsinYear(site_name=site_name, date_type=date_type, date_info=date_info)
    obj.run()
    print("执行结束时间：", datetime.now().strftime("%Y-%m-%d %H:%M"))
    print("success！！！")
