"""Draws the pictures used by the Passlabb emails (served from /static/email/).
Email apps block SVG and web fonts, so every icon is a PNG drawn here at 2x for sharp screens.
Run: python3 tools/email-assets/make.py   (needs Pillow and the Inter font)"""
import math, os, random
from PIL import Image, ImageDraw, ImageFont

OUT = os.path.join(os.path.dirname(__file__), "..", "..", "app", "public", "email")
os.makedirs(OUT, exist_ok=True)
FONT_DIR = "/usr/share/fonts/opentype/inter/"
def font(w, size): return ImageFont.truetype(FONT_DIR + f"Inter-{w}.otf", size)

# Passlabb identity: white and gold. Gold #B8893A, light gold #F1DFAE, bright gold #C9A24A, deep gold #8A6A2F, ink #1E1A14.
# (The names GREEN/MINT/ROSE are kept from the first palette so the drawing code below stays the same.)
GREEN = (184, 137, 58); MINT = (241, 223, 174); MINT2 = (217, 188, 116); ROSE = (201, 162, 74); ROSE2 = (230, 204, 138); ROSE_D = (168, 130, 46)
GOLD = (201, 162, 122); INK = (30, 26, 20); MUTED = (122, 111, 96); DEEP = (138, 106, 47)
CARD = (255, 255, 255); WHITE = (255, 255, 255); GLYPH = INK; PINK = DEEP
S = 4  # supersampling

def conic(size, radius):
    """The website's logo mark: rounded square with the conic green-rose gradient (.logo .mark)."""
    Z = size; img = Image.new("RGBA", (Z, Z)); px = img.load()
    stops = [(0, GREEN), (70, MINT2), (150, ROSE), (230, ROSE2), (300, ROSE_D), (360, GREEN)]
    cx, cy = Z * .55, Z * .45
    for y in range(Z):
        for x in range(Z):
            a = (math.degrees(math.atan2(x - cx, -(y - cy))) - 160) % 360
            for (a0, c0), (a1, c1) in zip(stops, stops[1:]):
                if a0 <= a <= a1:
                    t = (a - a0) / (a1 - a0); px[x, y] = tuple(round(c0[k] + (c1[k] - c0[k]) * t) for k in range(3)) + (255,); break
    m = Image.new("L", (Z, Z), 0); ImageDraw.Draw(m).rounded_rectangle((0, 0, Z - 1, Z - 1), radius=radius, fill=255)
    out = Image.new("RGBA", (Z, Z), (0, 0, 0, 0)); out.paste(img, (0, 0), m); return out

def linear(w, h, c0, c1, c2=None):
    """Green → (mint) → rose, like the website's buttons."""
    img = Image.new("RGBA", (w, h)); px = img.load()
    for x in range(w):
        t = x / max(1, w - 1)
        if c2: c = [c0[k] + (c1[k] - c0[k]) * min(1, t / .48) if t < .48 else c1[k] + (c2[k] - c1[k]) * (t - .48) / .52 for k in range(3)]
        else: c = [c0[k] + (c1[k] - c0[k]) * t for k in range(3)]
        for y in range(h): px[x, y] = tuple(round(v) for v in c) + (255,)
    return img

def save(img, name, size):
    img.resize(size, Image.LANCZOS).save(os.path.join(OUT, name), optimize=True)

def star(d, cx, cy, r, fill):
    pts = []
    for i in range(8):
        a = -math.pi / 2 + i * math.pi / 4
        rr = r if i % 2 == 0 else r * 0.32
        pts.append((cx + rr * math.cos(a), cy + rr * math.sin(a)))
    d.polygon(pts, fill=fill)

# ---- the Passlabb logo as on the website: mark + "passlabb" + "LUXURY TRIP DESIGN" (shown 210x42)
def logo():
    W, H = 260 * S, 42 * S
    img = Image.new("RGBA", (W, H), (0, 0, 0, 0)); d = ImageDraw.Draw(img)
    img.alpha_composite(conic(34 * S, 10 * S), (0, 4 * S))
    f = font("ExtraBold", 25 * S); x0 = 44 * S
    d.text((x0, -3 * S), "passlabb", font=f, fill=INK)
    wword = d.textlength("passlabb", font=f)
    ft = font("SemiBold", int(6.6 * S)); tag = "LUXURY TRIP DESIGN"; x = x0; y = 31 * S
    for ch in tag:
        d.text((x, y), ch, font=ft, fill=MUTED); x += d.textlength(ch, font=ft) + 2.75 * S
    d.line([(x + 4 * S, y + 4 * S), (x + 22 * S, y + 4 * S)], fill=GOLD, width=S)
    right = int(max(x0 + wword, x + 24 * S)) + S
    img = img.crop((0, 0, right, H))
    save(img, "logo.png", (round(img.width / S * 2), H // S * 2))
    save(conic(34 * S, 10 * S), "logo-mark.png", (68, 68))

# ---- letter confetti (header cluster and footer strips)
COLS = [GREEN, ROSE, GOLD, INK, MINT2, ROSE_D, GREEN]
def confetti(name, w, h, n, seed, sizes=(16, 30)):
    rnd = random.Random(seed)
    img = Image.new("RGBA", (w * S, h * S), (0, 0, 0, 0))
    placed = []
    for i in range(n * 6):
        if len(placed) >= n: break
        sz = rnd.randint(*sizes) * S
        x = rnd.randint(0, w * S - sz); y = rnd.randint(0, h * S - sz)
        if any(abs(x - px) < (sz + ps) * .55 and abs(y - py) < (sz + ps) * .55 for px, py, ps in placed): continue
        placed.append((x, y, sz))
        ch = rnd.choice("ppdpdpd"); col = rnd.choice(COLS)
        g = Image.new("RGBA", (sz * 2, sz * 2), (0, 0, 0, 0)); gd = ImageDraw.Draw(g)
        gd.text((sz * .45, sz * .15), ch, font=font(rnd.choice(["Black", "ExtraBold"]), sz), fill=col)
        g = g.rotate(rnd.uniform(-28, 28), resample=Image.BICUBIC)
        img.alpha_composite(g, (max(0, x - sz // 2), max(0, y - sz // 2)))
    save(img, name, (w * 2, h * 2))

# ---- round icons (white glyph on pink, shown 56x56)
def circle_icon(name, draw_glyph):
    Z = 56 * S; img = Image.new("RGBA", (Z, Z), (0, 0, 0, 0)); d = ImageDraw.Draw(img)
    g = linear(Z, Z, GREEN, MINT, ROSE); m = Image.new("L", (Z, Z), 0); ImageDraw.Draw(m).ellipse((0, 0, Z - 1, Z - 1), fill=255)
    img.paste(g, (0, 0), m); d = ImageDraw.Draw(img)
    draw_glyph(d, Z)
    save(img, f"ic-{name}.png", (112, 112))

def g_check(d, Z):
    w = int(Z * .075); d.line([(Z * .3, Z * .52), (Z * .44, Z * .66), (Z * .71, Z * .37)], fill=GLYPH, width=w, joint="curve")
    for p in [(Z * .3, Z * .52), (Z * .44, Z * .66), (Z * .71, Z * .37)]: d.ellipse((p[0] - w / 2, p[1] - w / 2, p[0] + w / 2, p[1] + w / 2), fill=GLYPH)
def g_clock(d, Z):
    w = int(Z * .06); c = Z / 2; r = Z * .23
    d.ellipse((c - r, c - r, c + r, c + r), outline=GLYPH, width=w)
    d.line([(c, c - r * .6), (c, c), (c + r * .45, c + r * .3)], fill=GLYPH, width=w, joint="curve")
def g_refund(d, Z):
    w = int(Z * .06); c = Z / 2; r = Z * .22
    d.arc((c - r, c - r, c + r, c + r), 200, 340, fill=GLYPH, width=w); d.arc((c - r, c - r, c + r, c + r), 20, 160, fill=GLYPH, width=w)
    a = math.radians(340); x, y = c + r * math.cos(a), c + r * math.sin(a); d.polygon([(x + Z * .07, y - Z * .02), (x - Z * .03, y - Z * .07), (x - Z * .01, y + Z * .06)], fill=GLYPH)
    a = math.radians(160); x, y = c + r * math.cos(a), c + r * math.sin(a); d.polygon([(x - Z * .07, y + Z * .02), (x + Z * .03, y + Z * .07), (x + Z * .01, y - Z * .06)], fill=GLYPH)
def g_warn(d, Z):
    w = int(Z * .055); c = Z / 2
    d.polygon([(c, Z * .26), (Z * .74, Z * .70), (Z * .26, Z * .70)], outline=GLYPH, width=w)
    d.line([(c, Z * .40), (c, Z * .53)], fill=GLYPH, width=w); d.ellipse((c - w * .65, Z * .575, c + w * .65, Z * .575 + w * 1.3), fill=GLYPH)
def g_receipt(d, Z):
    w = int(Z * .05)
    d.rounded_rectangle((Z * .32, Z * .25, Z * .68, Z * .75), radius=Z * .04, outline=GLYPH, width=w)
    for i, yy in enumerate([.38, .5, .62]): d.line([(Z * .4, Z * yy), (Z * (.6 if i < 2 else .52), Z * yy)], fill=GLYPH, width=w)
def g_cal(d, Z):
    w = int(Z * .05)
    d.rounded_rectangle((Z * .28, Z * .3, Z * .72, Z * .72), radius=Z * .05, outline=GLYPH, width=w)
    d.line([(Z * .28, Z * .43), (Z * .72, Z * .43)], fill=GLYPH, width=w)
    for xx in (.39, .61): d.line([(Z * xx, Z * .24), (Z * xx, Z * .34)], fill=GLYPH, width=w)
    for xx in (.4, .5, .6): d.ellipse((Z * xx - w * .6, Z * .55 - w * .6, Z * xx + w * .6, Z * .55 + w * .6), fill=GLYPH)
def g_heart(d, Z):
    c = Z / 2; r = Z * .11
    d.ellipse((c - 2 * r, Z * .33, c, Z * .33 + 2 * r), fill=GLYPH); d.ellipse((c, Z * .33, c + 2 * r, Z * .33 + 2 * r), fill=GLYPH)
    d.polygon([(c - 2 * r + Z * .005, Z * .33 + r * 1.25), (c + 2 * r - Z * .005, Z * .33 + r * 1.25), (c, Z * .72)], fill=GLYPH)
def g_edit(d, Z):
    w = int(Z * .07)
    d.line([(Z * .33, Z * .67), (Z * .64, Z * .36)], fill=GLYPH, width=w)
    d.polygon([(Z * .29, Z * .71), (Z * .3, Z * .6), (Z * .4, Z * .7)], fill=GLYPH)
    d.line([(Z * .27, Z * .75), (Z * .73, Z * .75)], fill=GLYPH, width=int(w * .6))

def g_lock(d, Z):
    w = int(Z * .055); c = Z / 2
    d.arc((c - Z * .12, Z * .25, c + Z * .12, Z * .5), 180, 360, fill=GLYPH, width=w)
    for xx in (c - Z * .12, c + Z * .12): d.line([(xx, Z * .37), (xx, Z * .46)], fill=GLYPH, width=w)
    d.rounded_rectangle((Z * .3, Z * .45, Z * .7, Z * .73), radius=Z * .05, fill=GLYPH)
def g_user(d, Z):
    w = int(Z * .055); c = Z / 2
    d.ellipse((c - Z * .1, Z * .26, c + Z * .1, Z * .46), outline=GLYPH, width=w)
    d.arc((c - Z * .2, Z * .52, c + Z * .2, Z * .92), 180, 360, fill=GLYPH, width=w)

# ---- small detail icons (pink line, shown 16x16)
def small_icon(name, fn):
    Z = 16 * S * 2; img = Image.new("RGBA", (Z, Z), (0, 0, 0, 0)); d = ImageDraw.Draw(img); fn(d, Z, int(Z * .085))
    save(img, f"si-{name}.png", (32, 32))
def s_cal(d, Z, w):
    d.rounded_rectangle((Z * .14, Z * .2, Z * .86, Z * .86), radius=Z * .08, outline=PINK, width=w); d.line([(Z * .14, Z * .4), (Z * .86, Z * .4)], fill=PINK, width=w)
    for xx in (.33, .67): d.line([(Z * xx, Z * .08), (Z * xx, Z * .26)], fill=PINK, width=w)
def s_user(d, Z, w):
    d.ellipse((Z * .32, Z * .1, Z * .68, Z * .46), outline=PINK, width=w); d.arc((Z * .14, Z * .52, Z * .86, Z * 1.2), 180, 360, fill=PINK, width=w)
def s_pin(d, Z, w):
    d.ellipse((Z * .22, Z * .08, Z * .78, Z * .64), outline=PINK, width=w); d.ellipse((Z * .42, Z * .27, Z * .58, Z * .43), fill=PINK)
    d.line([(Z * .27, Z * .5), (Z * .5, Z * .92), (Z * .73, Z * .5)], fill=PINK, width=w, joint="curve")
def s_clock(d, Z, w):
    d.ellipse((Z * .1, Z * .1, Z * .9, Z * .9), outline=PINK, width=w); d.line([(Z * .5, Z * .28), (Z * .5, Z * .52), (Z * .66, Z * .62)], fill=PINK, width=w, joint="curve")
def s_card(d, Z, w):
    d.rounded_rectangle((Z * .08, Z * .22, Z * .92, Z * .78), radius=Z * .08, outline=PINK, width=w); d.line([(Z * .08, Z * .42), (Z * .92, Z * .42)], fill=PINK, width=w)
def s_doc(d, Z, w):
    d.rounded_rectangle((Z * .2, Z * .08, Z * .8, Z * .92), radius=Z * .07, outline=PINK, width=w)
    for yy in (.35, .52, .69): d.line([(Z * .33, Z * yy), (Z * .67, Z * yy)], fill=PINK, width=w)
def s_money(d, Z, w):
    d.ellipse((Z * .1, Z * .1, Z * .9, Z * .9), outline=PINK, width=w); d.text((Z * .5, Z * .52), "M", font=font("Bold", int(Z * .42)), fill=PINK, anchor="mm")
def s_lock(d, Z, w):
    d.arc((Z * .3, Z * .08, Z * .7, Z * .5), 180, 360, fill=PINK, width=w)
    for xx in (.3, .7): d.line([(Z * xx, Z * .29), (Z * xx, Z * .44)], fill=PINK, width=w)
    d.rounded_rectangle((Z * .16, Z * .42, Z * .84, Z * .9), radius=Z * .08, outline=PINK, width=w)
    d.ellipse((Z * .45, Z * .58, Z * .55, Z * .68), fill=PINK)
def s_device(d, Z, w):
    d.rounded_rectangle((Z * .1, Z * .16, Z * .9, Z * .66), radius=Z * .06, outline=PINK, width=w)
    d.line([(Z * .3, Z * .84), (Z * .7, Z * .84)], fill=PINK, width=w); d.line([(Z * .5, Z * .66), (Z * .5, Z * .84)], fill=PINK, width=w)
def s_arrive(d, Z, w):
    s_cal(d, Z, w); d.line([(Z * .32, Z * .63), (Z * .46, Z * .76), (Z * .7, Z * .52)], fill=PINK, width=w, joint="curve")

# ---- pattern panel (payment pending): website gradient, "passlabb" words and gold stars, shown 200x260
def pattern():
    W, H = 200 * S, 260 * S
    img = linear(W, H, GREEN, MINT, ROSE)
    t = Image.new("RGBA", (W * 3, H * 3), (0, 0, 0, 0)); d = ImageDraw.Draw(t)
    f = font("Black", 28 * S); gap = 14 * S
    for i, yy in enumerate(range(0, H * 3, 40 * S)):
        x = -(i % 3) * 55 * S
        while x < W * 3:
            d.text((x, yy), "passlabb", font=f, fill=(255, 255, 255, 215) if i % 2 == 0 else (20, 20, 31, 40))
            x += d.textlength("passlabb", font=f) + gap
            star(d, x + 7 * S, yy + 19 * S, 8 * S, GOLD + (255,)); x += 14 * S + gap
    t = t.rotate(28, resample=Image.BICUBIC, center=(W * 1.5, H * 1.5))
    img.alpha_composite(t.crop((W, H, W * 2, H * 2)))
    m = Image.new("L", (W, H), 0); ImageDraw.Draw(m).rounded_rectangle((0, 0, W - 1, H - 1), radius=22 * S, fill=255)
    out = Image.new("RGBA", (W, H), (0, 0, 0, 0)); out.paste(img, (0, 0), m)
    save(out, "pattern.png", (400, 520))

# ---- trip picture fallback: the same gold pattern, wide, square corners (the email rounds them).
#      Used for the trip photo and header when none of the booked experiences has a photo. Shown up to 560x320.
def trip_pattern():
    W, H = 280 * S, 160 * S
    img = linear(W, H, GREEN, MINT, ROSE)
    t = Image.new("RGBA", (W * 3, H * 3), (0, 0, 0, 0)); d = ImageDraw.Draw(t)
    f = font("Black", 26 * S); gap = 14 * S
    for i, yy in enumerate(range(0, H * 3, 38 * S)):
        x = -(i % 3) * 55 * S
        while x < W * 3:
            d.text((x, yy), "passlabb", font=f, fill=(255, 255, 255, 215) if i % 2 == 0 else (20, 20, 31, 40))
            x += d.textlength("passlabb", font=f) + gap
            star(d, x + 7 * S, yy + 18 * S, 8 * S, GOLD + (255,)); x += 14 * S + gap
    t = t.rotate(22, resample=Image.BICUBIC, center=(W * 1.5, H * 1.5))
    img.alpha_composite(t.crop((W, H, W * 2, H * 2)))
    save(img.convert("RGB"), "trip-pattern.png", (1120, 640))

# ---- social media buttons (dark circle, white mark), shown 34x34
def social(name, draw):
    Z = 34 * S * 2; img = Image.new("RGBA", (Z, Z), (0, 0, 0, 0)); d = ImageDraw.Draw(img)
    d.ellipse((0, 0, Z - 1, Z - 1), fill=DEEP); draw(d, Z, int(Z * .06))
    save(img, f"so-{name}.png", (68, 68))
def so_instagram(d, Z, w):
    d.rounded_rectangle((Z * .29, Z * .29, Z * .71, Z * .71), radius=Z * .12, outline=WHITE, width=w)
    d.ellipse((Z * .41, Z * .41, Z * .59, Z * .59), outline=WHITE, width=w); d.ellipse((Z * .61, Z * .35, Z * .655, Z * .395), fill=WHITE)
def so_linkedin(d, Z, w):
    d.text((Z * .5, Z * .53), "in", font=font("Black", int(Z * .42)), fill=WHITE, anchor="mm")
def so_tiktok(d, Z, w):
    ww = int(Z * .075)
    d.line([(Z * .55, Z * .27), (Z * .55, Z * .62)], fill=WHITE, width=ww)
    d.ellipse((Z * .33, Z * .55, Z * .55 + ww / 2, Z * .75), outline=WHITE, width=ww)
    d.arc((Z * .55 - ww / 2, Z * .12, Z * .78, Z * .42), 90, 180, fill=WHITE, width=ww)
def so_youtube(d, Z, w):
    d.rounded_rectangle((Z * .24, Z * .32, Z * .76, Z * .68), radius=Z * .09, fill=WHITE)
    d.polygon([(Z * .45, Z * .41), (Z * .45, Z * .59), (Z * .6, Z * .5)], fill=INK)
def so_facebook(d, Z, w):
    d.text((Z * .53, Z * .55), "f", font=font("Black", int(Z * .5)), fill=WHITE, anchor="mm")

# ---- wave: cream edge laid over the bottom of the header photo (shown 560x46)
def wave(name, color):
    W, H = 560 * S, 46 * S
    img = Image.new("RGBA", (W, H), (0, 0, 0, 0)); d = ImageDraw.Draw(img)
    pts = [(0, H)]
    for x in range(0, W + 1, 4):
        t = x / W
        y = H * (0.62 - 0.38 * math.sin(t * math.pi * 1.35 + 0.35) * (1 - t * 0.25))
        pts.append((x, y))
    pts.append((W, H)); d.polygon(pts, fill=color)
    save(img, name, (1120, 92))

# ---- the same wave with the round icon sitting on its edge (shown 560x84). Email apps cannot overlap
#      elements reliably, so the icon is part of the picture; its top half shows over the header photo.
def wave_icon(icon_name, color):
    W, H = 560 * S, 84 * S
    img = Image.new("RGBA", (W, H), (0, 0, 0, 0)); d = ImageDraw.Draw(img)
    top = 38 * S; hh = H - top
    pts = [(0, H)]
    for x in range(0, W + 1, 4):
        t = x / W
        pts.append((x, top + hh * (0.62 - 0.38 * math.sin(t * math.pi * 1.35 + 0.35) * (1 - t * 0.25))))
    pts.append((W, H)); d.polygon(pts, fill=color)
    ic = Image.open(os.path.join(OUT, f"ic-{icon_name}.png")).convert("RGBA").resize((56 * S, 56 * S), Image.LANCZOS)
    ring = Image.new("RGBA", (62 * S, 62 * S), (0, 0, 0, 0)); ImageDraw.Draw(ring).ellipse((0, 0, 62 * S - 1, 62 * S - 1), fill=color)
    img.alpha_composite(ring, (23 * S, 11 * S)); img.alpha_composite(ic, (26 * S, 14 * S))
    save(img, f"wave-{icon_name}.png", (1120, 168))

if __name__ == "__main__":
    # the logo files (logo.png, logo-mark.png) come from tools/brand/make.py
    for n, fn in [("instagram", so_instagram), ("linkedin", so_linkedin), ("tiktok", so_tiktok), ("youtube", so_youtube), ("facebook", so_facebook)]:
        social(n, fn)
    confetti("confetti-head.png", 92, 56, 7, 3, (14, 26))
    confetti("confetti-foot-l.png", 210, 70, 13, 11, (14, 28))
    confetti("confetti-foot-r.png", 210, 70, 13, 29, (14, 28))
    for n, g in [("check", g_check), ("clock", g_clock), ("refund", g_refund), ("warn", g_warn), ("receipt", g_receipt), ("cal", g_cal), ("heart", g_heart), ("edit", g_edit), ("lock", g_lock), ("user", g_user)]:
        circle_icon(n, g)
    for n, g in [("cal", s_cal), ("user", s_user), ("pin", s_pin), ("clock", s_clock), ("card", s_card), ("doc", s_doc), ("money", s_money), ("arrive", s_arrive), ("lock", s_lock), ("device", s_device)]:
        small_icon(n, g)
    pattern()
    trip_pattern()
    wave("wave.png", CARD + (255,))
    for n in ["check", "clock", "refund", "warn", "receipt", "cal", "heart", "edit"]:
        wave_icon(n, CARD + (255,))
    print("assets written to", os.path.abspath(OUT))
