#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
培训日报海报 · 07-29 · 竖版 1080 宽
布局策略：先在超高画布上按流式顺序绘制并记录真实 y，最后按内容裁切。
避免「预估高度」与「实际绘制」不一致导致的空白/挤压。
"""
import os
from PIL import Image, ImageDraw, ImageFont, ImageFilter

W = 1080
CANVAS_H = 3000                      # 足够高，最后裁掉
HERE = os.path.dirname(os.path.abspath(__file__))
BOLD = "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc"
REG = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
IDX = 2

ORANGE = (221, 106, 10)
ORG_L = (255, 158, 56)
CYAN = (0, 205, 255)
WHITE = (255, 255, 255)
TXT = (228, 238, 250)
DIM = (154, 180, 212)
DIM2 = (108, 134, 168)
RED = (255, 112, 92)
RED_L = (255, 152, 136)

_fc = {}
def F(sz, bold=True):
    k = (sz, bold)
    if k not in _fc:
        _fc[k] = ImageFont.truetype(BOLD if bold else REG, sz, index=IDX)
    return _fc[k]

PAD = 76
RIGHT = W - PAD
IW = RIGHT - PAD                     # 内容宽度

img = Image.new("RGB", (W, CANVAS_H), (5, 15, 31))
d = ImageDraw.Draw(img, "RGBA")

# ---------------- 背景（渐变 + 网格） ----------------
for y in range(CANVAS_H):
    t = min(1.0, y / 1900)
    e = t * t * (3 - 2 * t)
    d.line([(0, y), (W, y)], fill=(round(5 + 2 * e), round(16 + 22 * e), round(33 + 45 * e)))
for x in range(0, W, 54):
    d.line([(x, 0), (x, CANVAS_H)], fill=(120, 170, 220, 9))
for y in range(0, CANVAS_H, 54):
    d.line([(0, y), (W, y)], fill=(120, 170, 220, 9))

def glow(cx, cy, r, col, peak):
    x0, y0 = cx - r, cy - r
    if x0 < 0 or y0 < 0 or x0 + 2 * r > W or y0 + 2 * r > CANVAS_H:
        return
    lay = Image.new("RGBA", (r * 2, r * 2), col + (0,))
    m = Image.new("L", (r * 2, r * 2), 0)
    md = ImageDraw.Draw(m)
    for i in range(40, 0, -1):
        rr = r * i / 40
        md.ellipse([r - rr, r - rr, r + rr, r + rr], fill=int(peak * (1 - i / 40) ** 2))
    lay.putalpha(m.filter(ImageFilter.GaussianBlur(r * 0.10)))
    reg = img.crop((x0, y0, x0 + 2 * r, y0 + 2 * r)).convert("RGBA")
    img.paste(Image.alpha_composite(reg, lay).convert("RGB"), (x0, y0))

glow(W - 90, 190, 470, (0, 122, 240), 122)
glow(120, 1560, 430, (221, 106, 10), 70)
glow(W // 2, 980, 520, (0, 92, 190), 46)
d = ImageDraw.Draw(img, "RGBA")

# 顶部橙条
d.rectangle([0, 0, W, 8], fill=ORANGE)
d.rectangle([0, 8, W, 11], fill=(255, 172, 84, 110))

# ---------------- 图标（自绘，避免 emoji 变豆腐块） ----------------
def icon_warn(cx, cy, r, col):
    """三角感叹号"""
    d.polygon([(cx, cy - r), (cx + r * 0.92, cy + r * 0.72), (cx - r * 0.92, cy + r * 0.72)],
              outline=col, fill=col + (40,), width=3)
    d.line([cx, cy - r * 0.34, cx, cy + r * 0.20], fill=col, width=4)
    d.ellipse([cx - 2.4, cy + r * 0.38, cx + 2.4, cy + r * 0.38 + 4.8], fill=col)

def icon_ban(cx, cy, r, col):
    """禁止符"""
    d.ellipse([cx - r, cy - r, cx + r, cy + r], outline=col, width=4)
    k = r * 0.62
    d.line([cx - k, cy + k, cx + k, cy - k], fill=col, width=4)

# ---------------- logo ----------------
LG = Image.open(os.path.join(HERE, "..", "huatech-logo.png")).convert("RGBA")
LG = LG.crop(LG.getbbox())
lh = 58
LG = LG.resize((round(LG.width * lh / LG.height), lh), Image.LANCZOS)
px = LG.load()
for yy in range(LG.height):
    for xx in range(LG.width):
        r, g, b, a = px[xx, yy]
        if a:
            px[xx, yy] = (255, 255, 255, a)

y = 66
img.paste(LG, (PAD, y), LG)
y += lh + 52

# ---------------- 标题区 ----------------
f = F(29, True)
d.text((PAD, y), "培训日报", font=f, fill=CYAN)
tw = d.textlength("培训日报", font=f)
d.text((PAD + tw + 18, y), "07-29　周三", font=F(29, False), fill=DIM2)
y += 54

ft = F(82, True)
d.text((PAD, y), "华腾养虾记", font=ft, fill=WHITE)
tw = d.textlength("华腾养虾记", font=ft)
d.text((PAD + tw + 22, y), "第一弹", font=ft, fill=ORG_L)
title_w = tw + 22 + d.textlength("第一弹", font=ft)
y += 112
# 下划线与标题同宽的一段
d.rounded_rectangle([PAD, y, PAD + int(title_w * 0.30), y + 6], radius=3, fill=ORANGE)
y += 6 + 46

d.text((PAD, y), "今天干货偏多，感谢大家扛住。", font=F(37, False), fill=TXT)
y += 52 + 42

# ---------------- 分区标题 ----------------
def sec_title(y, text, color=CYAN):
    fs = F(39, True)
    d.rounded_rectangle([PAD, y + 7, PAD + 6, y + 38], radius=3, fill=color)
    d.text((PAD + 22, y), text, font=fs, fill=color)
    return y + 62

y = sec_title(y, "今天讲了什么")

# ---------------- 三张卡片（高度按内容行数算） ----------------
items = [
    ("1", "概念", ["什么是数字员工"], CYAN, False),
    ("2", "工具", ["OpenClaw ／ WorkBuddy"], CYAN, False),
    ("3", "任务", ["给你的数字员工做入职培训",
                  "越细越好，把真实的自己交给它"], ORG_L, True),
]
F_T, F_S = F(45, True), F(29, False)
for num, title, subs, ac, hot in items:
    TH, SH_, GAP = 58, 41, 8
    inner = TH + GAP + SH_ * len(subs)
    ph = 30                                   # 上下内边距
    ch = inner + ph * 2
    d.rounded_rectangle([PAD, y, RIGHT, y + ch], radius=18,
                        fill=(0, 94, 198, 60) if hot else (255, 255, 255, 14),
                        outline=ac + (145,) if hot else (255, 255, 255, 38), width=2)
    d.rounded_rectangle([PAD, y, PAD + 7, y + ch], radius=4, fill=ac + (240,))
    # 序号圆
    cxn, cyn = PAD + 60, y + ch // 2
    rr = 26
    d.ellipse([cxn - rr, cyn - rr, cxn + rr, cyn + rr],
              fill=ac + (44,), outline=ac + (165,), width=2)
    fn = F(31, True)
    nb = d.textbbox((0, 0), num, font=fn)
    d.text((cxn - (nb[2] - nb[0]) / 2 - nb[0], cyn - (nb[3] + nb[1]) / 2), num, font=fn, fill=ac)
    # 文本块整体垂直居中
    tx, ty = PAD + 114, y + ph
    d.text((tx, ty), title, font=F_T, fill=WHITE)
    ty += TH + GAP
    for s in subs:
        d.text((tx, ty), s, font=F_S, fill=(206, 228, 252) if hot else DIM)
        ty += SH_
    y += ch + 16

y += 26
d.line([PAD, y, RIGHT, y], fill=(255, 255, 255, 30), width=2)
y += 44

# ---------------- 基线块 ----------------
top = y
d.text((PAD + 66, y + 24), "动手之前，先做一件事", font=F(37, True), fill=ORG_L)
icon_warn(PAD + 40, y + 43, 17, ORG_L)
yy = y + 84
d.text((PAD + 34, yy), "接下来你手上第一个任务，先照老办法做一次，", font=F(28, False), fill=TXT)
yy += 40
d.text((PAD + 34, yy), "记三个字段：", font=F(28, False), fill=TXT)
yy += 50
fx = PAD + 40
fchip = F(26, True)
for t in ["是否使用 AI", "实际工时", "切身体会与 AI 的交互"]:
    fw = d.textlength(t, font=fchip) + 32
    d.rounded_rectangle([fx, yy, fx + fw, yy + 42], radius=21,
                        fill=(255, 255, 255, 20), outline=ORG_L + (105,), width=1)
    d.text((fx + 16, yy + 6), t, font=fchip, fill=(255, 216, 164))
    fx += fw + 13
yy += 42
bh = yy + 26 - top
d.rounded_rectangle([PAD, top, RIGHT, top + bh], radius=18,
                    fill=(255, 168, 60, 20), outline=ORG_L + (125,), width=2)
d.rounded_rectangle([PAD, top, PAD + 7, top + bh], radius=4, fill=ORG_L)
# 重绘内容（框在下层，重画保证不被遮）
d.text((PAD + 66, top + 24), "动手之前，先做一件事", font=F(37, True), fill=ORG_L)
icon_warn(PAD + 40, top + 43, 17, ORG_L)
d.text((PAD + 34, top + 84), "接下来你手上第一个任务，先照老办法做一次，", font=F(28, False), fill=TXT)
d.text((PAD + 34, top + 124), "记三个字段：", font=F(28, False), fill=TXT)
fx = PAD + 40
for t in ["是否使用 AI", "实际工时", "切身体会与 AI 的交互"]:
    fw = d.textlength(t, font=fchip) + 32
    d.rounded_rectangle([fx, top + 174, fx + fw, top + 216], radius=21,
                        fill=(255, 255, 255, 20), outline=ORG_L + (105,), width=1)
    d.text((fx + 16, top + 180), t, font=fchip, fill=(255, 216, 164))
    fx += fw + 13
y = top + bh + 30

# ---------------- 红线块 ----------------
top = y
rh = 190
d.rounded_rectangle([PAD, top, RIGHT, top + rh], radius=18,
                    fill=(255, 84, 72, 22), outline=RED + (125,), width=2)
d.rounded_rectangle([PAD, top, PAD + 7, top + rh], radius=4, fill=RED)
icon_ban(PAD + 41, top + 43, 16, RED_L)
d.text((PAD + 66, top + 24), "一句话红线", font=F(37, True), fill=RED_L)
d.text((PAD + 34, top + 82), "客户数据、源码、投标文件，不进外部工具。", font=F(31, True), fill=WHITE)
d.text((PAD + 34, top + 132), "WorkBuddy 在腾讯云上，L2 及以上一律不进。拿不准的，先问。",
       font=F(24, False), fill=(255, 198, 188))
y = top + rh + 34

# ---------------- 尾条 ----------------
d.rounded_rectangle([PAD, y, RIGHT, y + 68], radius=16,
                    fill=(0, 205, 255, 24), outline=CYAN + (105,), width=2)
d.text((PAD + 30, y + 16), "配置文件明天发群里", font=F(33, True), fill=CYAN)
y += 68 + 40

# ---------------- 页脚 ----------------
d.line([PAD, y, RIGHT, y], fill=(255, 255, 255, 24), width=1)
y += 20
ff = F(24, True)
d.text((PAD, y), "华腾科技", font=F(24, False), fill=DIM2)
t = "同样的活不干第二遍"
tw = d.textlength(t, font=ff)
d.text((RIGHT - tw, y), t, font=ff, fill=ORANGE)
y += 34

# ---------------- 裁切 ----------------
H = y + 44
out = img.crop((0, 0, W, H))
p_png = os.path.join(HERE, "培训日报-20260729.png")
p_jpg = os.path.join(HERE, "培训日报-20260729.jpg")
out.save(p_png)
out.save(p_jpg, quality=94)
print("size", out.size)
print("png", os.path.getsize(p_png), "jpg", os.path.getsize(p_jpg))
