#!/usr/bin/env -S uv run
# /// script
# dependencies = ["pillow>=11.0.0"]
# ///
"""Create a LinkedIn landscape packshot layout.

Layout:
- left third: original image
- middle third: main/best generated image
- right third: two generated variants stacked
"""
from __future__ import annotations

import argparse
from pathlib import Path

from PIL import Image, ImageDraw, ImageFilter, ImageFont

FONT_BOLD = "/System/Library/Fonts/Supplemental/Arial Bold.ttf"
FONT_REGULAR = "/System/Library/Fonts/Supplemental/Arial.ttf"


def load_font(size: int, bold: bool = False):
    try:
        return ImageFont.truetype(FONT_BOLD if bold else FONT_REGULAR, size=size)
    except Exception:
        return ImageFont.load_default()


def contain(img: Image.Image, box: tuple[int, int]) -> Image.Image:
    out = img.copy()
    out.thumbnail(box, Image.Resampling.LANCZOS)
    return out


def cover_blur(img: Image.Image, box: tuple[int, int]) -> Image.Image:
    w, h = box
    iw, ih = img.size
    scale = max(w / iw, h / ih)
    resized = img.resize((round(iw * scale), round(ih * scale)), Image.Resampling.LANCZOS)
    x = (resized.width - w) // 2
    y = (resized.height - h) // 2
    return resized.crop((x, y, x + w, y + h)).filter(ImageFilter.GaussianBlur(22))


def draw_text_center(draw: ImageDraw.ImageDraw, center_x: int, y: int, text: str, font, fill):
    bbox = draw.textbbox((0, 0), text, font=font)
    draw.text((center_x - (bbox[2] - bbox[0]) // 2, y), text, font=font, fill=fill)


def draw_badge(draw: ImageDraw.ImageDraw, panel: tuple[int, int, int, int], text: str, subtext: str | None = None):
    x, y, w, h = panel
    title_font = load_font(max(38, w // 23), bold=True)
    sub_font = load_font(max(24, w // 38), bold=False)
    pad_x = max(24, w // 42)
    pad_y = max(14, w // 70)
    title_bbox = draw.textbbox((0, 0), text, font=title_font)
    sub_bbox = draw.textbbox((0, 0), subtext or "", font=sub_font)
    text_w = max(title_bbox[2] - title_bbox[0], sub_bbox[2] - sub_bbox[0])
    text_h = (title_bbox[3] - title_bbox[1]) + (sub_bbox[3] - sub_bbox[1] if subtext else 0) + (8 if subtext else 0)
    bw = text_w + pad_x * 2
    bh = text_h + pad_y * 2
    bx = x + (w - bw) // 2
    by = y + max(20, w // 40)
    draw.rounded_rectangle((bx, by, bx + bw, by + bh), radius=bh // 2, fill=(255, 255, 255), outline=(220, 220, 216), width=2)
    draw_text_center(draw, bx + bw // 2, by + pad_y - 3, text, title_font, (18, 18, 18))
    if subtext:
        draw_text_center(draw, bx + bw // 2, by + pad_y + (title_bbox[3] - title_bbox[1]) + 6, subtext, sub_font, (82, 82, 82))


def draw_panel(canvas: Image.Image, panel: tuple[int, int, int, int], image_path: Path, label: str, sublabel: str | None, use_blur: bool = False):
    x, y, w, h = panel
    draw = ImageDraw.Draw(canvas)
    shadow = Image.new("RGBA", canvas.size, (0, 0, 0, 0))
    sdraw = ImageDraw.Draw(shadow)
    sdraw.rounded_rectangle((x + 8, y + 10, x + w + 8, y + h + 10), radius=28, fill=(0, 0, 0, 28))
    canvas.alpha_composite(shadow.filter(ImageFilter.GaussianBlur(14)))

    draw.rounded_rectangle((x, y, x + w, y + h), radius=28, fill=(255, 255, 255), outline=(222, 222, 218), width=2)

    img = Image.open(image_path).convert("RGB")
    inner_pad = max(26, w // 38)
    content = (x + inner_pad, y + inner_pad, w - inner_pad * 2, h - inner_pad * 2)
    cx, cy, cw, ch = content

    if use_blur:
        bg = cover_blur(img, (cw, ch)).convert("RGBA")
        overlay = Image.new("RGBA", bg.size, (255, 255, 255, 118))
        bg.alpha_composite(overlay)
        mask = Image.new("L", bg.size, 255)
        canvas.alpha_composite(bg, (cx, cy))

    fitted = contain(img, (cw, ch))
    px = cx + (cw - fitted.width) // 2
    py = cy + (ch - fitted.height) // 2
    canvas.alpha_composite(fitted.convert("RGBA"), (px, py))
    draw_badge(draw, panel, label, sublabel)


def main():
    p = argparse.ArgumentParser()
    p.add_argument("--original", required=True)
    p.add_argument("--main", required=True)
    p.add_argument("--top", required=True)
    p.add_argument("--bottom", required=True)
    p.add_argument("--out", required=True)
    p.add_argument("--main-label", default="4K flat-lay")
    p.add_argument("--main-score", default="9/11")
    p.add_argument("--top-label", default="4K ghost")
    p.add_argument("--top-score", default="8/11")
    p.add_argument("--bottom-label", default="4K folded")
    p.add_argument("--bottom-score", default="8.5/11")
    p.add_argument("--width", type=int, default=3600)
    p.add_argument("--height", type=int, default=2025)
    args = p.parse_args()

    W, H = args.width, args.height
    margin = 42
    gutter = 30
    col_w = (W - margin * 2 - gutter * 2) // 3
    full_h = H - margin * 2
    right_h = (full_h - gutter) // 2

    left = (margin, margin, col_w, full_h)
    middle = (margin + col_w + gutter, margin, col_w, full_h)
    top = (margin + (col_w + gutter) * 2, margin, col_w, right_h)
    bottom = (margin + (col_w + gutter) * 2, margin + right_h + gutter, col_w, full_h - right_h - gutter)

    canvas = Image.new("RGBA", (W, H), (247, 246, 242, 255))
    draw_panel(canvas, left, Path(args.original), "original", "camera roll input", use_blur=True)
    draw_panel(canvas, middle, Path(args.main), args.main_label, args.main_score, use_blur=False)
    draw_panel(canvas, top, Path(args.top), args.top_label, args.top_score, use_blur=False)
    draw_panel(canvas, bottom, Path(args.bottom), args.bottom_label, args.bottom_score, use_blur=False)

    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    canvas.convert("RGB").save(out, format="PNG", optimize=True)
    print(out)


if __name__ == "__main__":
    main()
