"""京东联盟 OpenAPI 客户端。

当前只接数据大盘需要的订单明细接口:
`jd.union.open.order.row.query`。京东要求订单查询时间窗最长 1 小时,
调用方负责切窗分页。
"""
from __future__ import annotations

import hashlib
import json
import logging
from datetime import datetime, timedelta, timezone
from typing import Any

import httpx

from app.core.config import settings

logger = logging.getLogger(__name__)

_BEIJING = timezone(timedelta(hours=8))


class JdUnionError(RuntimeError):
    """京东联盟 API 调用失败。"""


def _parse_json_maybe(value: Any) -> Any:
    if not isinstance(value, str):
        return value
    text = value.strip()
    if not text:
        return value
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        return value


def _sign(params: dict[str, Any], secret: str) -> str:
    pieces = [secret]
    for key in sorted(k for k in params if k != "sign"):
        value = params[key]
        if value is None:
            continue
        pieces.append(f"{key}{value}")
    pieces.append(secret)
    raw = "".join(pieces)
    return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()


def _unwrap_response(data: dict[str, Any]) -> dict[str, Any]:
    if "error_response" in data:
        err = data["error_response"] or {}
        msg = err.get("zh_desc") or err.get("en_desc") or err.get("msg") or err
        raise JdUnionError(f"京东 API 错误: {msg}")

    body: Any = data
    for key, value in data.items():
        if key.endswith("_responce") or key.endswith("_response"):
            body = value
            break

    body = _parse_json_maybe(body)
    if not isinstance(body, dict):
        raise JdUnionError("京东 API 返回格式异常")

    result = body.get("queryResult", body.get("result", body))
    result = _parse_json_maybe(result)
    if not isinstance(result, dict):
        raise JdUnionError("京东 API 业务结果格式异常")

    code = str(result.get("code", result.get("resultCode", "200")))
    if code not in {"0", "200"}:
        msg = result.get("message") or result.get("msg") or result.get("resultMsg") or result
        raise JdUnionError(f"京东 API 业务错误: {msg}")
    return result


def call(method: str, payload: dict[str, Any], *, version: str = "1.0") -> dict[str, Any]:
    if not settings.jd_union_configured:
        raise JdUnionError("京东联盟凭证未配置")

    biz_json = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
    params: dict[str, Any] = {
        "method": method,
        "app_key": settings.JD_UNION_APP_KEY,
        "timestamp": datetime.now(_BEIJING).strftime("%Y-%m-%d %H:%M:%S"),
        "format": "json",
        "v": version,
        "sign_method": "md5",
        "360buy_param_json": biz_json,
    }
    params["sign"] = _sign(params, settings.JD_UNION_APP_SECRET)

    try:
        with httpx.Client(timeout=settings.JD_UNION_TIMEOUT_SEC, trust_env=False) as client:
            resp = client.post(settings.JD_UNION_GATEWAY, data=params)
            resp.raise_for_status()
            data = resp.json()
    except httpx.HTTPError as e:
        raise JdUnionError(f"京东 API 网络错误: {e}") from e
    except json.JSONDecodeError as e:
        raise JdUnionError("京东 API 返回非 JSON") from e

    return _unwrap_response(data)


def _extract_rows(result: dict[str, Any]) -> tuple[list[dict[str, Any]], bool]:
    payload = _parse_json_maybe(result.get("data", result.get("result", result)))
    has_more = bool(result.get("hasMore") or result.get("has_more"))

    if isinstance(payload, dict):
        for key in ("orderRowResp", "orderRows", "orderList", "orders", "list", "rows"):
            rows = _parse_json_maybe(payload.get(key))
            if isinstance(rows, list):
                return [r for r in rows if isinstance(r, dict)], bool(
                    payload.get("hasMore") or payload.get("has_more") or has_more
                )
        return [], bool(payload.get("hasMore") or payload.get("has_more") or has_more)
    if isinstance(payload, list):
        return [r for r in payload if isinstance(r, dict)], has_more
    return [], has_more


def query_order_rows(
    *,
    start_time: datetime,
    end_time: datetime,
    query_time_type: int = 3,
    page_index: int = 1,
    page_size: int = 200,
) -> dict[str, Any]:
    """查询京东 CPS 订单行。

    query_time_type: 1 下单时间, 2 完成时间, 3 更新时间。
    start_time/end_time 用北京时间展示给京东;调用方需保证窗口不超过 1 小时。
    """
    start_bj = start_time.astimezone(_BEIJING)
    end_bj = end_time.astimezone(_BEIJING)
    if end_bj <= start_bj:
        return {"rows": [], "has_more": False}
    if end_bj - start_bj > timedelta(hours=1):
        raise JdUnionError("京东订单查询单次时间窗不能超过 1 小时")

    order_req: dict[str, Any] = {
        "pageIndex": page_index,
        "pageSize": min(max(page_size, 1), 200),
        "type": query_time_type,
        "startTime": start_bj.strftime("%Y-%m-%d %H:%M:%S"),
        "endTime": end_bj.strftime("%Y-%m-%d %H:%M:%S"),
    }
    if settings.JD_UNION_AUTH_KEY:
        order_req["key"] = settings.JD_UNION_AUTH_KEY
    result = call("jd.union.open.order.row.query", {"orderReq": order_req})
    rows, has_more = _extract_rows(result)
    logger.info(
        "jd.union.open.order.row.query fetched rows=%s page=%s has_more=%s",
        len(rows),
        page_index,
        has_more,
    )
    return {"rows": rows, "has_more": has_more, "raw": result}
