"""fetch_icon.py: 从 Iconify CDN 拉个性化图标,按主题色染色,缓存本地。

Iconify 聚合了 150+ 免费开源图标集,无需账号、无 API key:
    tabler           -- 现代描边 (Apache 2.0) ⭐ 推荐
    lucide           -- 开源经典 (ISC)
    heroicons        -- Tailwind (MIT)
    material-symbols -- Google (Apache 2.0)
    carbon           -- IBM (Apache 2.0)
    fluent           -- Microsoft (MIT)
    mdi              -- Material Design (Apache 2.0)

每个集都有数千图标,在 https://icon-sets.iconify.design/ 浏览找名字。

用法:
    # 推荐: 染主色,导出 PNG (需 cairosvg 或 svglib)
    python fetch_icon.py rocket --set tabler --color C00000 --size 128 \\
        -o slides/rocket.png

    # 只要 SVG (PowerPoint 2016+ 支持嵌入 SVG)
    python fetch_icon.py target --set lucide --color FFC107 \\
        -o slides/target.svg

    # 默认值: set=tabler, color=C00000(主红), size=128
    python fetch_icon.py chart-bar -o slides/chart_bar.png

环境:
    PNG 转换依赖任一: `pip install cairosvg`  (推荐) 或 `pip install svglib`
    若都没有,会保存 .svg 到目标路径(扩展名自动改).

退出码:
    0 = 成功 PNG/SVG
    1 = SVG 有了但 PNG 转换失败 (已保存 SVG)
    2 = 网络/图标名错误 (没拉到)
"""
from __future__ import annotations

import argparse
import io
import sys
import urllib.parse
import urllib.request
from pathlib import Path

ICONIFY_API = "https://api.iconify.design/{set}/{name}.svg"


def fetch_svg(name: str, icon_set: str, color: str, size: int) -> str:
    """从 Iconify 拉 SVG,带主题色和大小参数。"""
    params: dict[str, str] = {}
    if color:
        params["color"] = "#" + color.lstrip("#")
    if size:
        params["height"] = str(size)
        params["width"] = str(size)
    url = ICONIFY_API.format(set=icon_set, name=name)
    if params:
        url += "?" + urllib.parse.urlencode(params)
    req = urllib.request.Request(
        url, headers={"User-Agent": "ppt-skill-fetch_icon/1.0"}
    )
    with urllib.request.urlopen(req, timeout=15) as resp:
        body = resp.read().decode("utf-8")
    return body


def svg_to_png(svg_text: str, out_path: Path, size: int) -> bool:
    """SVG → PNG,降级链:cairosvg → svglib+reportlab → 失败。"""
    # 路径 1: cairosvg (推荐,质量最好)
    try:
        import cairosvg  # type: ignore
        cairosvg.svg2png(
            bytestring=svg_text.encode("utf-8"),
            write_to=str(out_path),
            output_width=size,
            output_height=size,
        )
        return True
    except ImportError:
        pass
    except Exception as e:
        print(f"[warn] cairosvg 渲染失败: {e}", file=sys.stderr)

    # 路径 2: svglib + reportlab
    try:
        from svglib.svglib import svg2rlg  # type: ignore
        from reportlab.graphics import renderPM  # type: ignore

        drawing = svg2rlg(io.StringIO(svg_text))
        if drawing is None:
            return False
        renderPM.drawToFile(drawing, str(out_path), fmt="PNG")
        return True
    except ImportError:
        pass
    except Exception as e:
        print(f"[warn] svglib 渲染失败: {e}", file=sys.stderr)

    return False


def main() -> int:
    ap = argparse.ArgumentParser(
        description="从 Iconify CDN 拉个性化 SVG/PNG 图标"
    )
    ap.add_argument("name", help="图标名,见 https://icon-sets.iconify.design/")
    ap.add_argument(
        "--set", default="tabler",
        help="图标集 (默认 tabler;可选 lucide/heroicons/material-symbols/carbon/fluent/mdi)",
    )
    ap.add_argument(
        "--color", default="C00000",
        help="主题色 hex,无 # (默认 C00000 商务红主色)",
    )
    ap.add_argument(
        "--size", type=int, default=128,
        help="像素 (默认 128,适合 0.5-1.0 in PPT 图标)",
    )
    ap.add_argument(
        "-o", "--out", required=True, type=Path,
        help="输出路径 (.png 或 .svg)",
    )
    ap.add_argument(
        "--svg-only", action="store_true",
        help="只输出 SVG,跳过 PNG 转换",
    )
    args = ap.parse_args()

    args.out.parent.mkdir(parents=True, exist_ok=True)

    try:
        svg = fetch_svg(args.name, args.set, args.color, args.size)
    except urllib.error.HTTPError as e:
        print(
            f"[error] Iconify 返回 {e.code}: 图标 '{args.set}:{args.name}' "
            f"可能不存在,在 https://icon-sets.iconify.design/{args.set}/ 搜",
            file=sys.stderr,
        )
        return 2
    except Exception as e:
        print(f"[error] 拉取失败: {e}", file=sys.stderr)
        return 2

    if "<svg" not in svg:
        print(
            f"[error] 返回不是 SVG: 图标 '{args.set}:{args.name}' 不存在",
            file=sys.stderr,
        )
        return 2

    out: Path = args.out
    want_svg = args.svg_only or out.suffix.lower() == ".svg"

    if want_svg:
        if out.suffix.lower() != ".svg":
            out = out.with_suffix(".svg")
        out.write_text(svg, encoding="utf-8")
        print(f"[ok] SVG → {out}")
        return 0

    if svg_to_png(svg, out, args.size):
        print(f"[ok] PNG → {out}  ({args.set}:{args.name}  #{args.color})")
        return 0

    # PNG 转换失败,保存 SVG 兜底
    svg_alt = out.with_suffix(".svg")
    svg_alt.write_text(svg, encoding="utf-8")
    print(
        f"[warn] PNG 转换不可用 (装 `pip install cairosvg` 或 `pip install svglib`)\n"
        f"        已保存 SVG → {svg_alt}\n"
        f"        PowerPoint 2016+ 直接 add_picture(svg) 也可以",
        file=sys.stderr,
    )
    return 1


if __name__ == "__main__":
    sys.exit(main())
