"""
Fashion Matches — API Route
Manages AI-powered fashion matching (ai_matches) per product.

Endpoints:
  GET  /api/fashion-matches/{code}              → lấy ai_matches
  POST /api/fashion-matches/{code}/update       → lưu manual edits
  POST /api/fashion-matches/{code}/regen        → trigger AI engine 1 SP
  POST /api/fashion-matches/batch               → trigger engine toàn bộ
  GET  /api/fashion-matches/batch/status        → poll batch progress
  GET  /api/fashion-matches/rules/config        → lấy fashion_rules.json
  PUT  /api/fashion-matches/rules/config        → cập nhật rules
  GET  /api/fashion-matches/rules/meta          → structured metadata (colors, styles, occasions)
  POST /api/fashion-matches/color-logic         → kiểm tra synergy 2 màu
  POST /api/fashion-matches/outfit-suggest      → gợi ý outfit đầy đủ từ 1 product code
  POST /api/fashion-matches/score-test          → test pairwise score breakdown
"""

import json
import logging
import os
from typing import Optional

from fastapi import APIRouter, BackgroundTasks
from fastapi.responses import JSONResponse
from pydantic import BaseModel

from common.pool_wrapper import get_pooled_connection_compat

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/fashion-matches", tags=["Fashion Matches"])

TABLE = "dashboard_canifa.ultra_descriptions"
RULES_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "worker", "fashion_rules.json")


# ─── Pydantic ────────────────────────────────────────────────
class UpdateMatchesRequest(BaseModel):
    ai_matches: dict


class UpdateRulesRequest(BaseModel):
    rules: dict


class OutfitSuggestRequest(BaseModel):
    code: str
    occasion: Optional[str] = None


class ColorLogicRequest(BaseModel):
    src_color: str
    tgt_color: str


# ─── Helpers ─────────────────────────────────────────────────
def _get_ai_matches(code: str) -> Optional[dict]:
    conn = None
    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()
        cur.execute(
            f"SELECT ai_matches FROM {TABLE} WHERE magento_ref_code = %s OR internal_ref_code = %s LIMIT 1",
            (code, code),
        )
        row = cur.fetchone()
        cur.close()
        if not row:
            return None
        data = row[0]
        if isinstance(data, str):
            data = json.loads(data)
        return data or {}
    except Exception as e:
        logger.error("[FashionMatches] get error %s: %s", code, e)
        return None
    finally:
        if conn:
            conn.close()


def _save_ai_matches(code: str, ai_matches: dict) -> bool:
    conn = None
    try:
        conn = get_pooled_connection_compat()
        cur = conn.cursor()
        cur.execute(
            f"""UPDATE {TABLE}
                SET ai_matches = %s::jsonb, updated_at = NOW()
                WHERE magento_ref_code = %s OR internal_ref_code = %s""",
            (json.dumps(ai_matches, ensure_ascii=False), code, code),
        )
        updated = cur.rowcount > 0
        conn.commit()
        cur.close()
        return updated
    except Exception as e:
        if conn:
            conn.rollback()
        logger.error("[FashionMatches] save error %s: %s", code, e)
        return False
    finally:
        if conn:
            conn.close()


def _run_engine_background(code: Optional[str] = None):
    try:
        from worker.stylist_engine import StylistEngine
        engine = StylistEngine()
        if code:
            engine.run_for_code(code)
        else:
            engine.run_batch()
    except Exception as e:
        logger.error("[FashionMatches] engine error: %s", e)


def _load_rules() -> dict:
    with open(RULES_PATH, "r", encoding="utf-8") as f:
        return json.load(f)


def _detect_color_key(color_str: str, color_keys: dict) -> Optional[str]:
    c = color_str.lower()
    for key, variants in color_keys.items():
        if any(v in c for v in variants):
            return key
    return None


def _color_group_explain(src_key: Optional[str], tgt_key: Optional[str], rules: dict) -> dict:
    color_groups = rules.get("color_groups", {})
    group_matrix = rules.get("color_group_matrix", {})
    color_matrix = rules.get("color_matrix", {})
    default_score = rules.get("default_color_score", 12)

    src_group = color_groups.get(src_key) if src_key else None
    tgt_group = color_groups.get(tgt_key) if tgt_key else None

    if src_group and tgt_group:
        score = group_matrix.get(src_group, {}).get(tgt_group, default_score)
        advice = _color_advice(src_group, tgt_group, src_key, tgt_key)
        return {
            "src_key": src_key, "src_group": src_group,
            "tgt_key": tgt_key, "tgt_group": tgt_group,
            "score": score, "max": 30,
            "method": "group_matrix",
            "advice": advice,
        }

    if src_key and tgt_key:
        score = color_matrix.get(src_key, {}).get(tgt_key, default_score)
        return {
            "src_key": src_key, "src_group": src_group or "?",
            "tgt_key": tgt_key, "tgt_group": tgt_group or "?",
            "score": score, "max": 30,
            "method": "color_matrix",
            "advice": f"Phối {src_key} + {tgt_key} (legacy matrix)",
        }

    return {
        "src_key": src_key or "?", "src_group": src_group or "?",
        "tgt_key": tgt_key or "?", "tgt_group": tgt_group or "?",
        "score": default_score, "max": 30,
        "method": "default",
        "advice": "Không xác định được màu — dùng điểm mặc định",
    }


def _color_advice(src_group: str, tgt_group: str, src_key: Optional[str], tgt_key: Optional[str]) -> str:
    combo = (src_group, tgt_group)
    if combo == ("neutral", "neutral"):
        return f"✅ Công thức An Toàn: {src_key} + {tgt_key} — luôn hài hòa, phù hợp mọi dịp."
    if combo in [("neutral", "light"), ("light", "neutral")]:
        return f"✅ Công thức Điểm Nhấn: màu sáng ({src_key or tgt_key}) + trung tính ({tgt_key or src_key}) — nổi bật, tươi trẻ."
    if combo in [("neutral", "dark"), ("dark", "neutral")]:
        return f"✅ Công thức Điểm Nhấn: màu nổi ({src_key or tgt_key}) + trung tính ({tgt_key or src_key}) — cá tính, ấn tượng."
    if combo in [("light", "dark"), ("dark", "light")]:
        return f"⚠️ Tương phản mạnh: {src_key} + {tgt_key} — táo bạo, cần phụ kiện trung tính để cân bằng."
    if combo == ("light", "light"):
        return f"⚠️ Cùng tông sáng: {src_key} + {tgt_key} — pastel dịu dàng nhưng dễ bị nhạt, cần phụ kiện tương phản."
    if combo == ("dark", "dark"):
        return f"⚠️ Cùng tông đậm: {src_key} + {tgt_key} — mạnh mẽ, cần chú ý không bị nặng nề. Thêm phụ kiện trung tính."
    return f"Phối {src_key} + {tgt_key}"


def _build_color_strategy(src_key: Optional[str], src_group: Optional[str], rules: dict) -> dict:
    group_matrix = rules.get("color_group_matrix", {})
    color_groups_map = rules.get("color_groups", {})

    if not src_group:
        return {"summary": "Không xác định được màu sắc sản phẩm.", "strategies": []}

    if src_group == "neutral":
        summary = f"{src_key} là màu Trung Tính — dễ phối nhất, áp dụng được cả 3 công thức."
        strategies = [
            {
                "name": '🟢 Công thức "An Toàn"',
                "desc": "Phối với màu Neutral khác (Trắng, Đen, Xám, Be, Nâu) — luôn hài hòa.",
                "score": group_matrix.get("neutral", {}).get("neutral", 30),
                "example_colors": _get_colors_by_group("neutral", color_groups_map, exclude=src_key),
            },
            {
                "name": '✨ Công thức "Điểm Nhấn" (sáng)',
                "desc": "Phối với màu Light (Hồng, Vàng, Xanh lam, Tím) — tươi trẻ, nổi bật.",
                "score": group_matrix.get("neutral", {}).get("light", 22),
                "example_colors": _get_colors_by_group("light", color_groups_map),
            },
            {
                "name": '🔥 Công thức "Cá Tính" (đậm)',
                "desc": "Phối với màu Dark (Đỏ, Cam, Xanh Jeans, Xanh navy) — mạnh mẽ, ấn tượng.",
                "score": group_matrix.get("neutral", {}).get("dark", 25),
                "example_colors": _get_colors_by_group("dark", color_groups_map),
            },
        ]
    elif src_group == "light":
        summary = f"{src_key} là màu Sáng/Pastel — phối BOTTOM Neutral để tiết chế, tránh phụ kiện quá sặc sỡ."
        strategies = [
            {
                "name": "✅ Phối với Neutral",
                "desc": f"Màu {src_key} (sáng/pastel) → phối BOTTOM Neutral (Trắng, Đen, Xám, Be) để cân bằng.",
                "score": group_matrix.get("light", {}).get("neutral", 22),
                "example_colors": _get_colors_by_group("neutral", color_groups_map),
            },
            {
                "name": "⚠️ Phụ kiện nên Neutral",
                "desc": "Chọn phụ kiện đen/trắng để không rối mắt. Tuân thủ quy tắc tối đa 3 màu.",
                "score": None,
                "example_colors": ["Đen", "Trắng", "Xám"],
            },
        ]
    else:
        summary = f"{src_key} là màu Đậm/Nổi — ưu tiên BOTTOM Neutral để cân bằng. Phụ kiện cũng nên Neutral."
        strategies = [
            {
                "name": "✅ Phối với Neutral",
                "desc": f"Màu {src_key} (đậm/nổi) → phối BOTTOM Neutral (Trắng, Đen, Xám, Be) để hài hòa.",
                "score": group_matrix.get("dark", {}).get("neutral", 20),
                "example_colors": _get_colors_by_group("neutral", color_groups_map),
            },
            {
                "name": "🎯 Mono-color (cùng tông)",
                "desc": f"Mặc head-to-toe {src_key} — mạnh mẽ, cần phụ kiện kim loại Gold/Silver làm điểm nhấn.",
                "score": group_matrix.get("dark", {}).get("dark", 10),
                "example_colors": _get_colors_by_group("dark", color_groups_map, exclude=src_key),
            },
        ]

    return {"summary": summary, "strategies": strategies}


def _get_colors_by_group(group: str, color_groups_map: dict, exclude: Optional[str] = None) -> list:
    return [k for k, g in color_groups_map.items() if g == group and k != exclude]


# ─── Endpoints ───────────────────────────────────────────────

@router.get("/{code}")
async def get_fashion_matches(code: str):
    from worker.stylist_engine import StylistEngine
    engine = StylistEngine()
    data = engine.compute_dynamic_rule_matches(code)
    classifications = engine.compute_super_classifications_sql(code)
    
    # Return empty ai_matches if not found (or gracefully handle) to avoid UI breaking
    if data is None:
        data = {}
    return {"ok": True, "code": code, "ai_matches": data, "classifications": classifications}


@router.post("/{code}/update")
async def update_fashion_matches(code: str, req: UpdateMatchesRequest):
    ok = _save_ai_matches(code, req.ai_matches)
    if ok:
        logger.info("[FashionMatches] Manual update saved: %s", code)
        return {"ok": True, "message": "Đã lưu gợi ý phối đồ"}
    return JSONResponse({"ok": False, "error": "Không tìm thấy sản phẩm"}, status_code=404)


@router.post("/{code}/regen")
async def regen_fashion_matches(code: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(_run_engine_background, code)
    logger.info("[FashionMatches] Regen triggered: %s", code)
    return {"ok": True, "message": f"Đang tính toán phối đồ cho {code}..."}


@router.post("/batch")
async def batch_fashion_matches(background_tasks: BackgroundTasks):
    from worker.stylist_engine import BATCH_STATE
    if BATCH_STATE.get("is_running"):
        return JSONResponse({"ok": False, "error": "Batch đang chạy, vui lòng chờ"}, status_code=409)
    background_tasks.add_task(_run_engine_background, None)
    return {"ok": True, "message": "Đã khởi động batch Stylist Engine..."}


@router.get("/batch/status")
async def batch_status():
    from worker.stylist_engine import BATCH_STATE
    state = dict(BATCH_STATE)
    pct = 0
    if state.get("total", 0) > 0:
        pct = round(state["done"] / state["total"] * 100, 1)
    state["progress_pct"] = pct
    return {"ok": True, "state": state}


@router.get("/rules/config")
async def get_rules():
    try:
        rules = _load_rules()
        return {"ok": True, "rules": rules}
    except Exception as e:
        return JSONResponse({"ok": False, "error": str(e)}, status_code=500)


@router.put("/rules/config")
async def update_rules(req: UpdateRulesRequest):
    try:
        with open(RULES_PATH, "w", encoding="utf-8") as f:
            json.dump(req.rules, f, ensure_ascii=False, indent=2)
        return {"ok": True, "message": "Rules đã được cập nhật"}
    except Exception as e:
        return JSONResponse({"ok": False, "error": str(e)}, status_code=500)


@router.get("/rules/meta")
async def get_rules_meta():
    try:
        rules = _load_rules()
        color_hex_map = {
            "Trắng": "#F5F5F5", "Đen": "#1A1A1A", "Be": "#D4B896", "Xám": "#9E9E9E",
            "Nâu": "#795548", "Vàng": "#FDD835", "Hồng": "#F48FB1", "Xanh lam": "#64B5F6",
            "Tím": "#BA68C8", "Đỏ": "#EF5350", "Cam": "#FFA726", "Xanh lá": "#66BB6A",
            "Xanh navy": "#1A237E", "Xanh Jeans": "#5C6BC0", "Xanh than": "#00897B",
        }
        color_meta = []
        for key, variants in rules.get("color_keys", {}).items():
            group = rules.get("color_groups", {}).get(key, "unknown")
            color_meta.append({
                "key": key, "group": group,
                "hex": color_hex_map.get(key, "#CCCCCC"),
                "keywords": variants,
            })
        return {
            "ok": True,
            "meta": {
                "colors": color_meta,
                "color_group_matrix": rules.get("color_group_matrix", {}),
                "styles": list(rules.get("style_compat", {}).keys()),
                "occasions": [
                    {"key": k, "label": v}
                    for k, v in rules.get("occasion_labels", {}).items()
                ],
                "roles": [
                    {"key": k, "label": v}
                    for k, v in rules.get("role_labels", {}).items()
                ],
                "score_weights": rules.get("score_weights", {}),
                "version": rules.get("version", "?"),
            },
        }
    except Exception as e:
        return JSONResponse({"ok": False, "error": str(e)}, status_code=500)


@router.post("/color-logic")
async def color_logic(req: ColorLogicRequest):
    try:
        rules = _load_rules()
        color_keys = rules.get("color_keys", {})
        src_key = _detect_color_key(req.src_color, color_keys)
        tgt_key = _detect_color_key(req.tgt_color, color_keys)
        result = _color_group_explain(src_key, tgt_key, rules)
        return {"ok": True, "result": result}
    except Exception as e:
        return JSONResponse({"ok": False, "error": str(e)}, status_code=500)


@router.post("/outfit-suggest")
async def outfit_suggest(req: OutfitSuggestRequest):
    try:
        rules = _load_rules()
        color_keys = rules.get("color_keys", {})

        from worker.stylist_engine import StylistEngine
        engine = StylistEngine()
        catalog = engine._load_catalog()

        # Find source product in catalog
        src_product = next((p for p in catalog if p.get("code") == req.code or p.get("internal_ref_code") == req.code), None)

        if not src_product:
            return JSONResponse(
                {"ok": False, "error": f"Không tìm thấy sản phẩm: {req.code}"},
                status_code=404,
            )

        logger.info("[OutfitSuggest] Computing real-time dynamic matches for %s", req.code)
        ai_matches = engine.compute_dynamic_rule_matches(req.code)

        src_color = src_product.get("color", "")
        src_key = _detect_color_key(src_color, color_keys)
        src_group = rules.get("color_groups", {}).get(src_key) if src_key else None
        
        desc_data = src_product.get("description_data", {})
        src_name = src_product.get("name", "")
        src_line = src_product.get("product_line", "")
        src_gender = src_product.get("gender", "")
        src_img = src_product.get("image", "")

        src_color = src_color or ""
        src_key = _detect_color_key(src_color, color_keys)
        src_group = rules.get("color_groups", {}).get(src_key) if src_key else None

        occasions = rules.get("occasions", [])
        active_occasions = [req.occasion] if (req.occasion and req.occasion in occasions) else occasions

        outfit_by_occasion = {}
        for occ in active_occasions:
            occ_data = ai_matches.get(occ, {})
            if not occ_data:
                continue
            slots = {}
            for role, items in occ_data.items():
                enriched = []
                for item in items:
                    item_color_key = _detect_color_key(item.get("color", ""), color_keys)
                    color_info = _color_group_explain(src_key, item_color_key, rules)
                    enriched.append({
                        **item,
                        "color_key": item_color_key,
                        "color_group": rules.get("color_groups", {}).get(item_color_key, "?") if item_color_key else "?",
                        "color_synergy": color_info,
                    })
                slots[role] = enriched
            if slots:
                outfit_by_occasion[occ] = {
                    "label": rules.get("occasion_labels", {}).get(occ, occ),
                    "slots": slots,
                }

        color_strategy = _build_color_strategy(src_key, src_group, rules)
        
        # Inject our brand new SQL-based super classification matches
        classifications = engine.compute_super_classifications_sql(req.code)

        return {
            "ok": True,
            "source_product": {
                "code": req.code,
                "name": src_name or "",
                "color": src_color,
                "color_key": src_key,
                "color_group": src_group,
                "product_line": src_line or "",
                "gender": src_gender or "",
                "image": src_img or "",
                "style": desc_data.get("phong_cach", ""),
                "occasion": desc_data.get("dip_mac", ""),
                "material": desc_data.get("vat_lieu", ""),
            },
            "color_strategy": color_strategy,
            "outfit_by_occasion": outfit_by_occasion,
            "classifications": classifications
        }

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


class ScoreTestRequest(BaseModel):
    source_code: str
    target_code: str


@router.post("/score-test")
async def score_test(req: ScoreTestRequest):
    try:
        from worker.stylist_engine import StylistEngine
        engine = StylistEngine()
        catalog = engine._load_catalog()

        source = next((p for p in catalog if p.get("code") == req.source_code), None)
        target = next((p for p in catalog if p.get("code") == req.target_code), None)

        if not source:
            return JSONResponse({"ok": False, "error": f"Không tìm thấy SP nguồn: {req.source_code}"}, status_code=404)
        if not target:
            return JSONResponse({"ok": False, "error": f"Không tìm thấy SP đích: {req.target_code}"}, status_code=404)

        breakdown = engine._score_breakdown(source, target)
        total = sum(v["score"] for v in breakdown.values())
        occ0 = (engine.rules.get("occasions") or ["hang_ngay"])[0]
        reason = engine._build_reason(source, target, occ0, total)

        return {
            "ok": True,
            "result": {
                "source_code": req.source_code,
                "target_code": req.target_code,
                "target_name": target.get("name", ""),
                "roles_str": target.get("product_line", ""),
                "total_score": total,
                "reason": reason,
                "breakdown": breakdown,
            },
        }
    except Exception as e:
        logger.error("[ScoreTest] error: %s", e)
        return JSONResponse({"ok": False, "error": str(e)}, status_code=500)
