# 02 — Smart Outfit Suggestion: Inject gợi ý phối đồ có stock check + metadata đầy đủ

## 📌 Idea
Cải thiện logic `_enrich_with_outfit()` hiện tại: hiện chỉ lấy top 3 SP rồi fetch match từ SQLite.
Idea: mở rộng sang **tất cả SP kết quả** (không chỉ top 3), **deduplicate thông minh theo role**,
và **kèm thêm metadata đầy đủ** (discount, is_new, quantity_sold) cho outfit match để AI tư vấn
chính xác hơn (ưu tiên gợi hàng sale, hàng mới).

## 🗂 Codebase Scope
- **Root:** `d:\cnf\chatbot-canifa-feedback\backend`
- **Affected:**
  - `agent/tag_search_agent_v2/tag_search_tool.py` — hàm `_enrich_with_outfit()`
  - `backend/123.db` → `pg__dashboard_canifa__ai_outfit_product_matches`
  - StarRocks `test_db.magento_product_dimension_with_text_embedding`
- **Languages:** Python 3.12+
- **Size:** S-M

## 📊 Current State Analysis

Hàm `_enrich_with_outfit()` hiện tại (lines 640-733):
```python
top_products = products[:3]   # ← chỉ lấy top 3
```

Và outfit match chỉ inject:
```python
{"sku", "name", "color", "price", "original_price", "url", "image", "role", "ai_reason", "in_stock", "stock"}
```

**Thiếu:**
- `discount` field → Responder không biết outfit match đang sale không
- `is_new_product` → không ưu tiên hàng mới
- `quantity_sold` → không biết outfit match có phổ biến không
- Nếu có 10 SP kết quả, chỉ 3 cái đầu được inject outfit → 7 cái sau không có gợi ý

## 🎯 Goal / Definition of Done
- `top_products = products[:5]` thay vì `[:3]` → tăng coverage
- Outfit match item kèm `discount_percent`, `is_new`, `quantity_sold`
- Deduplicate theo `match_role`: mỗi role (Bottom, Top, Accessories) chỉ giữ 1 best match (score cao nhất)
- Score threshold: chỉ inject match có `score >= 0.7` để tránh gợi ý không liên quan
- Fallback: nếu SQLite lỗi → silently skip, không crash

## 📋 Execution Plan

### Phase 1: Mở rộng top_products (10 phút)
- [ ] Đổi `top_products = products[:3]` → `products[:5]`
- [ ] Thêm comment giải thích lý do

### Phase 2: Deduplicate theo role + score filter (20 phút)
- [ ] Khi build `matches_by_anchor`, group by `match_role`
- [ ] Mỗi role chỉ giữ entry có `score` cao nhất
- [ ] Filter `score >= 0.7` (threshold tunable)
- [ ] `matches_by_anchor[anchor][:3]` → giữ max 3 outfit suggestions (1 per role)

### Phase 3: Enrich outfit match metadata (30 phút)
- [ ] Trong SQL fetch `match_products` từ StarRocks, đảm bảo `SELECT_COLUMNS` đã có:
  - `discount_percent` (calculated field - đã có trong SELECT_COLUMNS)
  - `is_new_product` (đã có)
  - `quantity_sold` (đã có)
- [ ] Thêm các field này vào match dict:
  ```python
  "discount": f"-{disc_pct}%" if disc_pct > 0 else None,
  "is_new": bool(match_p.get("is_new_product")),
  "popularity": int(match_p.get("quantity_sold") or 0),
  ```

### Phase 4: Test với curl (15 phút)
- [ ] Gọi `/api/tag-search/chat` với "áo phông trắng nữ"
- [ ] Verify response JSON có `ai_outfit_suggestions` với 3 roles
- [ ] Verify `discount` field đúng

## ⚠️ Risks & Side Effects
- **Score threshold cứng:** Nếu matches toàn dưới 0.7 → không có gợi ý → Thêm fallback: nếu 0 match qua filter, lấy top 1 bất kể score
- **Performance:** Mở rộng top 3→5 SP → 1-2 SP thêm gọi stock check → +2ms stock call, acceptable

## 🔧 Tools / Scripts Needed
- Không cần tool ngoài
- Unit test: `pytest backend/agent/tag_search_agent_v2/tests/` nếu có

## ⏱ Effort Estimate
**S** — ~1.5 giờ (incremental change trên code đã có)

---
_Captured: 2026-04-28 | Type: Codebase-Aware_
