import numpy as np
from PIL import Image
from collections.abc import Callable as Callable
from numpy.typing import NDArray as NDArray
from typing import Any

def image_resize[T: Image.Image | NDArray[np.number]](image: T, max_result_size: int, resampling: Image.Resampling | None = None, min_or_max: Callable[[int, int], int] = ..., return_type: type[T] | str = 'same', keep_aspect_ratio: bool = True) -> Any:
    ''' Resize an image while preserving its aspect ratio by default.
\tScales the image so that its largest dimension equals max_result_size.

\tArgs:
\t\timage             (Image.Image | NDArray):    The image to resize.
\t\tmax_result_size   (int):                      Maximum size for the largest dimension.
\t\tresampling        (Image.Resampling | None):  PIL resampling filter to use (default: Image.Resampling.LANCZOS).
\t\tmin_or_max        (Callable):                 Function to use to get the minimum or maximum of the two ratios.
\t\treturn_type       (type | str):               Type of the return value (Image.Image, np.ndarray, or "same" to match input type).
\t\tkeep_aspect_ratio (bool):                     Whether to keep the aspect ratio.
\tReturns:
\t\tImage.Image | NDArray[np.number]: The resized image with preserved aspect ratio.
\tExamples:
\t\t>>> # Test with (height x width x channels) numpy array
\t\t>>> import numpy as np
\t\t>>> array = np.random.randint(0, 255, (100, 50, 3), dtype=np.uint8)
\t\t>>> image_resize(array, 100).shape
\t\t(100, 50, 3)
\t\t>>> image_resize(array, 100, min_or_max=max).shape
\t\t(100, 50, 3)
\t\t>>> image_resize(array, 100, min_or_max=min).shape
\t\t(200, 100, 3)

\t\t>>> # Test with PIL Image
\t\t>>> from PIL import Image
\t\t>>> pil_image: Image.Image = Image.new(\'RGB\', (200, 100))
\t\t>>> image_resize(pil_image, 50).size
\t\t(50, 25)
\t\t>>> # Test with different return types
\t\t>>> resized_array = image_resize(array, 50, return_type=np.ndarray)
\t\t>>> isinstance(resized_array, np.ndarray)
\t\tTrue
\t\t>>> resized_array.shape
\t\t(50, 25, 3)
\t\t>>> # Test with different resampling methods
\t\t>>> image_resize(pil_image, 50, resampling=Image.Resampling.NEAREST).size
\t\t(50, 25)
\t'''
