from pathlib import Path
from PIL import Image
import numpy as np

# Tunables
WHITE_THRESHOLD = 245        # how close to white a pixel must be
EDGE_SAMPLE_RATIO = 0.08    # % of image edges to sample
BACKGROUND_DISTANCE = 12    # tolerance for recoloring background


def normalize_white_background(
    image_path: Path,
    *,
    target_rgb=(207, 207, 207),
    inplace: bool = True,
) -> Path:
    """
    Detects near-white surrounding background and recolors it
    to a soft off-white / grayish-white.

    - Only operates if edges are predominantly white
    - Modifies background only, not subject
    - Safe for illustrations, watercolor art, scans

    Returns image path (same path if inplace).
    """

    img = Image.open(image_path).convert("RGB")
    w, h = img.size
    arr = np.asarray(img).astype(np.int16)

    edge_px = int(min(w, h) * EDGE_SAMPLE_RATIO)

    # Collect edge samples
    samples = np.concatenate([
        arr[:edge_px, :, :].reshape(-1, 3),          # top
        arr[-edge_px:, :, :].reshape(-1, 3),         # bottom
        arr[:, :edge_px, :].reshape(-1, 3),          # left
        arr[:, -edge_px:, :].reshape(-1, 3),         # right
    ])

    # Check if background is mostly white
    is_white = np.all(samples >= WHITE_THRESHOLD, axis=1)
    white_ratio = is_white.mean()

    # Require strong confidence
    if white_ratio < 0.92:
        return image_path  # background not clean white → do nothing

    # Compute distance from pure white
    dist_from_white = np.linalg.norm(arr - 255, axis=2)

    # Mask background-like pixels
    bg_mask = dist_from_white <= BACKGROUND_DISTANCE

    # Apply target color
    arr[bg_mask] = np.array(target_rgb, dtype=np.int16)

    out_img = Image.fromarray(arr.astype(np.uint8), "RGB")

    if inplace:
        out_img.save(image_path, format=img.format)
        return image_path

    out_path = image_path.with_name(f"{image_path.stem}_bg{image_path.suffix}")
    out_img.save(out_path, format=img.format)
    return out_path
