#!/usr/bin/env python3
"""Visuel kit social Tradition des Vosges — B2C + B2B, fond noir, liquid glass."""

from __future__ import annotations

import shutil
from pathlib import Path

import numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageFont

ROOT = Path(__file__).resolve().parents[1]
KIT = ROOT / "www/propositions/kit-social-tradition-des-vosges/visuels"
ASSETS = ROOT / "assets"
SHOT_B2C = KIT / "tdv-b2c-home.png"
SHOT_B2B = KIT / "tdv-b2b-home.png"

W, H = 1920, 1080
ORANGE = (233, 128, 48)
TERRACOTTA = (176, 92, 58)
DARK = (10, 9, 8)
CREAM = (236, 226, 210)
WHITE = (255, 255, 255)
INK = (22, 18, 16)

FONT_WOFF2 = ROOT / "www/wp-content/themes/numberone/fonts/Montserrat-ExtraBold.woff2"
FONT_EXTRABOLD = Path("/tmp/Montserrat-ExtraBold.ttf")
FONT_MULI_SB = Path("/tmp/Muli-SemiBold.ttf")
FONT_UI = "/System/Library/Fonts/Supplemental/Arial.ttf"
FONT_UI_BOLD = "/System/Library/Fonts/Supplemental/Arial Bold.ttf"

POINTS = [
    "Boutique particuliers + boutique professionnels",
    "Devis, compte pro, tarifs métier",
    "Hôtellerie, gîtes, détaillants, grossistes",
    "Accompagnement après la refonte",
]


def woff2_to_ttf(src: Path, dst: Path) -> str:
    if dst.exists():
        return str(dst)
    from fontTools.ttLib import TTFont

    font = TTFont(str(src))
    font.flavor = None
    font.save(str(dst))
    return str(dst)


def ensure_fonts() -> tuple[str, str]:
    extra = FONT_UI_BOLD
    muli_sb = FONT_UI_BOLD
    fonts = ROOT / "www/wp-content/themes/numberone/fonts"
    try:
        extra = woff2_to_ttf(FONT_WOFF2, FONT_EXTRABOLD)
        muli_sb = woff2_to_ttf(fonts / "Muli-SemiBold.woff2", FONT_MULI_SB)
    except Exception:
        pass
    return extra, muli_sb


def font(path: str, size: int) -> ImageFont.FreeTypeFont:
    return ImageFont.truetype(path, size)


def cover_top(img: Image.Image, size: tuple[int, int]) -> Image.Image:
    tw, th = size
    scale = max(tw / img.width, th / img.height)
    resized = img.resize(
        (max(1, int(img.width * scale)), max(1, int(img.height * scale))),
        Image.Resampling.LANCZOS,
    )
    left = (resized.width - tw) // 2
    return resized.crop((left, 0, left + tw, th))


def rounded_mask(size: tuple[int, int], radius: int) -> Image.Image:
    mask = Image.new("L", size, 0)
    ImageDraw.Draw(mask).rounded_rectangle((0, 0, size[0] - 1, size[1] - 1), radius=radius, fill=255)
    return mask


def drop_shadow_device(device: Image.Image, blur: int = 24, opacity: float = 0.48, offset=(10, 22)) -> Image.Image:
    alpha = device.split()[3]
    shadow = Image.new("RGBA", device.size, (0, 0, 0, 255))
    mask = alpha.point(lambda p: int(p * opacity))
    shadow.putalpha(mask)
    shadow = shadow.filter(ImageFilter.GaussianBlur(blur))
    pad = blur * 2
    layer = Image.new("RGBA", (device.width + pad * 2, device.height + pad * 2), (0, 0, 0, 0))
    layer.paste(shadow, (pad + offset[0], pad + offset[1]), shadow)
    layer.paste(device, (pad, pad), device)
    return layer


def radial_orb(size: tuple[int, int], center: tuple[int, int], radius: int, color: tuple[int, int, int], strength: int) -> Image.Image:
    glow = Image.new("RGBA", size, (0, 0, 0, 0))
    d = ImageDraw.Draw(glow)
    for r in range(radius, 0, -10):
        alpha = int(strength * (1 - r / radius) ** 2.2)
        d.ellipse((center[0] - r, center[1] - r, center[0] + r, center[1] + r), fill=(*color, alpha))
    return glow.filter(ImageFilter.GaussianBlur(36))


def liquid_glass_browser(
    screenshot: Image.Image,
    backdrop: Image.Image,
    xy: tuple[int, int],
    *,
    content_width: int = 1000,
    viewport_ratio: float = 0.50,
    url_label: str = "",
) -> Image.Image:
    w = content_width
    bar_h = max(50, int(w * 0.048))
    radius = max(24, int(w * 0.026))
    content_h = int(content_width * viewport_ratio)
    content = cover_top(screenshot.convert("RGB"), (w, content_h)).convert("RGBA")
    total_h = bar_h + content.height
    x, y = xy

    bx1 = max(0, x)
    by1 = max(0, y)
    bx2 = min(backdrop.width, x + w)
    by2 = min(backdrop.height, y + total_h)
    region = Image.new("RGB", (w, total_h), DARK)
    cropped = backdrop.crop((bx1, by1, bx2, by2)).convert("RGB")
    region.paste(cropped, (bx1 - x, by1 - y))
    shifted = region.copy()
    shifted.paste(region, (3, -2))
    frosted = Image.blend(region, shifted, 0.28).filter(ImageFilter.GaussianBlur(16))

    frost_tint = Image.new("RGBA", (w, total_h), (232, 238, 246, 36))
    glass = Image.blend(frosted.convert("RGBA"), frost_tint, 0.32)

    bar_tint = Image.new("RGBA", (w, bar_h + radius), (248, 250, 255, 64))
    bar_layer = Image.new("RGBA", (w, total_h), (0, 0, 0, 0))
    bar_layer.paste(bar_tint, (0, 0))
    glass = Image.alpha_composite(glass, bar_layer)

    spec = Image.new("RGBA", (w, total_h), (0, 0, 0, 0))
    sd = ImageDraw.Draw(spec)
    for i in range(bar_h + 110):
        alpha = int(70 * (1 - i / (bar_h + 110)) ** 1.45)
        sd.line((0, i, w, i), fill=(255, 255, 255, alpha))
    sd.ellipse((-int(w * 0.22), -int(total_h * 0.28), int(w * 0.62), int(total_h * 0.22)), fill=(255, 255, 255, 32))
    for i in range(28):
        a = int(42 * (1 - i / 28))
        sd.line((i, 0, i, total_h), fill=(255, 255, 255, a))
    glass = Image.alpha_composite(glass, spec)

    mask = rounded_mask((w, total_h), radius)
    glass.putalpha(mask)

    inner = Image.new("L", (w, content.height), 0)
    ImageDraw.Draw(inner).rounded_rectangle(
        (0, -radius, w - 1, content.height - 1),
        radius=max(14, radius - 4),
        fill=255,
    )
    content.putalpha(inner)

    out = Image.new("RGBA", (w, total_h), (0, 0, 0, 0))
    out.paste(glass, (0, 0), glass)
    out.paste(content, (0, bar_h), content)

    out_a = np.minimum(np.array(out.split()[3]), np.array(mask))
    out.putalpha(Image.fromarray(out_a, "L"))

    overlay = Image.new("RGBA", (w, total_h), (0, 0, 0, 0))
    od = ImageDraw.Draw(overlay)
    od.rounded_rectangle((0, 0, w - 1, total_h - 1), radius=radius, outline=(255, 255, 255, 110), width=2)
    od.rounded_rectangle((2, 2, w - 3, total_h - 3), radius=max(16, radius - 2), outline=(255, 255, 255, 40), width=1)
    od.line((16, bar_h, w - 16, bar_h), fill=(255, 255, 255, 50), width=1)

    cy = bar_h // 2
    for i, color in enumerate([(255, 95, 87, 220), (255, 189, 46, 220), (40, 202, 65, 220)]):
        cx = 26 + i * 22
        od.ellipse((cx - 7, cy - 7, cx + 7, cy + 7), fill=color)
        od.ellipse((cx - 7, cy - 7, cx + 7, cy + 7), outline=(255, 255, 255, 70), width=1)
    pill_w = int(w * 0.52)
    px1 = (w - pill_w) // 2 + 18
    od.rounded_rectangle((px1, cy - 13, px1 + pill_w, cy + 13), radius=13, fill=(255, 255, 255, 48))
    od.rounded_rectangle((px1, cy - 13, px1 + pill_w, cy + 13), radius=13, outline=(255, 255, 255, 55), width=1)
    if url_label:
        f = font(FONT_UI, max(13, int(w * 0.016)))
        od.text((px1 + pill_w // 2, cy), url_label, font=f, fill=(255, 255, 255, 210), anchor="mm")

    out = Image.alpha_composite(out, overlay)
    return drop_shadow_device(out, blur=26, opacity=0.38, offset=(10, 22))


def compose() -> Image.Image:
    extra, muli_sb = ensure_fonts()
    b2c = Image.open(SHOT_B2C)
    b2b = Image.open(SHOT_B2B)

    canvas = Image.new("RGBA", (W, H), (*DARK, 255))
    canvas.alpha_composite(radial_orb((W, H), (180, 40), 640, TERRACOTTA, 42))
    canvas.alpha_composite(radial_orb((W, H), (1680, 80), 700, ORANGE, 44))
    canvas.alpha_composite(radial_orb((W, H), (920, -80), 420, (255, 160, 90), 18))

    title = Image.new("RGBA", (W, H), (0, 0, 0, 0))
    td = ImageDraw.Draw(title)
    f_title = font(extra, 78)
    f_brand = font(muli_sb, 28)
    td.text((56, 36), "REFONTE B2C + B2B", font=f_title, fill=(*CREAM, 230))
    td.text((56, 128), "PRESTASHOP", font=f_title, fill=(*CREAM, 230))
    td.text((56, 226), "Tradition des Vosges  ·  depuis 1856", font=f_brand, fill=(*ORANGE, 235))
    canvas.alpha_composite(title)

    draw = ImageDraw.Draw(canvas)
    f_point = font(muli_sb, 26)
    y = 290
    for line in POINTS:
        draw.ellipse((68, y + 8, 82, y + 22), fill=ORANGE)
        draw.text((100, y), line, font=f_point, fill=(*CREAM, 235))
        y += 46

    bx, by = 690, 70
    browser_b2c = liquid_glass_browser(
        b2c,
        canvas,
        (bx, by),
        content_width=1160,
        viewport_ratio=0.50,
        url_label="traditiondesvosges.com  ·  B2C",
    )
    canvas.alpha_composite(browser_b2c, (bx - 52, by - 52))

    px, py = 48, 548
    browser_b2b = liquid_glass_browser(
        b2b,
        canvas,
        (px, py),
        content_width=720,
        viewport_ratio=0.46,
        url_label="pro.traditiondesvosges.com  ·  B2B",
    )
    canvas.alpha_composite(browser_b2b, (px - 52, py - 52))

    url_font = font(extra, 22)
    draw.text((W - 48, H - 32), "arnaud-merigeau.fr", font=url_font, fill=ORANGE, anchor="rd")
    return canvas.convert("RGB")


def main() -> None:
    visual = compose()
    KIT.mkdir(parents=True, exist_ok=True)
    jpg = KIT / "visuel-tradition-des-vosges-b2c-b2b.jpg"
    png = KIT / "visuel-tradition-des-vosges-b2c-b2b.png"
    visual.save(jpg, "JPEG", quality=92, optimize=True, subsampling=0)
    visual.save(png, "PNG")
    shutil.copy2(jpg, ASSETS / "visuel-tradition-des-vosges-b2c-b2b.jpg")
    print(f"saved {jpg} {visual.size}")
    print(f"saved {png}")


if __name__ == "__main__":
    main()
