"""Offline image placeholder generator using Pillow.

Used as a free fallback when DALL-E (or any paid image API) is missing
credentials, returns a billing/quota error, or is otherwise unavailable.
The resulting PNG is a clean education-themed gradient card with the
title overlaid — good enough to keep the LMS UI populated until a real
image is generated.

Pillow is the only runtime dependency (already a transitive dep of Odoo
through ``reportlab`` on most installs). If Pillow is not importable we
raise so the caller knows to skip this provider and try the next one.
"""

from __future__ import annotations

import io
import logging
import random

_logger = logging.getLogger(__name__)

try:
    from PIL import Image, ImageDraw, ImageFont
except ImportError:  # pragma: no cover — Pillow is a soft dep
    Image = None
    ImageDraw = None
    ImageFont = None


# Soft education palettes (top-left, bottom-right gradient pairs)
_PALETTES = [
    ((59, 130, 246), (147, 197, 253)),   # blue
    ((16, 185, 129), (110, 231, 183)),   # emerald
    ((245, 158, 11), (252, 211, 77)),    # amber
    ((139, 92, 246), (196, 181, 253)),   # violet
    ((236, 72, 153), (249, 168, 212)),   # pink
    ((20, 184, 166), (153, 246, 228)),   # teal
    ((99, 102, 241), (165, 180, 252)),   # indigo
    ((220, 38, 38), (252, 165, 165)),    # rose
]


def _parse_size(size):
    try:
        w, h = (int(p) for p in str(size).lower().split('x'))
    except Exception:
        return 1024, 1024
    return max(64, min(2048, w)), max(64, min(2048, h))


def _wrap(draw, text, font, max_width, max_lines=6):
    words = (text or '').split()
    lines, current = [], ''
    for word in words:
        candidate = (current + ' ' + word).strip()
        if draw.textlength(candidate, font=font) <= max_width:
            current = candidate
        else:
            if current:
                lines.append(current)
            current = word
            if len(lines) >= max_lines:
                return lines
    if current and len(lines) < max_lines:
        lines.append(current)
    return lines


def _load_font(preferred, size):
    """Try a list of fonts; fall back to the bundled default at any size."""
    for name in preferred:
        try:
            return ImageFont.truetype(name, size=size)
        except Exception:
            continue
    return ImageFont.load_default()


def render_placeholder(title, *, subtitle=None, size='1024x1024', seed=None):
    """Return PNG bytes for a placeholder card.

    Args:
        title: Main title text rendered large and centred.
        subtitle: Optional smaller line below the title (e.g. CEFR level,
            week label) — pass ``None`` to skip.
        size: ``"WIDTHxHEIGHT"`` string, e.g. ``"1024x1024"``.
        seed: Optional seed for palette selection so the same title yields
            the same gradient repeatably.
    """
    if Image is None:
        raise RuntimeError(
            'Pillow not installed — pip install Pillow to enable the free '
            'image fallback.'
        )
    w, h = _parse_size(size)
    rng = random.Random(seed if seed is not None else (title or '').lower())
    top, bottom = rng.choice(_PALETTES)

    img = Image.new('RGB', (w, h), top)
    px = img.load()
    for y in range(h):
        t = y / max(1, h - 1)
        r = int(top[0] * (1 - t) + bottom[0] * t)
        g = int(top[1] * (1 - t) + bottom[1] * t)
        b = int(top[2] * (1 - t) + bottom[2] * t)
        for x in range(w):
            px[x, y] = (r, g, b)

    draw = ImageDraw.Draw(img)
    title_font = _load_font(
        ['DejaVuSans-Bold.ttf', 'Arial Bold.ttf', 'arial.ttf'],
        int(h * 0.07),
    )
    sub_font = _load_font(
        ['DejaVuSans.ttf', 'Arial.ttf', 'arial.ttf'],
        int(h * 0.035),
    )

    margin = int(w * 0.08)
    max_text_w = w - 2 * margin
    title_lines = _wrap(draw, title or 'Untitled', title_font, max_text_w)
    line_h = int(h * 0.085)
    total_h = line_h * len(title_lines)
    y = (h - total_h) // 2
    for line in title_lines:
        line_w = draw.textlength(line, font=title_font)
        x = (w - line_w) // 2
        draw.text((x + 2, y + 2), line, font=title_font, fill=(0, 0, 0))
        draw.text((x, y), line, font=title_font, fill=(255, 255, 255))
        y += line_h

    if subtitle:
        sub_w = draw.textlength(subtitle, font=sub_font)
        sx = (w - sub_w) // 2
        sy = y + int(h * 0.02)
        draw.text((sx + 1, sy + 1), subtitle, font=sub_font, fill=(0, 0, 0))
        draw.text((sx, sy), subtitle, font=sub_font, fill=(255, 255, 255))

    # Subtle EnCoach watermark badge so generated assets are easy to spot
    badge_font = _load_font(['DejaVuSans.ttf', 'Arial.ttf'], int(h * 0.022))
    badge = 'EnCoach · placeholder'
    bw = draw.textlength(badge, font=badge_font)
    draw.text(
        (w - margin - bw, h - margin),
        badge, font=badge_font, fill=(255, 255, 255),
    )

    buf = io.BytesIO()
    img.save(buf, format='PNG', optimize=True)
    return buf.getvalue()
