"""
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            → lấy fashion_rules.json
  PUT  /api/fashion-matches/rules            → cập nhật rules
"""

import json
import logging
import os
import threading
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(__file__)), "worker", "fashion_rules.json")


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


class UpdateRulesRequest(BaseModel):
    rules: dict


# ─── 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):
    """Background task: run engine for 1 code or full batch."""
    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)


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

@router.get("/{code}")
async def get_fashion_matches(code: str):
    """GET ai_matches for a single product."""
    data = _get_ai_matches(code)
    if data is None:
        return JSONResponse({"ok": False, "error": "Product not found"}, status_code=404)
    return {"ok": True, "code": code, "ai_matches": data}


@router.post("/{code}/update")
async def update_fashion_matches(code: str, req: UpdateMatchesRequest):
    """Save manually edited ai_matches."""
    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):
    """Trigger AI engine for a single product (async background)."""
    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):
    """Trigger full batch engine run."""
    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():
    """Poll batch progress."""
    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():
    """Fetch current fashion_rules.json."""
    try:
        with open(RULES_PATH, "r", encoding="utf-8") as f:
            rules = json.load(f)
        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):
    """Save updated fashion_rules.json."""
    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)


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


@router.post("/score-test")
async def score_test(req: ScoreTestRequest):
    """Test pairwise match score between two products — returns full breakdown."""
    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)

        # Compute score with breakdown
        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)
