import asyncio
import json
import logging

from fastapi import APIRouter, Query
from fastapi.responses import JSONResponse, StreamingResponse

from worker.stylist_engine import StylistEngine

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/fashion-matches/simulator", tags=["Fashion Matches Simulator"])


def _flatten_candidates(ai_matches: dict) -> list[dict]:
    """Normalize ai_matches into a flat candidate list for simulator UI."""
    flat: list[dict] = []
    if not isinstance(ai_matches, dict):
        return flat

    for occ_or_role, role_or_items in ai_matches.items():
        if isinstance(role_or_items, list):
            for item in role_or_items:
                if isinstance(item, dict):
                    flat.append(
                        {
                            "occasion": "all",
                            "role": occ_or_role,
                            "code": item.get("code") or item.get("sku") or "",
                            "name": item.get("name") or "",
                            "image_url": item.get("image") or item.get("image_url") or "",
                            "score": float(item.get("score") or 0),
                            "color": item.get("color") or "",
                        }
                    )
            continue

        if not isinstance(role_or_items, dict):
            continue

        for role, items in role_or_items.items():
            if not isinstance(items, list):
                continue
            for item in items:
                if not isinstance(item, dict):
                    continue
                flat.append(
                    {
                        "occasion": occ_or_role,
                        "role": role,
                        "code": item.get("code") or item.get("sku") or "",
                        "name": item.get("name") or "",
                        "image_url": item.get("image") or item.get("image_url") or "",
                        "score": float(item.get("score") or 0),
                        "color": item.get("color") or "",
                    }
                )
    return sorted(flat, key=lambda x: -x["score"])


def _build_top_by_role(candidates: list[dict], top_k: int = 3) -> dict[str, list[dict]]:
    """Create a compact role-based top list for simulator final render."""
    role_map: dict[str, list[dict]] = {}
    for item in candidates:
        role = item.get("role") or "Khác"
        role_map.setdefault(role, [])

        # Deduplicate by product code inside each role.
        existing_codes = {p.get("sku") for p in role_map[role]}
        code = item.get("code")
        if code in existing_codes:
            continue

        role_map[role].append(
            {
                "sku": code,
                "code": code,
                "name": item.get("name") or "",
                "image_url": item.get("image_url") or "",
                "score": float(item.get("score") or 0),
                "color": item.get("color") or "",
                "occasion": item.get("occasion") or "",
            }
        )

    for role in list(role_map.keys()):
        role_map[role] = role_map[role][:top_k]
        if not role_map[role]:
            del role_map[role]

    return role_map


@router.get("/search")
async def search_product(q: str = Query(..., description="Mã SP hoặc Tên SP", min_length=2)):
    """API hỗ trợ tìm kiếm nhanh Product Code cho Simulator"""
    try:
        engine = StylistEngine()
        catalog = engine._get_catalog()

        q_lower = q.lower().strip()
        results = []

        for p in catalog:
            code = p.get("code", "").lower()
            ref = p.get("internal_ref_code", "").lower()
            name = p.get("name", "").lower()

            # Simple match logic
            if q_lower in code or q_lower in ref or q_lower in name:
                results.append(
                    {
                        "code": p.get("code"),
                        "name": p.get("name"),
                        "image": p.get("image", ""),
                        "color": p.get("color", ""),
                    }
                )
                if len(results) >= 10:
                    break

        return {"ok": True, "data": results}
    except Exception as e:
        logger.error("[Simulator] Search error: %s", e)
        return JSONResponse({"ok": False, "error": str(e)}, status_code=500)


@router.get("/stream")
async def stream_flow(code: str = Query(..., description="Product code to simulate")):
    """SSE Endpoint cho Realtime Live Simulator"""

    async def event_generator():
        try:
            # --- BƯỚC 1: INIT ---
            msg = json.dumps(
                {"step": 1, "node": "init", "status": f"🔧 Khởi chạy Cỗ máy StylistEngine. Mã SP đưa vào: {code}..."},
                ensure_ascii=False,
            )
            yield f"data: {msg}\n\n"
            await asyncio.sleep(0.8)  # Dramatic delay

            engine = StylistEngine()
            catalog = engine._get_catalog()
            source = next((p for p in catalog if p["code"] == code), None)

            if not source:
                yield f"data: {json.dumps({'error': True, 'status': '❌ Không tìm thấy mã sản phẩm trong Catalog!'})}\n\n"
                return

            # --- BƯỚC 2: PHÂN TÍCH SP GỐC ---
            msg = json.dumps(
                {
                    "step": 2,
                    "node": "fetch_product",
                    "status": f"🔍 Bóc tách SP gốc: {source.get('name')} (Màu: {source.get('color')} / Giới tính: {source.get('gender')})",
                    "payload": {
                        "code": source.get("code"),
                        "name": source.get("name"),
                        "image": source.get("image"),
                        "color": source.get("color"),
                        "category": source.get("product_line"),
                        "catalog_total": max(len(catalog) - 1, 0),
                    },
                },
                ensure_ascii=False,
            )
            yield f"data: {msg}\n\n"
            await asyncio.sleep(1.0)

            # --- BƯỚC 3: KÉO LUẬT DB ---
            anchor_cat = source.get("product_line", "")
            gender = source.get("gender", "")
            db_rules = engine._fetch_rules_with_reason(anchor_cat, gender)
            rules_count = len(db_rules)

            if rules_count == 0:
                status_rules = (
                    f"⚠️ Không có luật DB từ [chatbot_fashion_rules] cho '{anchor_cat}'. Rơi vào Fallback Rules."
                )
            else:
                status_rules = (
                    f"📂 Khớp thành công {rules_count} luật (Rules) phối đồ theo Dịp (Occasion) & Role từ Database."
                )

            msg = json.dumps(
                {
                    "step": 3,
                    "node": "fetch_rules",
                    "status": status_rules,
                    "payload": {
                        "rules_count": rules_count,
                        "stage_metrics": {
                            "stage": "rules",
                            "input_count": max(len(catalog) - 1, 0),
                            "output_count": rules_count,
                        },
                    },
                },
                ensure_ascii=False,
            )
            yield f"data: {msg}\n\n"
            await asyncio.sleep(1.2)

            # --- BƯỚC 4: CHẤM ĐIỂM ---
            # (Run the heavy lifting here)
            ai_matches = engine.compute_dynamic_rule_matches(code)
            scored_candidates = _flatten_candidates(ai_matches)

            msg = json.dumps(
                {
                    "step": 4,
                    "node": "scoring",
                    "status": "🧮 Khởi động Scoring Engine: Tính điểm Color Synergy (30đ), Material (10đ), Occasion (20đ) cho toàn bộ Catalog...",
                    "payload": {
                        "stage_metrics": {
                            "stage": "scoring",
                            "input_count": max(len(catalog) - 1, 0),
                            "output_count": len(scored_candidates),
                        },
                        "preview_products": scored_candidates[:12],
                    },
                },
                ensure_ascii=False,
            )
            yield f"data: {msg}\n\n"
            await asyncio.sleep(1.5)

            # --- BƯỚC 5: HẬU KỲ VÀ PHẨN LOẠI MỞ RỘNG (DEDUPLICATE / SQL CLASSIFICATIONS) ---
            top_role_matches = _build_top_by_role(scored_candidates, top_k=3)
            final_count = sum(len(v) for v in top_role_matches.values())
            msg = json.dumps(
                {
                    "step": 5,
                    "node": "dedup",
                    "status": "📋 Sàng lọc (Deduplication): Loại bỏ kết quả trùng, lấy Top 3 cho mỗi Role (Top/Bottom/Khoác)... Đồng thời nạp Data Phân Loại mở rộng SQL.",
                    "payload": {
                        "stage_metrics": {
                            "stage": "dedup",
                            "input_count": len(scored_candidates),
                            "output_count": final_count,
                        },
                        "preview_products": scored_candidates[:24],
                        "role_matches": top_role_matches,
                    },
                },
                ensure_ascii=False,
            )
            yield f"data: {msg}\n\n"

            classifications = engine.compute_super_classifications_sql(code)
            await asyncio.sleep(0.8)

            # --- BƯỚC 6: FINISH ---
            msg = json.dumps(
                {
                    "step": 6,
                    "node": "complete",
                    "status": "✅ HOÀN TẤT! Đẩy khung Outfit JSON ra giao diện.",
                    "payload": {
                        "ai_matches": top_role_matches,
                        "raw_ai_matches": ai_matches,
                        "classifications": classifications,
                    },
                },
                ensure_ascii=False,
            )
            yield f"data: {msg}\n\n"

        except Exception as e:
            logger.error("[Simulator] Generator error: %s", e)
            msg = json.dumps({"error": True, "status": f"❌ Lỗi Engine: {e!s}"}, ensure_ascii=False)
            yield f"data: {msg}\n\n"

    return StreamingResponse(event_generator(), media_type="text/event-stream")
