"""Image encoding/decoding/resizing utilities using OpenCV.

Replaces PIL/Pillow for JPEG encoding, image loading, and resizing operations.
All functions work with numpy RGB arrays (H, W, 3) uint8.
"""

import base64
from pathlib import Path
from typing import Tuple, Union

import cv2
import numpy as np


def encode_jpeg(image: np.ndarray, quality: int = 85) -> bytes:
    """Encode an RGB numpy array as JPEG bytes."""
    bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    ok, buf = cv2.imencode(".jpg", bgr, [cv2.IMWRITE_JPEG_QUALITY, quality])
    if not ok:
        raise RuntimeError("JPEG encoding failed")
    return buf.tobytes()


def encode_jpeg_data_uri(image: np.ndarray, quality: int = 85) -> str:
    """Encode an RGB numpy array as a JPEG base64 data URI."""
    raw = encode_jpeg(image, quality)
    b64 = base64.b64encode(raw).decode("utf-8")
    return f"data:image/jpeg;base64,{b64}"


def resize_image(image: np.ndarray, width: int, height: int) -> np.ndarray:
    """Resize an image to exact dimensions.

    Uses INTER_AREA for downscaling (better quality, faster) and
    INTER_LANCZOS4 for upscaling.
    """
    h, w = image.shape[:2]
    shrinking = (width * height) < (w * h)
    interp = cv2.INTER_AREA if shrinking else cv2.INTER_LANCZOS4
    return cv2.resize(image, (width, height), interpolation=interp)


def thumbnail(image: np.ndarray, max_width: int) -> np.ndarray:
    """Create a thumbnail that fits within max_width, preserving aspect ratio.

    Uses INTER_AREA (optimal for downscaling).
    """
    h, w = image.shape[:2]
    if w <= max_width:
        return image.copy()
    scale = max_width / w
    new_w = max_width
    new_h = max(1, int(h * scale))
    return cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA)


def resize_down(image: np.ndarray, max_width: int) -> np.ndarray:
    """Downscale if wider than max_width; return as-is otherwise.

    Uses INTER_AREA (optimal for downscaling).
    """
    h, w = image.shape[:2]
    if w <= max_width:
        return image
    scale = max_width / w
    new_w = max_width
    new_h = max(1, int(h * scale))
    return cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA)


def load_image_file(path: Union[str, Path]) -> np.ndarray:
    """Load an image file and return as RGB numpy array."""
    path = str(path)
    bgr = cv2.imread(path, cv2.IMREAD_COLOR)
    if bgr is None:
        raise FileNotFoundError(f"Cannot load image: {path}")
    return cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)


def load_image_bytes(data: bytes) -> np.ndarray:
    """Decode image bytes (JPEG, PNG, etc.) and return as RGB numpy array."""
    arr = np.frombuffer(data, dtype=np.uint8)
    bgr = cv2.imdecode(arr, cv2.IMREAD_COLOR)
    if bgr is None:
        raise ValueError("Cannot decode image data")
    return cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)


def image_size(image: np.ndarray) -> Tuple[int, int]:
    """Return (width, height) of an image array."""
    return image.shape[1], image.shape[0]
