"""
Tag Search Tool — LangChain @tool cho AI gọi.
AI sinh keywords + tags → Tool build SQL → 3 tầng cascade → Trả kết quả.

Architecture:
  CỐ ĐỊNH (luôn giữ): product_type + color + gender + price
  BIẾN ĐỔI (fallback):
    Tầng 1: CỐ ĐỊNH + keywords  (từ nguyên văn user)
    Tầng 2: CỐ ĐỊNH + tags      (AI suy luận → tags chung)
    Tầng 3: CHỈ CỐ ĐỊNH         (product_type + color + gender)
"""

import json
import logging
import time

from langchain_core.tools import tool
from pydantic import BaseModel, Field

from common.starrocks_connection import get_db_connection
from .product_mapping import SYNONYM_TO_DB, get_related_lines

logger = logging.getLogger(__name__)

TABLE_NAME = "test_db.magento_product_dimension_with_text_embedding"

SELECT_COLUMNS = """
    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,
    COALESCE(discount_amount, 0) AS discount_amount,
    ROUND(CASE WHEN original_price > 0
        THEN ((original_price - sale_price) / original_price * 100)
        ELSE 0 END, 0) AS discount_percent,
    age_by_product,
    gender_by_product,
    product_line_vn,
    COALESCE(quantity_sold, 0) AS quantity_sold,
    COALESCE(is_new_product, 0) AS is_new_product,
    size_scale,
    description_text,
    suggest_items,
    similar_items
"""


# ═══════════════════════════════════════════════
# Pydantic Schema — AI sẽ fill vào đây
# ═══════════════════════════════════════════════
class TagSearchInput(BaseModel):
    """Schema cho hybrid search: keywords (user) + tags (AI suy luận)."""

    model_config = {"extra": "forbid"}

    # ① KEYWORDS — từ NGUYÊN VĂN của user (ưu tiên cao, search TRƯỚC)
    keywords: list[str] = Field(
        default=[],
        description=(
            "Từ khoá NGUYÊN VĂN từ câu hỏi khách. "
            "VD: khách nói '30/4' → keywords=['30/4']. "
            "Khách nói 'cờ đỏ sao vàng' → keywords=['cờ đỏ sao vàng']. "
            "Khách nói 'cotton ống rộng' → keywords=['cotton', 'ống rộng']. "
            "Tối đa 2. Để [] nếu khách không nhắc gì đặc biệt."
        )
    )

    # ② TAGS — AI suy luận từ 4 TRỤC CỐ ĐỊNH (BẮT BUỘC có prefix!)
    tags: list[str] = Field(
        default=[],
        description=(
            "AI suy luận ý định khách → chọn từ 4 TRỤC CỐ ĐỊNH (BẮT BUỘC có prefix!): "
            "Trục 1 (occ:): occ:di_lam, occ:di_choi, occ:di_tiec, occ:di_hoc, occ:mac_nha, occ:the_thao, occ:di_bien, occ:du_lich, occ:da_ngoai, occ:di_ngu. "
            "Trục 2a (wthr:): wthr:mua_he, wthr:mua_dong, wthr:giao_mua, wthr:troi_mua, wthr:troi_nang. "
            "Trục 2b (func:): func:thoang_mat, func:giu_am, func:tham_hut, func:nhanh_kho, func:chong_uv, func:can_gio. "
            "Trục 3 (style:): style:thanh_lich, style:nang_dong, style:basic, style:ca_tinh, style:de_thuong, style:tre_trung, style:toi_gian, style:smart_casual. "
            "Trục 4 (fit:): fit:oversize, fit:slim, fit:regular, fit:wide_leg, fit:cropped, fit:relaxed. "
            "VD: 'đi tiệc' → tags=['occ:di_tiec']. 'mùa đông' → tags=['wthr:mua_dong', 'func:giu_am']. "
            "KHÔNG tự nghĩ tag mới! PHẢI giữ prefix! Tối đa 3."
        )
    )

    # ═══ CỐ ĐỊNH — Luôn giữ trong mọi tầng ═══
    product_line_vn: list[str] = Field(
        default=[],
        description=(
            "⚠️ BẮT BUỘC — CẤM để trống []. Phải có ít nhất 1 giá trị khi gọi tool.\n"
            "Quy tắc CHỌN TÊN DÒNG SẢN PHẨM (Rất quan trọng): \n"
            "1. KHÁCH HỎI CỰC KỲ CỤ THỂ (VD 'áo phông', 'quần jean', 'sịp'): Chốt đúng tên ngách đó. VD: ['Áo phông'] hoặc ['Quần jean'] hoặc ['Quần lót'].\n"
            "2. KHÁCH HỎI TỪ KHOÁ NHÓM (VD 'áo', 'quần', 'váy'): Gửi từ KHOÁ GỐC. VD khách hỏi 'áo đi tiệc' → xuất ['Áo']. Hệ thống sẽ tự tìm mọi loại áo (sơ mi, polo, kiểu...), không cần liệt kê dài dòng.\n"
            "3. KHÁCH HỎI CHUNG CHUNG BAO LA (VD 'đồ đi biển', 'quần áo nam', 'có mẫu nào mới không'): TUYỆT ĐỐI KHÔNG tự bịa hay ép vào 1 ngách nhỏ. BẮT BUỘC xuất DÀN HẠNG MỤC TỔNG để quét toàn bộ: ['Áo', 'Quần', 'Váy', 'Bộ']. Hệ thống sẽ kết hợp nó với Tags suy luận để vớt đồ phù hợp!\n\n"
            "Gợi ý 1 số dòng SP cụ thể trong DB nếu khách cần đích danh: Áo phông, Áo Polo, Áo Sơ mi, Áo nỉ, Áo khoác gió, Áo khoác chần bông, Quần jean, Quần Khaki, Quần dài, Quần soóc, Quần mặc nhà, Váy liền, Chân váy, Bộ mặc nhà, Pyjama, Tất, Mũ..."
        ),
    )
    gender_by_product: str | None = Field(
        default=None,
        description="Giới tính. GIÁ TRỊ HỢP LỆ: women, men, boy, girl, unisex, newborn",
    )
    age_by_product: str | None = Field(
        default=None,
        description="Độ tuổi. GIÁ TRỊ HỢP LỆ: adult, kid, others",
    )
    master_color: str | None = Field(
        default=None,
        description=(
            "Màu sắc. Ghi CHÍNH XÁC từ khách nói: 'trắng', 'đen', 'đỏ'... "
            "VD: 'áo cờ đỏ sao vàng' → color='đỏ'."
        ),
    )
    price_min: int | None = Field(default=None, description="Giá thấp nhất (VND)")
    price_max: int | None = Field(default=None, description="Giá cao nhất (VND)")
    discount_min: int | None = Field(
        default=None, 
        description="% giảm giá TỐI THIỂU. VD: 'giảm 50%' → 50. 'hàng sale' → 1."
    )
    discount_max: int | None = Field(
        default=None, 
        description="% giảm giá TỐI ĐA. VD: 'giảm dưới 30%' → 30."
    )
    discovery_mode: str | None = Field(
        default=None,
        description="'new' = hàng mới, 'best_seller' = bán chạy. Chỉ dùng khi khách nói rõ.",
    )
    size: str | None = Field(
        default=None,
        description=(
            "Size sản phẩm khách yêu cầu. "
            "Người lớn: XS, S, M, L, XL, XXL, 3XL, 4XL. "
            "Trẻ em (chiều cao cm): 80, 86, 92, 98, 104, 110, 116, 122, 128, 134, 140, 152, 164. "
            "Trẻ sơ sinh (tháng): 1/3, 4/6, 7/9, 10/12, 13/14. "
            "VD: 'áo size L' → size='L'. 'quần bé 110' → size='110'. "
            "Chỉ điền khi khách NÓI RÕ size. Nếu không nhắc → None."
        ),
    )
    magento_ref_code: str | None = Field(
        default=None,
        description="Mã SKU cụ thể. Chỉ điền khi khách cung cấp.",
    )
    reasoning: str | None = Field(
        default=None,
        description=(
            "SUY LUẬN: Giải thích ngắn gọn TẠI SAO bạn chọn params này. "
            "VD: '2/9 = Quốc Khánh → áo đỏ đi chơi', 'khách muốn sịp mặc đi biển → giữ nguyên sịp, tag đi biển'. "
            "Viết 1-2 câu bằng tiếng Việt."
        ),
    )


# ═══════════════════════════════════════════════
# SQL Builder
# ═══════════════════════════════════════════════
def _build_fixed_clauses(req: TagSearchInput, params: list) -> list[str]:
    """Build các filter CỐ ĐỊNH (luôn giữ trong mọi tầng)."""
    clauses = []

    # Product line (list) — resolve synonym → search cột product_line_vn
    if req.product_line_vn:
        resolved_types = []
        for pt_raw in req.product_line_vn:
            pt = pt_raw.strip()
            if not pt:
                continue
            # Synonym resolve: "sịp" → "Quần lót", "áo thun" → "Áo phông"
            mapped = SYNONYM_TO_DB.get(pt.lower(), pt)
            # Related lines: "Quần lót" → ["Quần lót", "Quần lót đùi", "Quần lót tam giác"]
            related = get_related_lines(mapped)
            resolved_types.extend(related)
        resolved_types = list(dict.fromkeys(resolved_types))  # dedupe, keep order
        if resolved_types:
            in_placeholders = []
            like_clauses = []
            
            # Broad terms that need LIKE instead of exact IN match
            BROAD_TERMS = {"áo", "quần", "váy", "đồ", "bộ", "phụ kiện", "giày", "dép", "túi", "balo", "mũ", "nón", "tất"}
            
            for rt in resolved_types:
                if rt.lower() in BROAD_TERMS:
                    like_clauses.append("LOWER(product_line_vn) LIKE LOWER(%s)")
                    params.append(f"%{rt}%")
                else:
                    in_placeholders.append("%s")
                    params.append(rt)
            
            sub_clauses = []
            if in_placeholders:
                sub_clauses.append(f"LOWER(product_line_vn) IN ({', '.join(f'LOWER({p})' for p in in_placeholders)})")
            
            sub_clauses.extend(like_clauses)
            
            if sub_clauses:
                clauses.append("(" + " OR ".join(sub_clauses) + ")")
                
            logger.info("🏷️ [MAPPING] product_line_vn %s → resolved %s", req.product_line_vn, resolved_types)

    # Gender
    if req.gender_by_product:
        gender_lower = req.gender_by_product.lower().strip()
        if gender_lower in ("women", "nữ", "female"):
            gender_db = "female"
        elif gender_lower in ("men", "nam", "male"):
            gender_db = "male"
        else:
            gender_db = gender_lower

        if gender_db in ("female", "male"):
            params.append(gender_db)
            params.append("unisex")
            clauses.append(f"gender_by_product IN (%s, %s)")
        else:
            params.append(gender_db)
            clauses.append(f"gender_by_product = %s")

    # Age
    if req.age_by_product:
        age_lower = req.age_by_product.lower().strip()
        if age_lower in ("baby", "newborn"):
            params.append("kid")
            clauses.append(f"age_by_product = %s")
        else:
            params.append(age_lower)
            clauses.append(f"age_by_product = %s")

    # Color
    if req.master_color:
        c = req.master_color.lower().strip()
        params.append(f"%{c}%")
        params.append(f"%{c}%")
        clauses.append(f"(LOWER(master_color) LIKE %s OR LOWER(product_color_name) LIKE %s)")

    # Price
    if req.price_min is not None:
        params.append(req.price_min)
        clauses.append(f"sale_price >= %s")
    if req.price_max is not None:
        params.append(req.price_max)
        clauses.append(f"sale_price <= %s")

    # Discount
    if req.discount_min is not None:
        params.append(req.discount_min)
        # discount_percent is usually derived as ((original - sale) / original * 100)
        # Let's assume there is a discount_percent column, but looking at SELECT_COLUMNS, 
        # discount_percent is a calculated field. We should calculate it in the WHERE clause or use original_price > sale_price.
        # Actually in magento_product_dimension_with_text_embedding there probably IS some discount logic, but let's use:
        # WHERE original_price > 0 AND ((original_price - sale_price) / original_price * 100) >= X
        clauses.append(f"(original_price > 0 AND ((original_price - sale_price) / original_price * 100) >= %s)")
    if req.discount_max is not None:
        params.append(req.discount_max)
        clauses.append(f"(original_price > 0 AND ((original_price - sale_price) / original_price * 100) <= %s)")

    # Size (FIND_IN_SET trên cột size_scale dạng 'S|M|L|XL' hoặc '104|110|116')
    if req.size:
        s = req.size.strip().upper()
        params.append(s)
        # REPLACE('|', ',') rồi FIND_IN_SET để match chính xác (L ≠ XL)
        clauses.append("FIND_IN_SET(%s, REPLACE(size_scale, '|', ',')) > 0")

    # Discovery mode
    if req.discovery_mode:
        mode = req.discovery_mode.lower().strip()
        if mode == "new":
            clauses.append("is_new_product = 1")
        elif mode == "best_seller":
            clauses.append("quantity_sold > 0")

    return clauses


# ═══════════════════════════════════════════════
# Tag → Vietnamese search text (cho LIKE query)
# ═══════════════════════════════════════════════
TAG_TO_SEARCH_TEXT: dict[str, str] = {
    # Trục 1 — Hoàn cảnh & Dịp
    "occ:di_lam": "đi làm",
    "occ:di_choi": "đi chơi",
    "occ:di_tiec": "đi tiệc",
    "occ:di_hoc": "đi học",
    "occ:mac_nha": "mặc nhà",
    "occ:the_thao": "thể thao",
    "occ:di_bien": "đi biển",
    "occ:du_lich": "du lịch",
    "occ:da_ngoai": "dã ngoại",
    "occ:di_ngu": "đi ngủ",
    # Trục 2a — Thời tiết
    "wthr:mua_he": "mùa hè",
    "wthr:mua_dong": "mùa đông",
    "wthr:giao_mua": "giao mùa",
    "wthr:troi_mua": "trời mưa",
    "wthr:troi_nang": "trời nắng",
    # Trục 2b — Tính năng
    "func:thoang_mat": "thoáng mát",
    "func:giu_am": "giữ ấm",
    "func:tham_hut": "thấm hút",
    "func:nhanh_kho": "nhanh khô",
    "func:chong_uv": "chống uv",
    "func:can_gio": "cản gió",
    # Trục 3 — Phong cách
    "style:thanh_lich": "thanh lịch",
    "style:nang_dong": "năng động",
    "style:basic": "basic",
    "style:ca_tinh": "cá tính",
    "style:de_thuong": "dễ thương",
    "style:tre_trung": "trẻ trung",
    "style:toi_gian": "tối giản",
    "style:smart_casual": "smart casual",
    # Trục 4 — Dáng
    "fit:oversize": "oversize",
    "fit:slim": "slim",
    "fit:regular": "regular",
    "fit:wide_leg": "ống rộng",
    "fit:cropped": "cropped",
    "fit:relaxed": "relaxed",
}


def _resolve_tag(tag: str) -> str:
    """Chuyển prefixed tag → Vietnamese text cho SQL LIKE.
    VD: 'occ:di_tiec' → 'đi tiệc', 'func:giu_am' → 'giữ ấm'.
    Nếu không có trong mapping (tag cũ format) → giữ nguyên.
    """
    return TAG_TO_SEARCH_TEXT.get(tag.strip().lower(), tag)


def _build_search_clause(search_terms: list[str], params: list, resolve_tags: bool = False) -> str:
    """Build OR clause cho keywords hoặc tags, search trong description_text và product_name.
    Tách các từ trong mỗi term bằng khoảng trắng và áp dụng toán tử AND để tìm kiếm rộng hơn.
    """
    term_clauses = []
    for term in search_terms:
        t = _resolve_tag(term) if resolve_tags else term.strip().lower()
        if not t:
            continue
            
        words = t.split()
        if not words:
            continue
            
        word_clauses = []
        for word in words:
            params.append(f"%{word}%")
            params.append(f"%{word}%")
            word_clauses.append(f"(LOWER(description_text) LIKE %s OR LOWER(product_name) LIKE %s)")
            
        term_clauses.append(f"({' AND '.join(word_clauses)})")
        
    if term_clauses:
        return f"({' OR '.join(term_clauses)})"
    return ""


def _build_full_query(fixed_clauses: list[str], search_clause: str | None) -> str:
    """Combine fixed + search clauses thành SQL hoàn chỉnh."""
    all_clauses = list(fixed_clauses)
    if search_clause:
        all_clauses.append(search_clause)

    where = " AND ".join(all_clauses) if all_clauses else "1=1"
    return f"""
    SELECT {SELECT_COLUMNS}
    FROM {TABLE_NAME}
    WHERE {where}
    ORDER BY quantity_sold DESC NULLS LAST, sale_price ASC
    LIMIT 20
    """


# ═══════════════════════════════════════════════
# 3-Tier Cascading Search
# ═══════════════════════════════════════════════
async def _price_relaxed_search(req: TagSearchInput, db, multiplier: float) -> list:
    """Tìm với giá nới (nhân multiplier), giữ nguyên product_line + gender."""
    if req.price_max is None:
        return []
    new_max = int(req.price_max * multiplier)
    saved_max = req.price_max
    saved_min = req.price_min
    req.price_max = new_max
    req.price_min = None  # bỏ min để rộng hơn
    params = []
    fixed = _build_fixed_clauses(req, params)
    sql = _build_full_query(fixed, None)
    products = await db.execute_query_async(sql, params=tuple(params))
    req.price_max = saved_max
    req.price_min = saved_min
    return products


async def _cascading_search(req: TagSearchInput, db) -> tuple[list, int, str | None]:
    """
    Cascading search với fallback message khi nới giá.

    Tầng 1: CỐ ĐỊNH + keywords
    Tầng 2: CỐ ĐỊNH + tags
    Tầng 3: CHỈ CỐ ĐỊNH (product_line + gender + price)
    Tầng 4: Drop gender
    Tầng 5: Nới giá 1.5x (còn giữ product_line)
    Tầng 6: Nới giá 2x
    Tầng 7: Bỏ price_max hoàn toàn

    Returns: (products, tier_matched, fallback_message | None)
    """

    # ---- Tầng 1: CỐ ĐỊNH + KEYWORDS ----
    if req.keywords:
        params = []
        fixed = _build_fixed_clauses(req, params)
        search = _build_search_clause(req.keywords, params)
        if search:
            sql = _build_full_query(fixed, search)
            products = await db.execute_query_async(sql, params=tuple(params))
            if products:
                logger.info("🎯 Tầng 1 (keywords) match! %d kết quả", len(products))
                return products, 1, None

    # ---- Tầng 2: CỐ ĐỊNH + TAGS ----
    if req.tags:
        params = []
        fixed = _build_fixed_clauses(req, params)
        search = _build_search_clause(req.tags, params, resolve_tags=True)
        if search:
            sql = _build_full_query(fixed, search)
            products = await db.execute_query_async(sql, params=tuple(params))
            if products:
                logger.info("🎯 Tầng 2 (tags chung) match! %d kết quả", len(products))
                return products, 2, None

    # ---- Tầng 3: CHỈ CỐ ĐỊNH ----
    params = []
    fixed = _build_fixed_clauses(req, params)
    sql = _build_full_query(fixed, None)
    products = await db.execute_query_async(sql, params=tuple(params))
    if products:
        logger.info("🎯 Tầng 3 (chỉ cố định) match! %d kết quả", len(products))
        return products, 3, None
    logger.info("🎯 Tầng 3 (chỉ cố định) match! 0 kết quả")

    # ---- Tầng 4: DROP GENDER, chỉ giữ product_line ----
    # VD: "sịp nam" → gender=men returns 0 → try again without gender
    if req.product_line_vn and req.gender_by_product:
        logger.info("🔄 Tầng 4: thử drop gender '%s' để tìm rộng hơn...", req.gender_by_product)
        saved_gender = req.gender_by_product
        req.gender_by_product = None  # drop gender
        params = []
        fixed = _build_fixed_clauses(req, params)
        sql = _build_full_query(fixed, None)
        products = await db.execute_query_async(sql, params=tuple(params))
        req.gender_by_product = saved_gender  # restore
        if products:
            logger.info("🎯 Tầng 4 (drop gender) match! %d kết quả", len(products))
            return products, 4, None
    # ---- Tầng 5-7: PRICE RELAXATION (chỉ khi có price_max) ----
    if req.price_max is not None and req.product_line_vn:
        original_max = req.price_max
        product_label = "/".join(req.product_line_vn)

        for multiplier, tier, label in [
            (1.5, 5, "1.5x"),
            (2.0, 6, "2x"),
        ]:
            new_max = int(original_max * multiplier)
            logger.info("🔄 Tầng %d: nới giá %s → %d (gốc: %d)...", tier, label, new_max, original_max)
            products = await _price_relaxed_search(req, db, multiplier)
            if products:
                cheapest_p = min(products, key=lambda p: float(p.get("sale_price") or 999999999))
                cheapest = float(cheapest_p.get("sale_price") or 0)
                cheapest_name = cheapest_p.get("product_name", "")
                diff = int(cheapest - original_max)
                msg = (
                    f"FALLBACK_PRICE: Không có {product_label} dưới {original_max:,.0f}đ. "
                    f"Mẫu rẻ nhất: \"{cheapest_name}\" giá {cheapest:,.0f}đ "
                    f"(chỉ thêm {diff:,}đ so với budget). "
                    f"Tổng có {len(products)} mẫu trong tầm {new_max:,.0f}đ. "
                    f"→ HÃY gợi ý SP này cho khách và nói khéo: thêm chút xíu là có mẫu rất đẹp!"
                )
                logger.info("🎯 Tầng %d (price %s) match! %d kết quả | cheapest=%s %d", tier, label, len(products), cheapest_name, cheapest)
                return products, tier, msg

        # Tầng 7: bỏ price hoàn toàn
        logger.info("🔄 Tầng 7: bỏ price_max hoàn toàn...")
        saved_max = req.price_max
        saved_min = req.price_min
        req.price_max = None
        req.price_min = None
        params = []
        fixed = _build_fixed_clauses(req, params)
        sql = _build_full_query(fixed, None)
        products = await db.execute_query_async(sql, params=tuple(params))
        req.price_max = saved_max
        req.price_min = saved_min
        if products:
            cheapest_p = min(products, key=lambda p: float(p.get("sale_price") or 999999999))
            cheapest = float(cheapest_p.get("sale_price") or 0)
            cheapest_name = cheapest_p.get("product_name", "")
            msg = (
                f"FALLBACK_PRICE: Không có {product_label} dưới {original_max:,.0f}đ. "
                f"Mẫu rẻ nhất hiện có: \"{cheapest_name}\" giá {cheapest:,.0f}đ. "
                f"Tổng có {len(products)} mẫu. "
                f"→ HÃY gợi ý SP này và nói: budget thêm chút là có mẫu rất đáng!"
            )
            logger.info("🎯 Tầng 7 (no price limit) match! %d kết quả | cheapest=%s %d", len(products), cheapest_name, cheapest)
            return products, 7, msg

    return [], 3, None


async def _format_products(products: list, db) -> list[dict]:
    """Format output cho AI đọc, đồng thời TỰ ĐỘNG TRA CỨU thông tin suggest/similar items."""
    formatted = []
    
    # 1. Thu thập TẤT CẢ sku từ suggest_items và similar_items
    all_related_skus = set()
    raw_parsed_map = {}
    
    def parse_item_list(raw_val) -> list[str]:
        if not raw_val:
            return []
        if isinstance(raw_val, list):
            return [str(x) for x in raw_val[:3]]
        if isinstance(raw_val, str):
            try:
                parsed = json.loads(raw_val)
                if isinstance(parsed, list):
                    return [str(x) for x in parsed[:3]]
            except:
                pass
        return []

    for p in products[:20]:
        sku = p.get("magento_ref_code", "")
        # Lấy tối đa 2 gợi ý cho mỗi loại để tránh query quá dài
        sug_skus = parse_item_list(p.get("suggest_items"))[:2]
        sim_skus = parse_item_list(p.get("similar_items"))[:2]
        
        raw_parsed_map[sku] = {
            "suggest": sug_skus,
            "similar": sim_skus
        }
        all_related_skus.update(sug_skus)
        all_related_skus.update(sim_skus)

    # 2. BATCH QUERY lấy thông tin cho các mã liên quan
    sku_lookup = {}
    if all_related_skus:
        # Lấy cơ bản: mã, tên, giá
        phs = ", ".join(["%s"] * len(all_related_skus))
        q = f"SELECT magento_ref_code, product_name, sale_price FROM {TABLE_NAME} WHERE magento_ref_code IN ({phs})"
        related_products = await db.execute_query_async(q, params=tuple(all_related_skus))
        
        for rp in related_products:
            rpsku = rp.get("magento_ref_code")
            if rpsku:
                sku_lookup[rpsku] = {
                    "sku": rpsku,
                    "name": rp.get("product_name"),
                    "price": int(rp.get("sale_price") or 0)
                }

    # 3. Build kết quả cuối cùng (trả object thay vì raw SKU string)
    for p in products[:20]:
        sale = float(p.get("sale_price") or 0)
        orig = float(p.get("original_price") or 0)
        has_discount = sale < orig and orig > 0
        disc_pct = int(p.get("discount_percent") or 0)
        desc = (p.get("description_text") or "").strip()
        desc_short = desc[:300] + "..." if len(desc) > 300 else desc
        
        main_sku = p.get("magento_ref_code", "")
        parsed_rel = raw_parsed_map.get(main_sku, {"suggest": [], "similar": []})
        
        # Ánh xạ từ SKU string sang object {sku, name, price}
        rich_suggest = [sku_lookup[s] for s in parsed_rel["suggest"] if s in sku_lookup]
        rich_similar = [sku_lookup[s] for s in parsed_rel["similar"] if s in sku_lookup]

        formatted.append({
            "sku": main_sku,
            "name": p.get("product_name", ""),
            "price": int(sale),
            "original_price": int(orig),
            "discount": f"-{disc_pct}%" if has_discount else None,
            "color": p.get("master_color", ""),
            "gender": p.get("gender_by_product", ""),
            "product_line": p.get("product_line_vn", ""),
            "image": p.get("product_image_url_thumbnail", ""),
            "url": p.get("product_web_url", ""),
            "sizes": p.get("size_scale", ""),
            "description": desc_short,
            "suggest_items": rich_suggest,  # Đã dịch ra {sku, name, price}
            "similar_items": rich_similar,  # Đã dịch ra {sku, name, price}
        })
    return formatted


# ═══════════════════════════════════════════════
# SKU Lookup (đặc biệt, bypass cascade)
# ═══════════════════════════════════════════════
def _build_sku_query(code: str) -> tuple[str, list]:
    """Build query tìm theo mã SKU."""
    code = code.strip().upper()
    sql = f"""
    SELECT {SELECT_COLUMNS}
    FROM {TABLE_NAME}
    WHERE (UPPER(magento_ref_code) = %s
           OR UPPER(internal_ref_code) = %s
           OR UPPER(magento_ref_code) LIKE %s)
    ORDER BY quantity_sold DESC NULLS LAST
    LIMIT 20
    """
    return sql, [code, code, f"{code}%"]


# ═══════════════════════════════════════════════
# Tool Entry Point
# ═══════════════════════════════════════════════
@tool(args_schema=TagSearchInput)
async def tag_search_tool(
    keywords: list[str] | None = None,
    tags: list[str] | None = None,
    product_line_vn: list[str] | None = None,
    gender_by_product: str | None = None,
    age_by_product: str | None = None,
    master_color: str | None = None,
    price_min: int | None = None,
    price_max: int | None = None,
    discount_min: int | None = None,
    discount_max: int | None = None,
    discovery_mode: str | None = None,
    size: str | None = None,
    magento_ref_code: str | None = None,
    reasoning: str | None = None,
) -> str:
    """
    Tìm kiếm sản phẩm CANIFA.
    Ưu tiên keywords (từ user) trước, nếu không có thì dùng tags (AI suy luận).
    Hệ thống tự động fallback qua 7 tầng.
    """
    start = time.time()

    req = TagSearchInput(
        keywords=keywords or [],
        tags=tags or [],
        product_line_vn=product_line_vn or [],
        gender_by_product=gender_by_product,
        age_by_product=age_by_product,
        master_color=master_color,
        price_min=price_min,
        price_max=price_max,
        discount_min=discount_min,
        discount_max=discount_max,
        discovery_mode=discovery_mode,
        size=size,
        magento_ref_code=magento_ref_code,
    )

    db = get_db_connection()

    try:
        fallback_msg = None
        if req.magento_ref_code:
            sql, params = _build_sku_query(req.magento_ref_code)
            products = await db.execute_query_async(sql, params=tuple(params))
            tier = 0
        else:
            # cascading search → returns (products, tier, fallback_message)
            products, tier, fallback_msg = await _cascading_search(req, db)

        elapsed_ms = round((time.time() - start) * 1000, 2)

        logger.info(
            "🏷️ [SEARCH] keywords=%s, tags=%s, type=%s, color=%s, gender=%s | tier=%d, results=%d, time=%.2fms",
            keywords, tags, product_line_vn, master_color, gender_by_product,
            tier, len(products), elapsed_ms,
        )

        formatted = await _format_products(products, db)

        result = {
            "status": "success",
            "count": len(products),
            "tier": tier,
            "elapsed_ms": elapsed_ms,
            "reasoning": reasoning or "",
            "keywords_used": keywords or [],
            "tags_used": tags or [],
            "products": formatted,
        }
        # Thêm fallback_message nếu có (giá nới) — Stylist đọc và upsell tự nhiên
        if fallback_msg:
            result["fallback_message"] = fallback_msg
        return json.dumps(result, ensure_ascii=False, default=str)

    except Exception as e:
        logger.error("❌ Search tool error: %s", e)
        return json.dumps({"status": "error", "message": str(e)})
