"""Brand-aligned color grading and image compression.

Provides three capabilities:

1. ``generate_cube_lut()`` — portable .cube LUT file from brand colors
   (pure Python, zero deps beyond stdlib). Consumable by ffmpeg,
   ImageMagick, Photoshop, DaVinci Resolve, etc.
2. ``apply_brand_lut()`` — Pillow Color3DLUT for in-process grading.
3. ``smush_photo()`` — resize + JPEG compress + EXIF strip for embedding.
"""

from __future__ import annotations

from io import BytesIO

from PIL import Image, ImageFilter


def _split_tone(
    r: float,
    g: float,
    b: float,
    sr: float,
    sg: float,
    sb: float,
    hr: float,
    hg: float,
    hb: float,
    intensity: float,
) -> tuple[float, float, float]:
    """Map an RGB pixel toward shadow/highlight colors by luminance.

    All values in 0-1 range. BT.601 luminance coefficients.
    """
    lum = 0.299 * r + 0.587 * g + 0.114 * b
    tr = sr + (hr - sr) * lum
    tg = sg + (hg - sg) * lum
    tb = sb + (hb - sb) * lum
    return (
        max(0.0, min(1.0, r + (tr - r) * intensity)),
        max(0.0, min(1.0, g + (tg - g) * intensity)),
        max(0.0, min(1.0, b + (tb - b) * intensity)),
    )


def _prepare_grade_params(
    shadow_rgb: tuple[int, int, int],
    highlight_rgb: tuple[int, int, int],
    intensity: float,
) -> tuple[float, float, float, float, float, float, float]:
    """Normalize 0-255 RGB + intensity to 0-1 floats."""
    return (
        shadow_rgb[0] / 255.0,
        shadow_rgb[1] / 255.0,
        shadow_rgb[2] / 255.0,
        highlight_rgb[0] / 255.0,
        highlight_rgb[1] / 255.0,
        highlight_rgb[2] / 255.0,
        max(0.0, min(1.0, intensity)),
    )


def apply_brand_lut(
    image: Image.Image,
    *,
    shadow_rgb: tuple[int, int, int],
    highlight_rgb: tuple[int, int, int],
    intensity: float = 0.7,
) -> Image.Image:
    """Apply brand-aligned split-tone color grade via Pillow Color3DLUT.

    Maps shadows toward ``shadow_rgb`` and highlights toward ``highlight_rgb``
    using luminance-based interpolation — the same effect as OOXML
    ``a:duotone`` but applied as a raster operation.

    Args:
        image: Input PIL Image (RGB).
        shadow_rgb: Target color for dark tones (0-255 per channel).
        highlight_rgb: Target color for bright tones (0-255 per channel).
        intensity: Blend strength 0.0 (no effect) to 1.0 (full grade).

    Returns:
        Color-graded PIL Image (RGB).
    """
    sr, sg, sb, hr, hg, hb, clamped = _prepare_grade_params(
        shadow_rgb,
        highlight_rgb,
        intensity,
    )

    def grade(r: float, g: float, b: float) -> tuple[float, float, float]:
        return _split_tone(r, g, b, sr, sg, sb, hr, hg, hb, clamped)

    lut = ImageFilter.Color3DLUT.generate(17, grade)

    if image.mode != "RGB":
        image = image.convert("RGB")
    return image.filter(lut)


def generate_cube_lut(
    *,
    shadow_rgb: tuple[int, int, int],
    highlight_rgb: tuple[int, int, int],
    intensity: float = 0.7,
    size: int = 33,
    title: str = "Brand Split-Tone",
) -> str:
    """Generate a .cube 3D LUT file for brand split-tone color grading.

    Pure Python — no image library required. The output is an industry-standard
    .cube file consumable by ffmpeg (``-vf lut3d``), ImageMagick, Photoshop,
    DaVinci Resolve, and any OCIO-compatible tool.

    Args:
        shadow_rgb: Target color for dark tones (0-255 per channel).
        highlight_rgb: Target color for bright tones (0-255 per channel).
        intensity: Blend strength 0.0 (no effect) to 1.0 (full grade).
        size: LUT cube dimension (17=preview, 33=production, 65=archival).
        title: Title metadata embedded in the .cube header.

    Returns:
        Complete .cube file content as a string.
    """
    sr, sg, sb, hr, hg, hb, clamped = _prepare_grade_params(
        shadow_rgb,
        highlight_rgb,
        intensity,
    )
    step = 1.0 / (size - 1)

    lines = [
        f'TITLE "{title}"',
        f"LUT_3D_SIZE {size}",
        "",
    ]

    for b_i in range(size):
        for g_i in range(size):
            for r_i in range(size):
                out_r, out_g, out_b = _split_tone(
                    r_i * step,
                    g_i * step,
                    b_i * step,
                    sr,
                    sg,
                    sb,
                    hr,
                    hg,
                    hb,
                    clamped,
                )
                lines.append(f"{out_r:.6f} {out_g:.6f} {out_b:.6f}")

    return "\n".join(lines) + "\n"


def smush_photo(
    data: bytes,
    *,
    max_width: int = 1920,
    max_height: int = 1080,
    quality: int = 82,
) -> bytes:
    """Resize and compress a photo for template embedding.

    Args:
        data: Raw image bytes (JPEG or PNG).
        max_width: Maximum output width in pixels.
        max_height: Maximum output height in pixels.
        quality: JPEG quality (1-95). 82 is visually lossless.

    Returns:
        Compressed JPEG bytes with EXIF stripped.
    """
    img = Image.open(BytesIO(data))
    if img.mode != "RGB":
        img = img.convert("RGB")

    # Thumbnail preserves aspect ratio and never upscales
    img.thumbnail((max_width, max_height), Image.LANCZOS)  # type: ignore[attr-defined]

    buf = BytesIO()
    img.save(buf, format="JPEG", quality=quality, optimize=True)
    return buf.getvalue()
