"""节点 7/7：压缩 + 上传阿里云 OSS（oss_upload）。

在 product 之后运行：把 product 生成的成品图（composite / printed / design / basemap）
压缩为 3:4 / ≥1340×1785 / <2MB 的 JPEG，上传到 config.oss 图床。

上传 key（图床路径）：{国家}/{任务时间戳}/{货号}_{4位随机}.jpg
  - 任务时间戳：任务开始记录（YYYYMMDDHHMMSS），state["task_timestamp"]，缺失时取当前时间
  - 货号：用户自定义前缀（config.product.code_prefix，默认 DG）+ 3 位计数（000 起，最多 999）
  - 4 位随机：大小写英文 + 数字

压缩/上传均带兜底：单图失败不影响其它；未配置 oss 或 enabled=false 时静默跳过。
"""
import random
import string
import time
from pathlib import Path
from typing import Any, Dict, List

from graph.validate import with_fallback

# design 设计稿是过程稿（不进模板），不压缩不上传；只传最终商品图
KIND_ORDER = ["composite", "printed", "basemap"]
MAX_CODE = 999            # 货号计数上限（000~998 共 999 张）


def _gen_rand4() -> str:
    return "".join(random.choices(string.ascii_letters + string.digits, k=4))


@with_fallback("oss_upload")
def oss_upload_node(state: Dict[str, Any]) -> Dict[str, Any]:
    products: List[Dict[str, Any]] = state.get("product") or []
    config = state["config"] or {}
    oss_cfg = config.get("oss") or {}
    country = state.get("country", "")

    if not (oss_cfg.get("oss_bucket") and oss_cfg.get("oss_key_id")):
        print("[oss] 未配置 oss（config.oss），跳过压缩上传节点。")
        return {"oss": [], "stats": state.get("stats") or {}}
    if not bool(oss_cfg.get("enabled", True)):
        print("[oss] config.oss.enabled=false，跳过上传。")
        return {"oss": [], "stats": state.get("stats") or {}}

    from graph.oss_upload import build_oss_key, compress_for_oss, upload_to_oss

    # 任务时间戳：任务开始记录；缺失则当前时间
    ts = str(state.get("task_timestamp") or time.strftime("%Y%m%d%H%M%S"))
    # 货号前缀：config.product.code_prefix（默认 DG）
    prefix = str(((config.get("product") or {}).get("code_prefix")) or "DG").strip()
    # 序号从 state 续接（一次任务内跨多次节点调用不重号）
    seq = int(state.get("oss_seq") or 0)

    uploaded: List[Dict[str, Any]] = []
    stats = dict(state.get("stats") or {})
    for r in products:
        spu = r.get("spu_code", "")
        sku = r.get("sku_code", "")
        for kind in KIND_ORDER:
            src = r.get(f"{kind}_path")
            if not src or not Path(src).exists():
                continue
            if seq >= MAX_CODE:
                print(f"[oss] 货号计数已达上限 999，停止上传后续图片（{src}）")
                break
            try:
                code = f"{prefix}{seq:03d}"           # 货号：前缀 + 3 位计数（000 起）
                compressed = compress_for_oss(src, str(Path(src).with_suffix(".oss.jpg")))
                key = build_oss_key(country, ts, code, _gen_rand4())
                url = upload_to_oss(oss_cfg, compressed, key)
                if url:
                    r[f"{kind}_url"] = url
                    r["oss_code"] = code
                    uploaded.append({"spu_code": spu, "sku_code": sku, "kind": kind,
                                     "code": code, "url": url})
                    seq += 1
            except Exception as e:  # noqa: BLE001
                print(f"[oss] 处理失败 {src}: {e}")

        # 多色：color_composites 用于模板按颜色路由——首色用主图 url/code，额外色单独上传（独立货号）
        color_ups: List[Dict[str, Any]] = []
        comps = r.get("color_composites") or []
        if comps and r.get("composite_url"):
            color_ups.append({**comps[0], "url": r["composite_url"], "code": r.get("oss_code", "")})
        for cc in comps[1:]:
            src = cc.get("composite_path")
            if not src or not Path(src).exists():
                continue
            if seq >= MAX_CODE:
                print("[oss] 货号计数已达上限 999，停止上传颜色图")
                break
            try:
                code = f"{prefix}{seq:03d}"
                compressed = compress_for_oss(src, str(Path(src).with_suffix(".oss.jpg")))
                key = build_oss_key(country, ts, code, _gen_rand4())
                url = upload_to_oss(oss_cfg, compressed, key)
                if url:
                    cc["url"] = url
                    cc["code"] = code
                    color_ups.append(cc)
                    uploaded.append({"spu_code": spu, "sku_code": cc.get("sku_code"),
                                     "kind": "composite_color", "code": code, "url": url})
                    seq += 1
            except Exception as e:  # noqa: BLE001
                print(f"[oss] 颜色图上传失败 {src}: {e}")
        if color_ups:
            r["color_composites"] = color_ups

    stats["oss"] = {"uploaded": len(uploaded), "timestamp": ts, "prefix": prefix, "seq": seq}
    return {"oss": uploaded, "product": products, "oss_seq": seq, "stats": stats}
