"""POST /api/v1/worth-buy —— 多比比"AI 值不值得买"接口。

用户给一个商品标题 + 当前到手价,LLM 综合市场常见价、价格水位、大促节奏,
给出 0-100 的"现在值不值得买"评分 + 建议 + 理由 + 更划算的时机。

application/json:
  {"title": "...", "price": 8099.0, "platform": "京东"(可选)}

响应:
  {"score": 78, "verdict": "buy", "headline": "现在入手划算",
   "reasons": ["...","..."], "typical_price": 8299.0, "best_time": "..."}

verdict: buy(建议买) / wait(再等等) / neutral(看个人需求)
兜底:LLM 抽风/解析失败也总能给中性结论,不抛 5xx(决策类接口应该永远有答案)。
"""
from __future__ import annotations

import hashlib
import json
import logging
import re
from typing import Optional

from fastapi import APIRouter, HTTPException

from app.llm_client import MOCK_LLM, chat
from app.schemas import WorthBuyRequest, WorthBuyResponse

logger = logging.getLogger("app.worth_buy")

router = APIRouter(prefix="/api/v1")

SYSTEM_PROMPT = """你是一个理性购物决策助手。

# 任务
用户给你一个商品标题 + 当前到手价(可选平台)。判断「现在是不是入手的好时机」,
综合考虑:市场常见价水位、该价格在历史区间的位置、临近的大促节奏(618 / 双11 / 年货节)、
品类贬值速度(数码贬值快、日用品稳定)。

# 评分口径(score 0-100)
- 越高 = 越值得现在买(价格够低 / 临近无更优时机)。
- 80-100 建议买(verdict=buy);40-79 看个人需求(verdict=neutral);0-39 再等等(verdict=wait)。

# 输出格式
严格 JSON,无 markdown,无解释:

{"score": 78, "verdict": "buy", "headline": "一句话结论",
 "reasons": ["理由1", "理由2"], "typical_price": 8299.0, "best_time": "更划算的时机"}

字段:
- score: 0-100 整数
- verdict: "buy" / "wait" / "neutral"
- headline: 一句话结论(尽量 ≤ 14 字)
- reasons: 2-4 条短理由
- typical_price: 市场常见价(正数,必填)
- best_time: 若现在不是最佳给出更划算时机;若现在就值,可写"现在就合适"
"""


def _extract_json(s: str) -> Optional[dict]:
    s = s.strip()
    s = re.sub(r"^```(?:json)?\s*", "", s)
    s = re.sub(r"\s*```$", "", s)
    try:
        data = json.loads(s)
    except json.JSONDecodeError:
        m = re.search(r"\{.*\}", s, re.DOTALL)
        if not m:
            return None
        try:
            data = json.loads(m.group(0))
        except json.JSONDecodeError:
            return None
    return data if isinstance(data, dict) else None


def _num(v) -> Optional[float]:
    if isinstance(v, bool):
        return None
    if isinstance(v, (int, float)):
        return float(v)
    if isinstance(v, str):
        try:
            return float(v.strip())
        except ValueError:
            return None
    return None


def _mock_raw(title: str, price: float, platform: str) -> str:
    """占坑期 mock(DUOBIBI_MOCK_LLM):不调 LLM,按"当前价相对常见价的水位"
    确定性算分(价越低分越高),造 LLM 同格式 JSON,走下游解析/兜底。"""
    seed = int(hashlib.sha256(f"{title}|{platform}".encode("utf-8")).hexdigest()[:8], 16)
    typical = round(price * (0.90 + (seed % 25) / 100.0), 2)  # 常见价:price 的 0.90~1.14 倍
    ratio = price / typical if typical > 0 else 1.0
    score = int(round(max(0.0, min(100.0, (1.15 - ratio) / 0.30 * 100.0))))
    if score >= 80:
        verdict, headline, best_time = "buy", "现在入手划算", "现在就合适"
    elif score < 40:
        verdict, headline, best_time = "wait", "建议再等等", "下次大促(如 618 / 双11)"
    else:
        verdict, headline, best_time = "neutral", "看个人需求", "可关注大促节点"
    pct = round((typical - price) / typical * 100, 1) if typical > 0 else 0.0
    if pct > 0:
        reasons = [f"当前价低于市场常见价约 {pct}%", "近期价格处于偏低水位"]
    elif pct < 0:
        reasons = [f"当前价高于市场常见价约 {abs(pct)}%", "可等促销或比价后再入手"]
    else:
        reasons = ["当前价与市场常见价基本持平", "建议结合自身需求判断"]
    return json.dumps(
        {"score": score, "verdict": verdict, "headline": headline,
         "reasons": reasons, "typical_price": typical, "best_time": best_time},
        ensure_ascii=False,
    )


@router.post("/worth-buy", response_model=WorthBuyResponse)
def worth_buy(req: WorthBuyRequest) -> WorthBuyResponse:
    title = req.title.strip()
    if not title:
        raise HTTPException(status_code=422, detail="empty_title")
    if req.price <= 0:
        raise HTTPException(status_code=422, detail="invalid_price")

    plat = (req.platform or "").strip()
    user_msg = (
        f"商品标题: {title}\n"
        f"当前到手价: ¥{req.price:.2f}\n"
        + (f"平台: {plat}\n" if plat else "")
    )
    if MOCK_LLM:
        raw = _mock_raw(title, req.price, plat)
    else:
        try:
            raw = chat(
                messages=[
                    {"role": "system", "content": SYSTEM_PROMPT},
                    {"role": "user", "content": user_msg},
                ]
            )
            logger.debug("worth-buy LLM raw: %s", raw[:400])
        except Exception as e:
            # LLM 不可用(限流/网络/超时等):决策类接口不抛 5xx,降级走下方中性兜底。
            logger.warning("worth-buy LLM call failed, falling back to neutral: %s", e)
            raw = ""

    data = _extract_json(raw) or {}

    # score → clamp 0..100,解析失败给 50(中性)
    score_raw = data.get("score")
    if isinstance(score_raw, bool):
        score = 50
    else:
        n = _num(score_raw)
        score = int(round(n)) if n is not None else 50
    score = max(0, min(100, score))

    verdict = data.get("verdict")
    if verdict not in {"buy", "wait", "neutral"}:
        verdict = "buy" if score >= 80 else "wait" if score < 40 else "neutral"

    headline = data.get("headline")
    if not isinstance(headline, str) or not headline.strip():
        headline = {"buy": "现在入手划算", "wait": "建议再等等", "neutral": "看个人需求"}[verdict]
    headline = headline.strip()

    reasons_raw = data.get("reasons")
    if isinstance(reasons_raw, list):
        reasons = [r.strip() for r in reasons_raw if isinstance(r, str) and r.strip()][:4]
    else:
        reasons = []
    if not reasons:
        reasons = ["暂无足够信息,建议结合自身需求判断"]

    typical = _num(data.get("typical_price"))
    if typical is None or typical <= 0:
        typical = req.price

    best_time = data.get("best_time")
    if not isinstance(best_time, str) or not best_time.strip():
        best_time = "现在就合适" if verdict == "buy" else "下次大促(如 618 / 双11)"
    best_time = best_time.strip()

    logger.info(
        "worth-buy title=%r price=%.2f score=%d verdict=%s", title, req.price, score, verdict
    )
    return WorthBuyResponse(
        score=score,
        verdict=verdict,
        headline=headline,
        reasons=reasons,
        typical_price=round(typical, 2),
        best_time=best_time,
    )
