import cv2
import numpy as np
from functools import lru_cache

def _clip_roi(x, y, w, h, W, H):
    x0 = max(x, 0); y0 = max(y, 0)
    x1 = min(x + w, W); y1 = min(y + h, H)
    return x0, y0, x1, y1

@lru_cache(maxsize=16)
def load_rgba(path: str):
    img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
    if img is None or img.shape[2] != 4:
        raise ValueError(f"Overlay must be an RGBA PNG: {path}")
    return img

def overlay_rgba(bgr, rgba, x, y, target_w=None):
    H, W = bgr.shape[:2]
    if target_w is not None:
        scale = target_w / rgba.shape[1]
        new_w = max(1, int(rgba.shape[1] * scale))
        new_h = max(1, int(rgba.shape[0] * scale))
        rgba = cv2.resize(rgba, (new_w, new_h), interpolation=cv2.INTER_AREA)

    h, w = rgba.shape[:2]
    x0, y0, x1, y1 = _clip_roi(x, y, w, h, W, H)
    if x1 <= x0 or y1 <= y0:
        return bgr

    ov = rgba[(y0 - y):(y1 - y), (x0 - x):(x1 - x)]
    bg = bgr[y0:y1, x0:x1]

    rgb = ov[..., :3].astype(np.float32)
    alpha = ov[..., 3:4].astype(np.float32) / 255.0
    out = alpha * rgb + (1 - alpha) * bg.astype(np.float32)
    bgr[y0:y1, x0:x1] = np.clip(out, 0, 255).astype(np.uint8)
    return bgr
