"""
Bulk Operations API v2 — Search in clean_description + AI-powered bulk field editing.
Integrates with Codex (Groq LLM) for context-aware content generation per product.
"""

import asyncio
import json
import logging

import httpx
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from langchain_core.messages import HumanMessage, SystemMessage
from pydantic import BaseModel, Field

from common.llm_factory import create_llm


class AiSearchFilter(BaseModel):
    keywords: list[str] = Field(default=[], description="Từ khóa tìm kiếm trong mô tả (VD: 'cotton', 'co giãn')")
    product_line: str | None = Field(default=None, description="Dòng sản phẩm xuất hiện trong yêu cầu (VD: 'Áo phông', 'Quần jean')")
    gender: str | None = Field(default=None, description="Giới tính: Nam, Nữ, Unisex, Bé trai, Bé gái")
    age_group: str | None = Field(default=None, description="Độ tuổi: adult, kid")
    color: str | None = Field(default=None, description="Màu sắc (VD: 'đỏ', 'xanh navy')")
    season: str | None = Field(default=None, description="Mùa: Xuân, Hè, Thu, Đông")
    occasion: str | None = Field(default=None, description="Dịp đặc biệt (VD: '2/9', 'đi biển')")
    search_explain: str = Field(default="", description="Giải thích ngắn gọn cách AI hiểu query")

from common.cache import redis_cache
from common.pool_wrapper import get_pooled_connection_compat


class GeneratedTags(BaseModel):
    tags: list[str] = Field(description="Mảng chứa các thẻ tags sau khi lọc")

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/bulk", tags=["Bulk Operations"])

PG_TABLE = "dashboard_canifa.ultra_descriptions"

# ═══ Groq Config — Reuse from product_desc_route ═══
GROQ_API_KEYS = [
    "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",
    "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",
    "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",
    "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",
    "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",
    "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",
]
_groq_key_index = 0

AI_MODEL = "openai/gpt-oss-120b"

# ═══ Field Labels for dropdown ═══
FIELD_OPTIONS = {
    "dip_mac": "Dịp mặc",
    "phoi_do": "Phối đồ",
    "mo_ta_chinh": "Mô tả chính",
    "phong_cach": "Phong cách",
    "layer": "Gợi ý layering",
    "cross_sell": "Cross-sell",
    "ly_do_mua": "Lý do mua",
    "huong_dan_size": "Hướng dẫn chọn size",
    "luu_y_size": "Lưu ý size",
    "hook_quang_cao": "Hook quảng cáo",
    "tinh_nang_vai": "Tính năng vải",
    "tags": "Tags",
    "mua": "Mùa phù hợp",
    "nguyen_tac_phoi_do": "Nguyên tắc phối đồ",
    "tranh_phoi_cung": "Tránh phối cùng",
    "loi_song": "Lối sống",
    "tinh_cach": "Tính cách",
    "tagline": "Tagline",
    "faq_1_q": "FAQ 1 - Câu hỏi",
    "faq_1_a": "FAQ 1 - Trả lời",
    "faq_2_q": "FAQ 2 - Câu hỏi",
    "faq_2_a": "FAQ 2 - Trả lời",
    "faq_3_q": "FAQ 3 - Câu hỏi",
    "faq_3_a": "FAQ 3 - Trả lời",
}

# ═══ Section grouping for re-rendering clean_description ═══
_FIELD_LABELS = {
    "ten_san_pham": "Tên sản phẩm", "tagline": "Tagline", "mo_ta_chinh": "Mô tả chính",
    "chat_lieu": "Chất liệu", "tinh_nang_vai": "Tính năng vải",
    "huong_dan_bao_quan": "Hướng dẫn bảo quản", "phong_cach": "Phong cách", "tags": "Tags",
    "do_tuoi": "Độ tuổi", "gioi_tinh": "Giới tính", "loi_song": "Lối sống",
    "tinh_cach": "Tính cách", "dip_mac": "Dịp mặc", "phoi_do": "Gợi ý phối đồ",
    "nguyen_tac_phoi_do": "Nguyên tắc phối đồ", "tranh_phoi_cung": "Tránh phối cùng",
    "layer": "Gợi ý layering", "mua": "Mùa phù hợp",
    "faq_1_q": "FAQ 1 - Câu hỏi", "faq_1_a": "FAQ 1 - Trả lời",
    "faq_2_q": "FAQ 2 - Câu hỏi", "faq_2_a": "FAQ 2 - Trả lời",
    "faq_3_q": "FAQ 3 - Câu hỏi", "faq_3_a": "FAQ 3 - Trả lời",
    "hook_quang_cao": "Hook quảng cáo", "cross_sell": "Cross-sell",
    "ly_do_mua": "Lý do nên mua", "luu_y_size": "Lưu ý chọn size", "huong_dan_size": "Hướng dẫn chọn size",
}

_SECTIONS = [
    ("📦 THÔNG TIN CƠ BẢN", ["ten_san_pham", "tagline", "mo_ta_chinh", "phong_cach", "tags"]),
    ("🧵 CHẤT LIỆU & BẢO QUẢN", ["chat_lieu", "tinh_nang_vai", "huong_dan_bao_quan"]),
    ("🎯 ĐỐI TƯỢNG", ["do_tuoi", "gioi_tinh", "loi_song", "tinh_cach"]),
    ("📅 DỊP MẶC & STYLING", ["dip_mac", "phoi_do", "nguyen_tac_phoi_do", "tranh_phoi_cung", "layer", "mua"]),
    ("❓ FAQ", ["faq_1_q", "faq_1_a", "faq_2_q", "faq_2_a", "faq_3_q", "faq_3_a"]),
    ("🛒 HỖ TRỢ BÁN HÀNG", ["hook_quang_cao", "cross_sell", "ly_do_mua", "huong_dan_size", "luu_y_size"]),
]


# ═══ Batch AI Edit State ═══
BULK_AI_STATE = {
    "is_running": False,
    "total": 0,
    "done": 0,
    "errors": 0,
    "current_code": None,
    "current_name": None,
    "results": [],  # last results for display
}


# ═══════════════════════════════════════════════════════
# HELPERS
# ═══════════════════════════════════════════════════════

def _next_groq_key() -> str:
    global _groq_key_index
    key = GROQ_API_KEYS[_groq_key_index % len(GROQ_API_KEYS)]
    _groq_key_index += 1
    return key


async def _call_codex(messages: list, max_tokens: int = 1000, temperature: float = 0.7) -> str:
    """Call Groq Codex with multi-key round-robin and model fallback."""
    model_chain = [AI_MODEL, "openai/gpt-oss-20b", "qwen/qwen3-32b"]

    for attempt_round in range(3):  # max 3 full rounds
        for model in model_chain:
            for _ in range(len(GROQ_API_KEYS)):
                api_key = _next_groq_key()
                try:
                    async with httpx.AsyncClient(timeout=25) as client:
                        resp = await client.post(
                            "https://api.groq.com/openai/v1/chat/completions",
                            headers={
                                "Authorization": f"Bearer {api_key}",
                                "Content-Type": "application/json",
                            },
                            json={
                                "model": model,
                                "max_tokens": max_tokens,
                                "temperature": temperature,
                                "messages": messages,
                            },
                        )
                        if resp.status_code == 200:
                            result = resp.json()
                            if "error" not in result:
                                return result["choices"][0]["message"]["content"]
                        elif resp.status_code == 429:
                            continue  # next key
                        else:
                            break  # next model
                except Exception as e:
                    logger.warning(f"Codex error: {e}")
                    break

        await asyncio.sleep(3)

    raise Exception("Codex: tất cả model và key đều thất bại")


def _render_description_text(data: dict, product_name: str = "") -> str:
    """Re-render clean_description from description_data JSON."""
    if not data:
        data = {}
    lines = []
    title = data.get("ten_san_pham", product_name or "Sản phẩm")
    lines.append(f"{'=' * 50}")
    lines.append(f"  {title.upper()}")
    lines.append(f"{'=' * 50}")
    lines.append("")

    for section_title, fields in _SECTIONS:
        section_lines = []
        for key in fields:
            val = data.get(key, "")
            if not val:
                continue
            label = _FIELD_LABELS.get(key, key)
            if key.endswith("_q"):
                section_lines.append(f"  💬 {val}")
                continue
            elif key.endswith("_a"):
                section_lines.append(f"     → {val}")
                continue
            if key == "phoi_do" and "|" in str(val):
                section_lines.append(f"  {label}:")
                for combo in str(val).split("|"):
                    section_lines.append(f"    • {combo.strip()}")
                continue
            if key == "ly_do_mua" and "|" in str(val):
                section_lines.append(f"  {label}:")
                for reason in str(val).split("|"):
                    section_lines.append(f"    • {reason.strip()}")
                continue
            section_lines.append(f"  {label}: {val}")

        if section_lines:
            lines.append(f"── {section_title} ──")
            lines.extend(section_lines)
            lines.append("")
        else:
            lines.append(f"── {section_title} ──")
            lines.append("  Chưa có dữ liệu.")
            lines.append("")

    return "\n".join(lines)


# ═══════════════════════════════════════════════════════
# 1. SEARCH — In clean_description (PostgreSQL)
# ═══════════════════════════════════════════════════════

class BulkSearchRequest(BaseModel):
    keyword: str
    limit: int = 100


@router.post("/search", summary="Search products by keyword in clean_description")
async def bulk_search(req: BulkSearchRequest):
    """Search in clean_description using LIKE. Primary search for AI assistant."""
    keyword = req.keyword.strip()
    if not keyword or len(keyword) < 2:
        return JSONResponse(
            status_code=400,
            content={"status": "error", "message": "Keyword phải có ít nhất 2 ký tự"}
        )

    conn = None
    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()
        cur.execute(
            f"""SELECT internal_ref_code, product_name, product_image_url, product_line,
                       phase, status, 
                       LEFT(clean_description, 300) AS description_preview
                FROM {PG_TABLE}
                WHERE LOWER(clean_description) LIKE %s
                   OR LOWER(product_name) LIKE %s
                ORDER BY updated_at DESC
                LIMIT %s""",
            (f"%{keyword.lower()}%", f"%{keyword.lower()}%", req.limit),
        )
        cols = [desc[0] for desc in cur.description]
        results = [dict(zip(cols, row)) for row in cur.fetchall()]
        cur.close()

        return {
            "status": "success",
            "keyword": keyword,
            "count": len(results),
            "results": results,
        }

    except Exception as e:
        logger.error(f"Bulk search error: {e}", exc_info=True)
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
    finally:
        if conn:
            conn.close()


# ═══════════════════════════════════════════════════════
# 1B. AI SEARCH — Agent-style intent parsing + smart SQL
# ═══════════════════════════════════════════════════════

# Canifa product lines for AI context (fallback static list)
CANIFA_LINES = [
    "Áo phông", "Quần soóc", "Áo nỉ", "Váy liền", "Quần nỉ", "Tất", "Áo len",
    "Bộ mặc nhà", "Bộ quần áo", "Áo Polo", "Chân váy", "Áo nỉ có mũ", "Áo Sơ mi",
    "Quần jean", "Quần dài", "Áo khoác chần bông", "Quần mặc nhà", "Áo Body",
    "Quần Khaki", "Áo khoác gió", "Áo khoác dáng ngắn", "Áo kiểu", "Áo len gilet",
    "Áo khoác gilet chần bông", "Áo giữ nhiệt", "Áo mặc nhà", "Quần lót", "Pyjama",
    "Áo ba lỗ", "Cardigan", "Bộ thể thao", "Áo khoác chống nắng", "Quần váy",
    "Blazer", "Áo khoác", "Áo khoác lông vũ", "Quần leggings", "Áo hai dây",
]

# Attempt to dynamically fetch product lines from DB to override the static list
# Removed dynamic loading on import to prevent blocking thread when DB is slow
# try:
#     _conn = get_pooled_connection_compat()
#     _cur = _conn.cursor()
#     _cur.execute(f"SELECT DISTINCT product_line FROM {PG_TABLE} WHERE product_line IS NOT NULL AND product_line != ''")
#     _db_lines = [r[0] for r in _cur.fetchall()]
#     _cur.close()
#     _conn.close()
#     if _db_lines:
#         CANIFA_LINES = _db_lines
#         logger.info(f"✅ Loaded {len(CANIFA_LINES)} product lines dynamically from DB")
# except Exception as e:
#     logger.warning(f"⚠️ Could not load product lines from DB, using fallback. Error: {e}")

AI_SEARCH_SYSTEM = """Bạn là AI phân tích ý định tìm kiếm sản phẩm thời trang Canifa.

Nhiệm vụ: Phân tích câu nhập của người dùng và trích xuất thành JSON filter.

DANH SÁCH DÒNG SẢN PHẨM HỢP LỆ:
""" + ", ".join(CANIFA_LINES) + """

TRƯỜNG CÓ THỂ TRÍCH XUẤT:
{
  "keywords": ["từ khóa tìm trong mô tả"],     // VD: ["cotton", "co giãn"]
  "product_line": "Dòng SP từ danh sách trên",  // VD: "Áo Polo" (chỉ chọn từ list)
  "gender": "Nam|Nữ|Unisex|Bé trai|Bé gái",    // null nếu không rõ
  "age_group": "adult|kid",                      // null nếu không rõ
  "color": "màu sắc",                           // VD: "đỏ", "xanh navy"
  "season": "Xuân|Hè|Thu|Đông",                 // null nếu không rõ
  "occasion": "dịp mặc đặc biệt",              // VD: "2/9", "Tết", "đi làm" — dùng cho step bulk edit sau
  "search_explain": "giải thích ngắn gọn cách hiểu câu query"
}

QUY TẮC:
- Chỉ điền trường nào có thể suy ra từ câu nhập. Không bịa.
- "bé trai" → gender: "Nam", age_group: "kid"
- "bé gái" → gender: "Nữ", age_group: "kid"  
- "trẻ em" → age_group: "kid"
- "quần bò" → product_line: "Quần jean"
- "sịp" → product_line: "Quần lót"
- "áo đỏ" → color: "đỏ", keywords chứa product_line nếu nói rõ loại áo
- Nếu người dùng nói "cotton", "kháng khuẩn" → đó là keywords tìm trong mô tả
- Trả về ĐÚNG JSON, không text rác.
"""


class AiSearchRequest(BaseModel):
    query: str
    limit: int = 100


def _build_ai_search_sql(filters: dict, limit: int, use_jsonb: bool = True) -> tuple[str, list, str]:
    """
    Build SQL from AI-parsed filters.
    Supports 2 modes:
      - JSONB mode (strict): queries description_data JSONB fields directly
      - TEXT mode (relaxed): falls back to LIKE on clean_description
    Returns: (sql_string, params_list, search_mode_label)
    """
    clauses = []
    params = []

    # ── Product line → always use dedicated columns ──
    product_line = filters.get("product_line")
    if product_line:
        clauses.append("(LOWER(product_name) LIKE %s OR LOWER(product_line) LIKE %s)")
        params.append(f"%{product_line.lower()}%")
        params.append(f"%{product_line.lower()}%")

    if use_jsonb:
        # ═══ JSONB STRUCTURED FILTERS ═══
        # Gender → description_data->>'gioi_tinh'
        gender = filters.get("gender")
        if gender:
            gender_map = {
                "nam": "Nam", "bé trai": "Nam", "men": "Nam", "boy": "Nam",
                "nữ": "Nữ", "bé gái": "Nữ", "women": "Nữ", "girl": "Nữ",
                "unisex": "Unisex",
            }
            mapped = gender_map.get(gender.lower(), gender)
            if mapped in ("Nam", "Nữ"):
                clauses.append(
                    "(description_data->>'gioi_tinh' ILIKE %s OR description_data->>'gioi_tinh' ILIKE %s)"
                )
                params.extend([f"%{mapped}%", "%Unisex%"])
            else:
                clauses.append("description_data->>'gioi_tinh' ILIKE %s")
                params.append(f"%{mapped}%")

        # Age group → description_data->>'do_tuoi'
        age_group = filters.get("age_group")
        if age_group and age_group == "kid":
            clauses.append(
                "(description_data->>'do_tuoi' ILIKE %s OR description_data->>'do_tuoi' ILIKE %s "
                "OR LOWER(product_line) LIKE %s)"
            )
            params.extend(["%trẻ%", "%bé%", "%bé%"])

        # Color → LIKE in clean_description (color is spread across text, not a single JSONB field)
        color = filters.get("color")
        if color:
            clauses.append("LOWER(clean_description) LIKE %s")
            params.append(f"%{color.lower()}%")

        # Season → description_data->>'mua'
        season = filters.get("season")
        if season:
            clauses.append("description_data->>'mua' ILIKE %s")
            params.append(f"%{season}%")

        # Keywords → LIKE in clean_description (for material keywords like "cotton", "co giãn")
        keywords = filters.get("keywords", [])
        if keywords:
            kw_parts = []
            for kw in keywords[:5]:
                kw_parts.append("LOWER(clean_description) LIKE %s")
                params.append(f"%{kw.lower()}%")
            clauses.append(f"({' AND '.join(kw_parts)})")

        mode_label = "JSONB structured"
    else:
        # ═══ TEXT FALLBACK (relaxed) ═══
        gender = filters.get("gender")
        if gender:
            gender_variants = set()
            gl = gender.lower()
            if gl in ("nam", "bé trai"):
                gender_variants.update(["nam", "unisex"])
            elif gl in ("nữ", "bé gái"):
                gender_variants.update(["nữ", "unisex"])
            else:
                gender_variants.add(gl)
            gparts = []
            for g in gender_variants:
                gparts.append("LOWER(clean_description) LIKE %s")
                params.append(f"%{g}%")
            if gparts:
                clauses.append(f"({' OR '.join(gparts)})")

        age_group = filters.get("age_group")
        if age_group and age_group == "kid":
            clauses.append(
                "(LOWER(clean_description) LIKE %s OR LOWER(clean_description) LIKE %s)"
            )
            params.extend(["%trẻ em%", "%bé%"])

        color = filters.get("color")
        if color:
            clauses.append("LOWER(clean_description) LIKE %s")
            params.append(f"%{color.lower()}%")

        season = filters.get("season")
        if season:
            clauses.append("LOWER(clean_description) LIKE %s")
            params.append(f"%{season.lower()}%")

        keywords = filters.get("keywords", [])
        if keywords:
            kw_parts = []
            for kw in keywords[:5]:
                kw_parts.append("LOWER(clean_description) LIKE %s")
                params.append(f"%{kw.lower()}%")
            clauses.append(f"({' AND '.join(kw_parts)})")

        mode_label = "TEXT fallback"

    where_str = " AND ".join(clauses) if clauses else "1=1"

    sql = f"""SELECT internal_ref_code, product_name, product_image_url, product_line,
                     phase, status,
                     LEFT(clean_description, 300) AS description_preview
              FROM {PG_TABLE}
              WHERE {where_str}
              ORDER BY updated_at DESC
              LIMIT %s"""
    params.append(limit)

    return sql, params, mode_label


@router.post("/ai-search", summary="AI-powered search: parse intent → smart SQL filters")
async def ai_search(req: AiSearchRequest):
    """
    Agent-style search (v2 — JSONB structured filters):
    1. Send user query to structured Gemini endpoint → extract filters
    2. Build multi-condition SQL using JSONB operators on description_data
    3. Progressive relaxation: if JSONB returns 0 → retry with TEXT fallback
    4. Return matched products + explanation of how AI understood the query
    """
    query = req.query.strip()
    if not query or len(query) < 2:
        return JSONResponse(
            status_code=400,
            content={"status": "error", "message": "Query phải có ít nhất 2 ký tự"}
        )

    try:
        # ── Step 1: AI Parse Intent (Structured) ──
        logger.info(f"🧠 AI Search (Structured): parsing '{query}'...")
        
        # Use robust Gemini model with structured output
        llm = create_llm("gemini-3.1-flash-lite-preview", streaming=False)
        structured_llm = llm.with_structured_output(AiSearchFilter)
        
        messages = [
            SystemMessage(content=AI_SEARCH_SYSTEM),
            HumanMessage(content=f"Phân tích câu tìm kiếm: \"{query}\""),
        ]

        result: AiSearchFilter = await structured_llm.ainvoke(messages)
        filters = result.model_dump(exclude_none=True)
        
        if "keywords" not in filters or filters["keywords"] is None:
            filters["keywords"] = []

        logger.info(f"🧠 AI Filters: {json.dumps(filters, ensure_ascii=False)}")

        # ── Step 2+3: Build SQL → Execute with progressive relaxation ──
        results = []
        search_mode = "JSONB structured"

        conn = None
        try:
            conn = get_pooled_connection_compat()
            cur = conn.cursor()

            # Tier 1: JSONB structured query (strict, precise)
            sql, params, search_mode = _build_ai_search_sql(filters, req.limit, use_jsonb=True)
            logger.info(f"🔍 Tier 1 ({search_mode}): {sql[:120]}...")
            cur.execute(sql, tuple(params))
            cols = [desc[0] for desc in cur.description]
            results = [dict(zip(cols, row)) for row in cur.fetchall()]

            # Tier 2: TEXT fallback if JSONB returned too few results
            if len(results) < 3:
                logger.info(f"⚠️ Tier 1 returned {len(results)} results, trying TEXT fallback...")
                sql2, params2, search_mode = _build_ai_search_sql(filters, req.limit, use_jsonb=False)
                cur.execute(sql2, tuple(params2))
                cols2 = [desc[0] for desc in cur.description]
                text_results = [dict(zip(cols2, row)) for row in cur.fetchall()]

                # Merge: keep existing + add new unique results
                existing_codes = {r["internal_ref_code"] for r in results}
                for r in text_results:
                    if r["internal_ref_code"] not in existing_codes:
                        results.append(r)
                        existing_codes.add(r["internal_ref_code"])
                search_mode = "JSONB + TEXT merged"

            cur.close()
        finally:
            if conn:
                conn.close()

        explain = filters.get("search_explain", "")
        occasion = filters.get("occasion", "")

        logger.info(
            f"✅ AI Search done: query='{query}', mode={search_mode}, "
            f"results={len(results)}, filters={json.dumps(filters, ensure_ascii=False)}"
        )

        return {
            "status": "success",
            "query": query,
            "ai_filters": filters,
            "explain": explain,
            "occasion": occasion,
            "search_mode": search_mode,
            "count": len(results),
            "results": results,
        }

    except Exception as e:
        logger.error(f"AI Search error: {e}", exc_info=True)
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})


# ═══════════════════════════════════════════════════════
# 2. FIELD OPTIONS — For the widget dropdown
# ═══════════════════════════════════════════════════════

@router.get("/fields", summary="Get available fields for bulk AI editing")
async def get_fields():
    return {"status": "success", "fields": FIELD_OPTIONS}


# ═══════════════════════════════════════════════════════
# 3. AI EDIT — Codex-powered bulk field generation
# ═══════════════════════════════════════════════════════

class BulkAiEditRequest(BaseModel):
    codes: list[str] = []
    target_field: str          # which field in description_data to edit
    instruction: str           # Vietnamese instruction for AI
    mode: str = "append"       # append | overwrite
    target_line: str | None = None # Apply to all products in this line


@router.post("/ai-edit", summary="Bulk AI-powered field editing with Codex")
async def bulk_ai_edit(req: BulkAiEditRequest):
    """
    Push Codex-powered mass edit jobs to Redis Queue for the worker.
    """
    if not req.codes and not req.target_line:
        return JSONResponse(status_code=400, content={"status": "error", "message": "Trống codes và target_line"})
        
    codes = req.codes
    if req.target_line:
        conn = get_pooled_connection_compat()
        try:
            cur = conn.cursor()
            cur.execute(f"SELECT internal_ref_code FROM {PG_TABLE} WHERE product_line_vn = %s", (req.target_line,))
            codes = [r[0] for r in cur.fetchall()]
            if not codes:
                return JSONResponse(status_code=400, content={"status": "error", "message": f"Không tìm thấy SP nào thuộc dòng {req.target_line}"})
        finally:
            if conn:
                conn.close()

    if req.target_field not in FIELD_OPTIONS and req.target_field not in _FIELD_LABELS:
        return JSONResponse(status_code=400, content={
            "status": "error",
            "message": f"Trường '{req.target_field}' không hợp lệ. Chọn: {', '.join(FIELD_OPTIONS.keys())}"
        })
    if not req.instruction.strip():
        return JSONResponse(status_code=400, content={"status": "error", "message": "Thiếu instruction"})

    redis = redis_cache.get_client()
    if not redis:
        return JSONResponse(status_code=500, content={"status": "error", "message": "Redis is not available"})
        
    latest_job = await redis.get("ai_edit:progress:latest_job")
    if latest_job:
        status_data = await redis.hgetall(f"ai_edit:progress:{latest_job.decode('utf-8') if isinstance(latest_job, bytes) else latest_job}")
        if status_data and status_data.get(b"is_running", status_data.get("is_running")) in (b"true", "true"):
            return JSONResponse(status_code=409, content={
                "status": "error", "message": "Đang có task AI chạy. Vui lòng đợi hoàn tất."
            })

    import uuid
    job_id = f"ai_edit_{uuid.uuid4().hex[:8]}"
    job_data = {
        "job_id": job_id,
        "codes": codes,
        "target_field": req.target_field,
        "instruction": req.instruction,
        "mode": req.mode
    }
    
    # 🌟 MỚI: Route công việc sang queue chuyên biệt nếu là huong_dan_size
    queue_name = "ai_edit:queue"
    progress_prefix = "ai_edit:progress:"
    if req.target_field == "huong_dan_size":
        queue_name = "size_guide:queue"
        progress_prefix = "size_guide:progress:"

    await redis.lpush(queue_name, json.dumps(job_data))
    await redis.set(f"{progress_prefix}latest_job", job_id)
    
    # Initialize progress state
    await redis.hset(f"{progress_prefix}{job_id}", mapping={
        "is_running": "true",
        "total": len(codes),
        "done": 0,
        "errors": 0,
        "current_code": codes[0] if codes else "",
        "current_name": "",
        "results": "[]"
    })

    return {
        "status": "success",
        "message": f"Đã đẩy {len(codes)} SP vào worker để sinh tự động nội dung AI...",
        "total": len(codes),
    }

@router.post("/ai-edit-preview", summary="Preview AI generated content for a single product")
async def bulk_ai_edit_preview(req: BulkAiEditRequest):
    """
    Run Codex on ONE product and return the old and new text without saving to the DB.
    Provides a quick UI preview before running the background bulk task.
    """
    if not req.codes:
         return JSONResponse(status_code=400, content={"status": "error", "message": "Trống codes"})
         
    code = req.codes[0] # Just take the first one
    target_field = req.target_field
    instruction = req.instruction
    mode = req.mode
    
    field_label = FIELD_OPTIONS.get(target_field, _FIELD_LABELS.get(target_field, target_field))
    
    conn = None
    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()
        cur.execute(
            f"SELECT internal_ref_code, product_name, product_line, description_data FROM {PG_TABLE} WHERE internal_ref_code = %s",
            (code,)
        )
        cols = [desc[0] for desc in cur.description]
        row = cur.fetchone()
        cur.close()
        
        if not row:
            return JSONResponse(status_code=404, content={"status": "error", "message": "Không tìm thấy sản phẩm"})
            
        product = dict(zip(cols, row))
        desc_data = product.get("description_data", {})
        if isinstance(desc_data, str):
            try:
                desc_data = json.loads(desc_data)
            except Exception:
                desc_data = {}
                
        current_value = desc_data.get(target_field, "")
        
        # Build context for AI
        context_fields = {
            "ten_san_pham": desc_data.get("ten_san_pham", product.get("product_name", "")),
            "phong_cach": desc_data.get("phong_cach", ""),
            "chat_lieu": desc_data.get("chat_lieu", ""),
            "gioi_tinh": desc_data.get("gioi_tinh", ""),
            "mua": desc_data.get("mua", ""),
            "do_tuoi": desc_data.get("do_tuoi", ""),
            "product_line": product.get("product_line", ""),
        }
        context_str = "\n".join(f"- {k}: {v}" for k, v in context_fields.items() if v)
        
        if mode == "append" and current_value:
            prompt = f"""Bạn là Stylist thời trang Canifa. Dựa trên thông tin sản phẩm dưới đây, hãy BỔ SUNG thêm nội dung cho trường "{field_label}".

THÔNG TIN SẢN KHẨM:
{context_str}

GIÁ TRỊ HIỆN TẠI của trường "{field_label}":
{current_value}

YÊU CẦU BỔ SUNG:
{instruction}

QUY TẮC:
- GIỮ NGUYÊN nội dung cũ, CHỈ THÊM phần mới vào
- Viết tự nhiên, phù hợp với sản phẩm cụ thể này
- Dùng cùng format/style với nội dung cũ (nếu dùng dấu · thì thêm cũng dùng dấu ·)
- Trả về TOÀN BỘ nội dung trường (cũ + mới), KHÔNG giải thích

Trả về chỉ nội dung trường, không có gì khác."""
        else:
            prompt = f"""Bạn là Stylist thời trang Canifa. Dựa trên thông tin sản phẩm dưới đây, hãy VIẾT nội dung cho trường "{field_label}".

THÔNG TIN SẢN PHẨM:
{context_str}

YÊU CẦU:
{instruction}

QUY TẮC:
- Viết tự nhiên, phù hợp với sản phẩm cụ thể này
- Phong cách giống Stylist thời trang cao cấp
- Trả về chỉ nội dung trường, KHÔNG giải thích

Trả về chỉ nội dung trường, không có gì khác."""
            
        messages = [
            {"role": "system", "content": "Bạn là AI phụ trách nội dung sản phẩm thời trang Canifa. Trả lời ngắn gọn, chỉ trả về nội dung được yêu cầu."},
            {"role": "user", "content": prompt},
        ]

        logger.info(f"👀 Preview for code: {code}")
        ai_output = await _call_codex(messages, max_tokens=500, temperature=0.7)
        new_value = ai_output.strip().strip('"').strip("'")
        
        return {
            "status": "success",
            "code": code,
            "name": product.get("product_name", ""),
            "old_value": current_value,
            "new_value": new_value
        }
    except Exception as e:
        logger.error(f"Preview error: {e}", exc_info=True)
        return {"status": "error", "message": str(e)}
    finally:
        if conn:
            conn.close()





# ═══════════════════════════════════════════════════════
# 4. AI EDIT STATUS — Poll progress
# ═══════════════════════════════════════════════════════

@router.get("/ai-edit-status", summary="Get progress of bulk AI edit")
async def ai_edit_status():
    redis = redis_cache.get_client()
    default_state = {
        "is_running": False,
        "total": 0,
        "done": 0,
        "errors": 0,
        "current_code": None,
        "current_name": None,
        "results": []
    }

    if not redis:
        return default_state
        
    latest_job_id = await redis.get("ai_edit:progress:latest_job")
    if not latest_job_id:
        return default_state
        
    latest_job_id = latest_job_id.decode("utf-8") if isinstance(latest_job_id, bytes) else latest_job_id
    status_data = await redis.hgetall(f"ai_edit:progress:{latest_job_id}")
    if not status_data:
        return default_state
        
    # parse bytes if needed
    status_parsed = {k.decode('utf-8') if isinstance(k, bytes) else k: v.decode('utf-8') if isinstance(v, bytes) else v for k,v in status_data.items()}
    
    import json
    results_str = status_parsed.get("results", "[]")
    try:
        results = json.loads(results_str)
    except:
        results = []
        
    return {
        "is_running": status_parsed.get("is_running") == "true",
        "total": int(status_parsed.get("total", 0)),
        "done": int(status_parsed.get("done", 0)),
        "errors": int(status_parsed.get("errors", 0)),
        "current_code": status_parsed.get("current_code", None),
        "current_name": status_parsed.get("current_name", None),
        "results": results
    }


# ═══════════════════════════════════════════════════════
# 5. SIMPLE BULK UPDATE — Static text (keep from v1)
# ═══════════════════════════════════════════════════════

class BulkUpdateRequest(BaseModel):
    codes: list[str]
    action: str       # append | prepend | replace | remove
    text: str = ""
    find: str = ""
    replace_with: str = ""


@router.post("/update", summary="Bulk update clean_description with static text")
async def bulk_update(req: BulkUpdateRequest):
    """Simple bulk text operations (non-AI). For quick static edits."""
    if not req.codes:
        return JSONResponse(status_code=400, content={"status": "error", "message": "Trống codes"})

    conn = None
    updated = 0
    errors = []

    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()

        for code in req.codes:
            try:
                cur.execute(
                    f"SELECT clean_description FROM {PG_TABLE} WHERE internal_ref_code = %s",
                    (code,)
                )
                row = cur.fetchone()
                if not row:
                    errors.append({"code": code, "error": "Không tìm thấy"})
                    continue

                current_text = row[0] or ""

                if req.action == "append":
                    new_text = current_text + "\n" + req.text
                elif req.action == "prepend":
                    new_text = req.text + "\n" + current_text
                elif req.action == "replace":
                    new_text = current_text.replace(req.find, req.replace_with) if req.find else current_text
                elif req.action == "remove":
                    new_text = current_text.replace(req.text, "")
                else:
                    continue

                cur.execute(
                    f"UPDATE {PG_TABLE} SET clean_description = %s, updated_at = NOW() WHERE internal_ref_code = %s",
                    (new_text.strip(), code)
                )
                updated += 1
            except Exception as e:
                errors.append({"code": code, "error": str(e)})

        cur.close()
        return {
            "status": "success",
            "message": f"Đã cập nhật {updated}/{len(req.codes)} sản phẩm",
            "updated": updated,
            "total": len(req.codes),
            "errors": errors,
        }
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
    finally:
        if conn:
            conn.close()

# ═══════════════════════════════════════════════════════
# 6. AUTO-TAGGING BATCH (Generate tags for products)
# ═══════════════════════════════════════════════════════

TAGS_LIST = [
    # ── TRỤC 1: Màu sắc & Tông màu ──────────────────────────────
    "Màu đen", "Màu trắng", "Màu xám", "Màu xanh navy", "Màu xanh da trời", 
    "Màu be", "Màu nâu", "Màu hồng", "Màu đỏ", "Màu vàng", 
    "Tông trung tính (Neutral)", "Tông màu pastel", "Tông màu rực rỡ", "Tông màu tối", "Tông màu sáng",

    # ── TRỤC 2: Hoàn cảnh & Dịp ──────────────────────────────
    "Đi làm / Công sở", "Đi chơi / dạo phố", "Mặc nhà", "Tập thể thao", 
    "Đi tiệc", "Đi biển", "Du lịch", "Đi học",

    # ── TRỤC 3: Mùa vụ ─────────────────────────────────────
    "Mùa hè", "Mùa đông", "Cả 2 mùa", "Giao mùa",

    # ── TRỤC 4: Phong cách & Tính năng ────────────────────────────────
    "Cơ bản (Basic)", "Năng động", "Thanh lịch", "Dễ thương", "Cá tính", 
    "Tối giản", "Smart Casual", "Thoáng mát", "Giữ ấm", "Thấm hút", 
    "Nhanh khô", "Chống UV", "Cản gió",

    # ── TRỤC 5: Dáng & Kiểu cắt ───────────────────────
    "Oversize", "Slim", "Regular", "Wide leg", "Cropped", "Relaxed",
]

TAGS_SYSTEM_PROMPT = """Bạn là chuyên gia gán tag thời trang cho sản phẩm Canifa.
Từ mô tả sản phẩm (và tên loại sản phẩm), hãy chọn ra tối đa 6 thẻ (tags) tương ứng từ DANH SÁCH TAGS HỢP LỆ.
KHÔNG TỰ CHẾ TAG! Chỉ sử dụng các tags trong DANH SÁCH TAGS HỢP LỆ.
Nếu Tên sản phẩm có màu, ưu tiên trả về tag Màu sắc và Tông màu. Bắt buộc phải có tag Hoàn cảnh & Dịp, Mùa vụ (nếu có thể suy luận).
"""


@router.post("/batch-generate-tags", summary="Start Auto-Tagging for products without tags")
async def batch_generate_tags():
    redis = redis_cache.get_client()
    if not redis:
        return JSONResponse(status_code=500, content={"status": "error", "message": "Redis is not available"})
        
    latest_job = await redis.get("tags:progress:latest_job")
    if latest_job:
        status_data = await redis.hgetall(f"tags:progress:{latest_job}")
        if status_data.get("is_running") == "true":
            return JSONResponse(status_code=400, content={"status": "error", "message": "Đang có tiến trình Auto-Tagging chạy rồi!"})
        
    conn = None
    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()
        # Get up to 500 products that have clean_description (regardless of tags status)
        # This will merge new tags with existing ones
        cur.execute(f"""
            SELECT internal_ref_code
            FROM {PG_TABLE}
            WHERE clean_description IS NOT NULL
              AND BTRIM(clean_description) != ''
            LIMIT 500
        """)
        rows = cur.fetchall()
        
        if not rows:
            return {"status": "success", "message": "Không tìm thấy sản phẩm nào có clean_description để gắn tag!"}

        codes = [r[0] for r in rows]

        import uuid
        job_id = f"batch_{uuid.uuid4().hex[:8]}"
        job_data = {
            "job_id": job_id,
            "codes": codes
        }

        await redis.lpush("tags:queue", json.dumps(job_data))
        await redis.set("tags:progress:latest_job", job_id)

        # Initialize progress state
        await redis.hset(f"tags:progress:{job_id}", mapping={
            "is_running": "true",
            "total": len(codes),
            "done": 0,
            "errors": 0,
            "current_code": codes[0] if codes else ""
        })

        return {"status": "success", "message": f"Đã đẩy {len(codes)} sản phẩm vào hàng đợi để merge tags mới (occasion/season/style/fit) với tags cũ (color palette)"}
    except Exception as e:
        logger.error(f"Error checking untagged products: {e}", exc_info=True)
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
    finally:
        if conn:
            conn.close()


@router.get("/tags-batch-status", summary="Get status of the specific Auto-Tagging progress")
async def tags_batch_status():
    redis = redis_cache.get_client()
    default_state = {"is_running": False, "total": 0, "done": 0, "errors": 0, "current_code": None}

    if not redis:
        return {"progress": default_state}
        
    latest_job_id = await redis.get("tags:progress:latest_job")
    if not latest_job_id:
        return {"progress": default_state}
        
    status_data = await redis.hgetall(f"tags:progress:{latest_job_id}")
    if not status_data:
        return {"progress": default_state}
        
    return {
        "progress": {
            "is_running": status_data.get("is_running") == "true",
            "total": int(status_data.get("total", 0)),
            "done": int(status_data.get("done", 0)),
            "errors": int(status_data.get("errors", 0)),
            "current_code": status_data.get("current_code", None)
        }
    }


class SingleProductTagsRequest(BaseModel):
    internal_ref_code: str | None = None
    magento_ref_code: str | None = None  # FE sends this field


@router.post("/generate-tags-single", summary="Generate tags for a single product via Redis Queue")
async def generate_tags_single(req: SingleProductTagsRequest):
    code = req.magento_ref_code or req.internal_ref_code or ""
    if not code:
        return JSONResponse(status_code=400, content={"status": "error", "message": "Missing internal_ref_code"})

    redis = redis_cache.get_client()
    if not redis:
        return JSONResponse(status_code=500, content={"status": "error", "message": "Redis is not available"})
        
    import uuid
    job_id = f"single_{uuid.uuid4().hex[:8]}"
    job_data = {
        "job_id": job_id,
        "codes": [code]
    }
    
    try:
        from datetime import datetime
        start_time = datetime.now()
        await redis.lpush("tags:queue", json.dumps(job_data))
        
        # Real-time long-polling: Wait up to 15 seconds for the background worker
        conn = get_pooled_connection_compat()
        try:
            cur = conn.cursor()
            for _ in range(15):
                await asyncio.sleep(1)
                cur.execute(f"SELECT tags, updated_at FROM {PG_TABLE} WHERE internal_ref_code = %s", (code,))
                row = cur.fetchone()
                if row:
                    tags_val, updated_at = row
                    # If updated_at is > start_time AND tags is list
                    if updated_at and updated_at > start_time and tags_val is not None:
                        return {"status": "success", "tags": tags_val}
        finally:
            conn.close()
            
        return {"status": "success", "message": f"Đã đẩy {code} vào hàng đợi xử lý Tags bằng 5 OSS Models. Vui lòng đợi trong giây lát và F5/Tải lại trang!"}
    except Exception as e:
        logger.error(f"Error queuing single tags for {code}: {e}", exc_info=True)
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})


# ═══════════════════════════════════════════════════════
# 8. SIZE GUIDE ENRICHMENT — Single + Batch
# ═══════════════════════════════════════════════════════

SIZE_GUIDE_TABLE = "dashboard_canifa.product_size_guide"

class EnrichSizeRequest(BaseModel):
    internal_ref_code: str

class EnrichSizeBatchRequest(BaseModel):
    codes: list[str] = []  # Empty = all matched products


@router.post("/enrich-size-single", summary="AI-generate size guide for a single product")
async def enrich_size_single(req: EnrichSizeRequest):
    """
    1. Fetch product description_data + sizeguide_content from product_size_guide
    2. AI parses HTML size table → generates natural language size guide
    3. Saves to description_data.huong_dan_size + re-renders clean_description
    """
    code = req.internal_ref_code.strip()
    if not code:
        return JSONResponse(status_code=400, content={"status": "error", "message": "Thiếu mã sản phẩm"})

    conn = None
    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()

        # Fetch product + size guide data in one JOIN
        cur.execute(f"""
            SELECT u.product_name, u.product_line, u.description_data, u.size_scale,
                   s.sizeguide_content, s.age_by_product, s.gender_by_product
            FROM {PG_TABLE} u
            LEFT JOIN {SIZE_GUIDE_TABLE} s ON u.internal_ref_code = s.internal_ref_code
            WHERE u.internal_ref_code = %s
        """, (code,))
        row = cur.fetchone()

        if not row:
            return JSONResponse(status_code=404, content={"status": "error", "message": f"Không tìm thấy sản phẩm {code}"})

        name, p_line, desc_data, size_scale, sizeguide_html, age, gender = row

        if isinstance(desc_data, str):
            try: desc_data = json.loads(desc_data)
            except: desc_data = {}
        elif desc_data is None:
            desc_data = {}

        if not sizeguide_html:
            return JSONResponse(status_code=404, content={
                "status": "error",
                "message": f"Sản phẩm {code} không có dữ liệu bảng size từ Magento"
            })

        # Build context
        context_fields = {
            "ten_san_pham": desc_data.get("ten_san_pham", name or ""),
            "phong_cach": desc_data.get("phong_cach", ""),
            "chat_lieu": desc_data.get("chat_lieu", ""),
            "gioi_tinh": desc_data.get("gioi_tinh", gender or ""),
            "mua": desc_data.get("mua", ""),
            "do_tuoi": desc_data.get("do_tuoi", age or ""),
            "product_line": p_line or "",
            "size_scale": json.dumps(size_scale) if size_scale else "[]",
        }
        context_str = "\n".join(f"- {k}: {v}" for k, v in context_fields.items() if v)

        prompt = f"""Bạn là Stylist thời trang chuyên nghiệp của Canifa. Dựa trên thông tin sản phẩm và Bảng thông số size HTML bên dưới, hãy sinh hướng dẫn chọn size tự nhiên, chính xác.

THÔNG TIN SẢN PHẨM:
{context_str}

BẢNG SIZE HTML (Parse từ Magento):
{sizeguide_html[:3000]}

QUY TẮC BẮT BUỘC:
1. BẢNG THÔNG SỐ: Liệt kê các size có sẵn dạng bảng dễ đọc (Size, Chiều cao, Cân nặng, Vòng ngực/mông nếu có).
2. HƯỚNG DẪN CHỌN SIZE THEO CHIỀU CAO: Ví dụ cụ thể (VD: Bé cao 115cm, nặng 22kg → nên chọn Size 110).
3. GỢI Ý MẶC SIZE — PHẦN NÀY RẤT QUAN TRỌNG:
   Dựa vào LOẠI SẢN PHẨM CỤ THỂ (áo phông, quần jean, váy, áo khoác...) + chất liệu + phong cách, đưa ra lời khuyên:
   - Nên chọn đúng size hay tăng/giảm size? Tại sao?
   - VD: "Áo phông trẻ em nên chọn rộng hơn 1 size để bé thoải mái vận động"
   - VD: "Quần jean slim fit nên chọn đúng size, nếu thích thoải mái hơn thì tăng 1 size"
   - VD: "Áo khoác gió nên tăng 1 size nếu muốn layer áo bên trong"
   - VD: "Váy liền dáng xòe có thể chọn đúng size vì form rộng thoải mái"
   - Chất liệu co giãn (cotton, thun) → có thể chọn đúng size
   - Chất liệu ít co giãn (kaki, denim) → nên tăng 1 size
4. LƯU Ý: Kèm lưu ý nhỏ cuối (VD: Số đo có thể chênh 1-2cm tùy co giãn vải).
- FORMAT: Dùng bullet point rõ ràng dễ đọc.
- Trả về chỉ nội dung text, KHÔNG giải thích thêm."""

        messages = [
            {"role": "system", "content": "Bạn là AI phụ trách nội dung sản phẩm thời trang Canifa."},
            {"role": "user", "content": prompt},
        ]

        logger.info(f"📏 Enriching size guide for {code}...")
        ai_output = await _call_codex(messages, max_tokens=1500, temperature=0.7)
        new_value = ai_output.strip().strip('"').strip("'")

        # Save to DB
        desc_data["huong_dan_size"] = new_value
        new_clean = _render_description_text(desc_data, name or "")

        cur.execute(
            f"UPDATE {PG_TABLE} SET description_data = %s::jsonb, clean_description = %s, updated_at = NOW() WHERE internal_ref_code = %s",
            (json.dumps(desc_data, ensure_ascii=False), new_clean, code)
        )
        conn.commit()
        cur.close()

        logger.info(f"✅ Size guide enriched for {code}")
        return {
            "status": "success",
            "code": code,
            "name": name,
            "size_guide": new_value,
            "size_scale": size_scale,
        }

    except Exception as e:
        logger.error(f"Size enrich error for {code}: {e}", exc_info=True)
        if conn: conn.rollback()
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
    finally:
        if conn: conn.close()


@router.post("/enrich-size-batch", summary="Batch AI size guide enrichment for matched products")
async def enrich_size_batch(req: EnrichSizeBatchRequest):
    """Push size enrichment jobs for multiple products to Redis queue."""
    codes = req.codes

    # If no codes provided, find ALL products that need size guide
    # (both with Magento size table AND those with only size_scale)
    if not codes:
        conn = get_pooled_connection_compat()
        try:
            cur = conn.cursor()
            cur.execute(f"""
                SELECT u.internal_ref_code
                FROM {PG_TABLE} u
                LEFT JOIN {SIZE_GUIDE_TABLE} s ON u.internal_ref_code = s.internal_ref_code
                WHERE (u.description_data->>'huong_dan_size' IS NULL
                   OR u.description_data->>'huong_dan_size' = '')
                  AND (s.internal_ref_code IS NOT NULL
                   OR (u.size_scale IS NOT NULL AND u.size_scale != '[]'))
            """)
            codes = [r[0] for r in cur.fetchall()]
            cur.close()
        finally:
            conn.close()

    if not codes:
        return {"status": "success", "message": "Tất cả sản phẩm đã có hướng dẫn size!", "total": 0}

    redis = redis_cache.get_client()
    if not redis:
        return JSONResponse(status_code=500, content={"status": "error", "message": "Redis is not available"})

    import uuid
    job_id = f"size_enrich_{uuid.uuid4().hex[:8]}"
    job_data = {
        "job_id": job_id,
        "codes": codes,
        "instruction": "AUTO_ENRICH_FROM_SIZEGUIDE",
    }

    await redis.lpush("size_guide:queue", json.dumps(job_data))
    await redis.set("size_guide:progress:latest_job", job_id)
    await redis.hset(f"size_guide:progress:{job_id}", mapping={
        "is_running": "true",
        "total": len(codes),
        "done": 0,
        "errors": 0,
    })
    await redis.expire(f"size_guide:progress:{job_id}", 3600)

    return {
        "status": "success",
        "message": f"Đã đẩy {len(codes)} SP vào hàng đợi AI Size Guide Enrichment...",
        "job_id": job_id,
        "total": len(codes),
    }


@router.get("/size-stats", summary="Get size guide statistics")
async def size_stats():
    """Returns statistics about size guide coverage."""
    conn = None
    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()
        cur.execute(f"""
            SELECT
                (SELECT COUNT(*) FROM {PG_TABLE}) AS total_products,
                (SELECT COUNT(*) FROM {PG_TABLE} WHERE size_scale IS NOT NULL AND size_scale != '[]') AS has_size_scale,
                (SELECT COUNT(*) FROM {PG_TABLE} WHERE description_data->>'huong_dan_size' IS NOT NULL AND description_data->>'huong_dan_size' != '') AS has_size_guide,
                (SELECT COUNT(DISTINCT u.internal_ref_code) FROM {PG_TABLE} u JOIN {SIZE_GUIDE_TABLE} s ON u.internal_ref_code = s.internal_ref_code) AS matched_magento,
                (SELECT COUNT(*) FROM {PG_TABLE} u
                    LEFT JOIN {SIZE_GUIDE_TABLE} s ON u.internal_ref_code = s.internal_ref_code
                    WHERE (s.internal_ref_code IS NOT NULL OR (u.size_scale IS NOT NULL AND u.size_scale != '[]'))
                ) AS eligible_products,
                (SELECT COUNT(*) FROM {PG_TABLE} u
                    LEFT JOIN {SIZE_GUIDE_TABLE} s ON u.internal_ref_code = s.internal_ref_code
                    WHERE (u.description_data->>'huong_dan_size' IS NULL OR u.description_data->>'huong_dan_size' = '')
                      AND (s.internal_ref_code IS NOT NULL OR (u.size_scale IS NOT NULL AND u.size_scale != '[]'))
                ) AS needs_enrichment
        """)
        row = cur.fetchone()
        cur.close()
        return {
            "status": "success",
            "total_products": row[0],
            "has_size_scale": row[1],
            "has_size_guide": row[2],
            "matched_products": row[4],  # eligible = matched + has_size_scale
            "matched_magento": row[3],
            "needs_enrichment": row[5],
        }
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
    finally:
        if conn: conn.close()

