#!/usr/bin/env python3
"""Visuels kit social semaine absence — 1920×1080, charte arnaud-merigeau.fr (style comparatif PS/Shopify)."""

from __future__ import annotations

import json
import textwrap
from pathlib import Path

from PIL import Image, ImageDraw, ImageFilter, ImageFont

ROOT = Path(__file__).resolve().parents[1]
KIT = ROOT / "www/propositions/planning-social-semaine-absence-2026/visuels"
POSTS = ROOT / "www/propositions/planning-social-semaine-absence-2026/daily_posts.json"

W, H = 1920, 1080
ORANGE = (233, 128, 48)
PEACH = (255, 202, 161)
INK = (0, 0, 0)
MUTED = (102, 102, 102)
WHITE = (255, 255, 255)


def load_font(
    size: int,
    *,
    bold: bool = False,
    black: bool = False,
) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
    if black:
        candidates = [
            "/System/Library/Fonts/Supplemental/Arial Black.ttf",
            "/Library/Fonts/Arial Black.ttf",
            "/System/Library/Fonts/Supplemental/Arial Bold.ttf",
        ]
    elif bold:
        candidates = [
            "/System/Library/Fonts/Supplemental/Arial Bold.ttf",
            "/Library/Fonts/Arial Bold.ttf",
        ]
    else:
        candidates = [
            "/System/Library/Fonts/Supplemental/Arial.ttf",
            "/Library/Fonts/Arial.ttf",
        ]
    for path in candidates:
        if Path(path).exists():
            return ImageFont.truetype(path, size)
    return ImageFont.load_default()


def draw_heavy_title(
    draw: ImageDraw.ImageDraw,
    xy: tuple[int, int],
    text: str,
    font: ImageFont.ImageFont,
    *,
    fill: tuple[int, int, int] = INK,
) -> None:
    x, y = xy
    for dx, dy in ((0, 0), (1, 0), (0, 1), (1, 1)):
        draw.text((x + dx, y + dy), text, fill=fill, font=font, anchor="mm")


def add_blur_halo(
    base: Image.Image,
    center: tuple[int, int],
    radius: int,
    color: tuple[int, int, int, int],
    blur: int,
) -> Image.Image:
    layer = Image.new("RGBA", base.size, (0, 0, 0, 0))
    draw = ImageDraw.Draw(layer)
    cx, cy = center
    draw.ellipse((cx - radius, cy - radius, cx + radius, cy + radius), fill=color)
    layer = layer.filter(ImageFilter.GaussianBlur(radius=blur))
    return Image.alpha_composite(base.convert("RGBA"), layer)


def fit_title_lines(headline: str, max_lines: int = 3, width: int = 14) -> list[str]:
    words = headline.upper().split()
    lines: list[str] = []
    current: list[str] = []
    for word in words:
        trial = " ".join(current + [word])
        if len(trial) > width and current:
            lines.append(" ".join(current))
            current = [word]
        else:
            current.append(word)
    if current:
        lines.append(" ".join(current))
    if len(lines) > max_lines:
        merged = lines[: max_lines - 1]
        merged.append(" ".join(lines[max_lines - 1 :]))
        lines = merged
    return lines[:max_lines]


def render(headline: str, subline: str, *, badge: str | None = None) -> Image.Image:
    img = Image.new("RGB", (W, H), (255, 252, 248))
    draw = ImageDraw.Draw(img)

    for y in range(H):
        t = y / H
        r = int(255 - t * 3)
        g = int(252 - t * 5)
        b = int(248 - t * 8)
        draw.line([(0, y), (W, y)], fill=(r, g, b))

    img = add_blur_halo(img, (1180, 220), 420, (233, 128, 48, 62), blur=120)
    img = add_blur_halo(img, (260, 420), 360, (255, 190, 130, 52), blur=105)
    img = add_blur_halo(img, (1520, 760), 320, (236, 192, 89, 42), blur=90)
    img = add_blur_halo(img, (760, 900), 280, (233, 128, 48, 38), blur=80)
    img = add_blur_halo(img, (960, 540), 680, (255, 220, 190, 28), blur=140)
    img = img.convert("RGB")
    draw = ImageDraw.Draw(img)

    title_lines = fit_title_lines(headline)
    n = len(title_lines)
    font_title = load_font(108 if n <= 2 else 88, black=True)
    font_sub = load_font(38)
    font_badge = load_font(30, bold=True)
    font_url = load_font(22, bold=True)

    block_h = n * 98 + 40
    y_start = (H - block_h) // 2 - 40

    for i, line in enumerate(title_lines):
        y = y_start + i * 98
        draw_heavy_title(draw, (W // 2, y), line, font_title)

    sub_y = y_start + n * 98 + 24
    for line in textwrap.wrap(subline, width=48):
        draw.text((W // 2, sub_y), line, fill=ORANGE, font=font_sub, anchor="mm")
        sub_y += 48

    if badge:
        tw = draw.textlength(badge, font=font_badge)
        bx = (W - tw - 56) // 2
        by = sub_y + 20
        draw.rounded_rectangle((bx, by, bx + tw + 56, by + 58), radius=18, fill=ORANGE)
        draw.text((bx + 28, by + 29), badge, fill=WHITE, font=font_badge, anchor="lm")

    draw.text((W - 64, H - 48), "arnaud-merigeau.fr", fill=MUTED, font=font_url, anchor="rb")

    last_line = title_lines[-1]
    dot_x = W // 2 + int(draw.textlength(last_line, font=font_title) // 2) + 18
    dot_y = y_start + (n - 1) * 98 - 30
    draw.ellipse((dot_x, dot_y, dot_x + 22, dot_y + 22), fill=ORANGE)

    return img


def main() -> int:
    KIT.mkdir(parents=True, exist_ok=True)
    posts = json.loads(POSTS.read_text(encoding="utf-8"))

    for post in posts:
        visual = post.get("visual", {})
        headline = visual.get("headline") or post["title"]
        subline = visual.get("subline") or post.get("resource_label", "")
        badge = visual.get("badge")
        out_jpg = KIT / Path(visual["file"]).name
        out_png = out_jpg.with_suffix(".png")
        img = render(headline, subline, badge=badge)
        img.save(out_jpg, "JPEG", quality=92, optimize=True)
        img.save(out_png, "PNG", optimize=True)
        print(f"✓ {out_jpg.name}")

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
