import logging
import os
import time

from langfuse import observe

from common.embedding_service import create_embedding_async


logger = logging.getLogger(__name__)


def _get_price_clauses(params, sql_params: list) -> list[str]:
    """Lọc theo giá (Parameterized)."""
    clauses = []
    p_min = getattr(params, "price_min", None)
    if p_min is not None:
        clauses.append("sale_price >= %s")
        sql_params.append(p_min)
    p_max = getattr(params, "price_max", None)
    if p_max is not None:
        clauses.append("sale_price <= %s")
        sql_params.append(p_max)

    return clauses


def _get_discount_params(params) -> tuple[int | None, int | None]:
    """Lấy discount_min và discount_max từ params."""
    discount_min = getattr(params, "discount_min", None)
    discount_max = getattr(params, "discount_max", None)

    if discount_min is not None or discount_max is not None:
        if discount_min and discount_max:
            logger.info(f"🏷️ [DISCOUNT FILTER] Filtering products with discount {discount_min}% - {discount_max}%")
        elif discount_min:
            logger.info(f"🏷️ [DISCOUNT FILTER] Filtering products with discount >= {discount_min}%")
        else:
            logger.info(f"🏷️ [DISCOUNT FILTER] Filtering products with discount <= {discount_max}%")

    return discount_min, discount_max


def _get_metadata_clauses(params, sql_params: list) -> list[str]:
    """
    HARD FILTER: Gender + Age — lọc trực tiếp ở SQL level.
    Gender tự động include 'unisex' fallback cho men/women.
    Color + Product type → semantic search tự handle qua description.
    """
    clauses = []

    # Gender filter
    gender_val = getattr(params, "gender_by_product", None)
    if gender_val:
        gender_lower = gender_val.lower().strip()
        # Include 'unisex' fallback for men/women
        if gender_lower in ("men", "women"):
            clauses.append("gender_by_product IN (%s, %s)")
            sql_params.extend([gender_lower, "unisex"])
        else:
            clauses.append("gender_by_product = %s")
            sql_params.append(gender_lower)
        logger.info(f"👫 [SQL FILTER] Gender: {gender_val}")

    # Age filter
    age_val = getattr(params, "age_by_product", None)
    if age_val:
        age_lower = age_val.lower().strip()
        clauses.append("age_by_product = %s")
        sql_params.append(age_lower)
        logger.info(f"🎂 [SQL FILTER] Age: {age_val}")

    # Color filter (LIKE match on master_color OR product_color_name)
    color_val = getattr(params, "master_color", None)
    if color_val:
        color_lower = color_val.lower().strip()
        clauses.append("(LOWER(master_color) LIKE %s OR LOWER(product_color_name) LIKE %s)")
        sql_params.extend([f"%{color_lower}%", f"%{color_lower}%"])
        logger.info(f"🎨 [SQL FILTER] Color: {color_val}")

    # Product name filter: resolve synonym → split words → skip generic → LIKE AND
    # "áo cổ bẻ khaki" → resolve → "Áo Polo khaki" → keywords: ["polo", "khaki"]
    # "áo thun disney"  → resolve → "Áo phông disney" → keywords: ["phông", "disney"]
    # Auto-derive generic prefix words from PRODUCT_LINE_MAP keys
    # "Áo Sơ mi" → "áo", "Quần jean" → "quần", "Váy liền" → "váy", ...
    from agent.tools.product_mapping import PRODUCT_LINE_MAP
    GENERIC_WORDS = {key.split()[0].lower() for key in PRODUCT_LINE_MAP.keys()}
    name_val = getattr(params, "product_name", None)
    if name_val:
        from agent.tools.product_mapping import resolve_product_name
        resolved_name = resolve_product_name(name_val)
        words = resolved_name.lower().strip().split()
        keywords = [w for w in words if w not in GENERIC_WORDS and len(w) >= 2]
        # Fallback: nếu tất cả đều generic (VD: "quần"), vẫn dùng làm filter
        if not keywords:
            keywords = [w for w in words if len(w) >= 2]
        for kw in keywords:
            clauses.append("LOWER(product_name) LIKE %s")
            sql_params.append(f"%{kw}%")
        if keywords:
            logger.info(f"🏷️ [SQL FILTER] Product name: '{name_val}' → resolved: '{resolved_name}' → keywords: {keywords}")

    return clauses


@observe(name="build-starrocks-query")
async def build_starrocks_query(params, query_vector: list[float] | None = None) -> tuple[str, list]:
    """
    Build SQL query với Parameterized Query để tránh SQL Injection.
    Returns: (sql_string, params_list)
    """

    # ============================================================
    # CASE 1: CODE SEARCH
    # ============================================================
    magento_code = getattr(params, "magento_ref_code", None)
    if magento_code:
        sql = """
        SELECT 
            internal_ref_code,
            magento_ref_code,
            product_color_code,
            product_name,
            master_color,
            product_image_url_thumbnail,
            product_web_url,
            description_text,
            sale_price,
            original_price,
            discount_amount,
            ROUND(((original_price - sale_price) / original_price * 100), 0) as discount_percent,
            age_by_product,
            gender_by_product,
            product_line_vn,
            quantity_sold,
            size_scale,
            1.0 as similarity_score
        FROM shared_source.magento_product_dimension_with_text_embedding
        WHERE internal_ref_code = %s OR magento_ref_code = %s
        """
        return sql, [magento_code, magento_code]


    # ============================================================
    # CASE 2: DISCOVERY — Hàng mới / Bán chạy (Direct SQL, no embedding)
    # Khác với price/gender filter: discovery cần scan TOÀN BỘ bảng
    # vì top 100 vector results gần như không chứa new/best_seller
    # ============================================================
    discovery_mode = getattr(params, "discovery_mode", None)
    if discovery_mode:
        discovery_mode = discovery_mode.lower().strip()
        sql_params: list = []

        # Metadata filters (gender + age)
        where_clauses = _get_metadata_clauses(params, sql_params)



        # Price filters
        where_clauses.extend(_get_price_clauses(params, sql_params))

        # Discovery-specific WHERE + ORDER
        if discovery_mode == "new":
            where_clauses.append("is_new_product = 1")
            order_by = "quantity_sold DESC, magento_ref_code"
            logger.info("🆕 [DISCOVERY] New products, filters=%s", where_clauses)
        elif discovery_mode == "best_seller":
            where_clauses.append("quantity_sold > 0")
            order_by = "quantity_sold DESC, magento_ref_code"
            logger.info("🔥 [DISCOVERY] Best sellers, filters=%s", where_clauses)
        else:
            discovery_mode = None

        if discovery_mode:
            where_str = " AND ".join(where_clauses) if where_clauses else "1=1"
            sql = f"""
            SELECT
                internal_ref_code,
                magento_ref_code,
                product_color_code,
                product_name,
                master_color,
                product_image_url_thumbnail,
                product_web_url,
                sale_price,
                original_price,
                discount_amount,
                ROUND(((original_price - sale_price) / original_price * 100), 0) as discount_percent,
                age_by_product,
                gender_by_product,
                product_line_vn,
                quantity_sold,
                description_text,
                size_scale
            FROM shared_source.magento_product_dimension_with_text_embedding
            WHERE {where_str}
            ORDER BY {order_by}
            LIMIT 20
            """
            logger.info("⚡ [DISCOVERY] Direct SQL — no embedding")
            return sql, sql_params


    # ============================================================
    # CASE 3: SEMANTIC VECTOR SEARCH
    # ============================================================
    query_text = getattr(params, "description", None)
    if query_text and query_vector is None:
        query_vector = await create_embedding_async(query_text)

    if not query_vector:
        return "", []

    # Vector params
    v_str = "[" + ",".join(str(v) for v in query_vector) + "]"

    # Collect All Filters
    sql_params: list = []

    # 1. Price
    price_clauses = _get_price_clauses(params, sql_params)

    # 2. Metadata: Gender + Age + Color (HARD FILTER — all at SQL level)
    metadata_clauses = _get_metadata_clauses(params, sql_params)

    all_clauses = price_clauses + metadata_clauses

    # Discovery mode filters
    discovery_mode = getattr(params, "discovery_mode", None)
    if discovery_mode:
        discovery_mode = discovery_mode.lower().strip()
        if discovery_mode == "new":
            all_clauses.append("is_new_product = 1")
            logger.info("🆕 [SQL FILTER] Discovery: new products only")
        elif discovery_mode == "best_seller":
            all_clauses.append("quantity_sold > 0")
            logger.info("🔥 [SQL FILTER] Discovery: best sellers")

    # Get discount params
    discount_min, discount_max = _get_discount_params(params)

    post_filter_conditions = []
    
    # Price + Gender + Age filters
    if all_clauses:
        post_filter_conditions.extend(all_clauses)
    
    # Discount filters
    if discount_min is not None or discount_max is not None:
        post_filter_conditions.append("sale_price < original_price")  # Ensure has discount
        if discount_min is not None:
            post_filter_conditions.append("discount_percent >= %s")
            sql_params.append(discount_min)
        if discount_max is not None:
            post_filter_conditions.append("discount_percent <= %s")
            sql_params.append(discount_max)
    
    post_filter_where = ""
    if post_filter_conditions:
        post_filter_where = " WHERE " + " AND ".join(post_filter_conditions)

    # Determine sort order: best_seller uses quantity_sold, otherwise similarity_score
    if discovery_mode == "best_seller":
        final_order = "ORDER BY max_sold DESC, max_score DESC"
        extra_agg = ",\n        MAX(quantity_sold) as max_sold"
    else:
        final_order = "ORDER BY max_score DESC"
        extra_agg = ""

    sql = f"""
    WITH vector_matches AS (
        SELECT /*+ SET_VAR(ann_params='{{"ef_search":256}}') */
            internal_ref_code,
            magento_ref_code,
            product_color_code,
            product_name,
            master_color,
            product_color_name,
            product_image_url_thumbnail,
            product_web_url,
            sale_price,
            original_price,
            discount_amount,
            ROUND(((original_price - sale_price) / original_price * 100), 0) as discount_percent,
            age_by_product,
            gender_by_product,
            product_line_vn,
            product_line_en,
            description_text,
            size_scale,
            quantity_sold,
            is_new_product,
            approx_cosine_similarity(vector, {v_str}) as similarity_score
        FROM shared_source.magento_product_dimension_with_text_embedding
        ORDER BY similarity_score DESC
        LIMIT 200
    ),
    filtered_matches AS (
        SELECT * FROM vector_matches
        {post_filter_where}
        ORDER BY similarity_score DESC
        LIMIT 150
    )
    SELECT 
        internal_ref_code,
        MAX_BY(magento_ref_code, similarity_score) as magento_ref_code,
        product_color_code,
        MAX_BY(product_name, similarity_score) as product_name,
        MAX_BY(master_color, similarity_score) as master_color,
        MAX_BY(product_image_url_thumbnail, similarity_score) as product_image_url_thumbnail,
        MAX_BY(product_web_url, similarity_score) as product_web_url,
        MAX_BY(sale_price, similarity_score) as sale_price,
        MAX_BY(original_price, similarity_score) as original_price,
        MAX_BY(discount_amount, similarity_score) as discount_amount,
        MAX_BY(discount_percent, similarity_score) as discount_percent,
        MAX_BY(description_text, similarity_score) as description_text,
        MAX_BY(gender_by_product, similarity_score) as gender_by_product,
        MAX_BY(age_by_product, similarity_score) as age_by_product,
        MAX_BY(product_line_vn, similarity_score) as product_line_vn,
        MAX_BY(quantity_sold, similarity_score) as quantity_sold,
        MAX_BY(size_scale, similarity_score) as size_scale,
        MAX(similarity_score) as max_score{extra_agg}
    FROM filtered_matches
    GROUP BY product_color_code, internal_ref_code
    {final_order}
    LIMIT 80
    """

    # try:
    #     query_log_path = os.path.join(os.path.dirname(__file__), "note/query.txt")
        
    #     # Build executable query by substituting %s with actual values
    #     executable_sql = sql
    #     for param in sql_params:
    #         if isinstance(param, str):
    #             # Escape single quotes and wrap in quotes
    #             escaped = param.replace("'", "''")
    #             executable_sql = executable_sql.replace("%s", f"'{escaped}'", 1)
    #         else:
    #             executable_sql = executable_sql.replace("%s", str(param), 1)
        
    #     with open(query_log_path, "w", encoding="utf-8") as f:
    #         f.write(f"-- [HYDE SEARCH] Full Executable Query\n-- Original Params: {sql_params}\n{executable_sql}")
    # except Exception as e:
    #     logger.error(f"Error writing to query.txt: {e}")


    return sql, sql_params
