"""Constants and utilities related to OpenCV (cv2) codes (e.g., interpolation, border modes)."""

import cv2

# Type aliases
ConstantNames = tuple[str, ...]  # Immutable sequence of "cv2.CONST_NAME"

# # Define valid interpolation and border modes
VALID_INTERPOLATION_MODES = {
    cv2.INTER_NEAREST,
    cv2.INTER_LINEAR,
    cv2.INTER_CUBIC,
    cv2.INTER_AREA,
    cv2.INTER_LANCZOS4,
    cv2.INTER_LINEAR_EXACT,
    cv2.INTER_NEAREST_EXACT,
}

VALID_BORDER_MODES = {
    cv2.BORDER_CONSTANT,
    cv2.BORDER_REPLICATE,
    cv2.BORDER_REFLECT,
    cv2.BORDER_WRAP,
    cv2.BORDER_REFLECT_101,
}

# Define preferred names for constants with multiple names
PREFERRED_CONSTANT_NAMES = {
    cv2.BORDER_DEFAULT: "BORDER_REFLECT_101",  # Prefer REFLECT_101 over DEFAULT
}


class CV2ConstantHandler:
    """Handles CV2 constant management and conversions."""

    def __init__(self) -> None:
        # Cache the constant names to avoid repeated lookups
        self._interpolation_names: ConstantNames = self._get_interpolation_names()
        self._border_names: ConstantNames = self._get_border_names()

        # Create reverse mappings with both prefixed and unprefixed versions
        self._name_to_interpolation = {}
        interp_constants = self._get_cv2_constants("INTER_")
        for name, value in interp_constants.items():
            self._name_to_interpolation[f"cv2.{name}"] = value  # with prefix
            self._name_to_interpolation[name] = value  # without prefix

        self._name_to_border = {}
        border_constants = self._get_cv2_constants("BORDER_")
        for name, value in border_constants.items():
            self._name_to_border[f"cv2.{name}"] = value  # with prefix
            self._name_to_border[name] = value  # without prefix

    def _create_name_to_constant_map(self, prefix: str) -> dict[str, int]:
        """Create a mapping from constant names (with or without cv2 prefix) to their values."""
        constants = self._get_cv2_constants(prefix)
        mapping = {}
        for name, value in constants.items():
            # Skip if this value has a preferred name and this isn't it
            if value in PREFERRED_CONSTANT_NAMES and PREFERRED_CONSTANT_NAMES[value] != name:
                continue
            mapping[f"cv2.{name}"] = value  # With cv2 prefix
            mapping[name] = value  # Without cv2 prefix

        return mapping

    def get_interpolation_constant(self, name: str) -> int:
        """Convert interpolation constant name to its integer value.

        Args:
            name: String representation (e.g., "cv2.INTER_LINEAR" or "INTER_LINEAR")

        Returns:
            int: Integer value of the constant

        Raises:
            ValueError: If the name is not a valid interpolation constant
        """
        try:
            value = self._name_to_interpolation[name]
            if not self.is_interpolation(value):
                raise ValueError("Invalid interpolation value")
            return value
        except (KeyError, ValueError) as err:
            raise ValueError(f"Invalid interpolation constant name: {name}") from err

    def get_border_constant(self, name: str) -> int:
        """Convert border constant name to its integer value.

        Args:
            name: String representation (e.g., "cv2.BORDER_REFLECT" or "BORDER_REFLECT")

        Returns:
            int: Integer value of the constant

        Raises:
            ValueError: If the name is not a valid border constant
        """
        try:
            value = self._name_to_border[name]
            if not self.is_border_mode(value):
                raise ValueError("Invalid border mode value")
            return value
        except (KeyError, ValueError) as err:
            raise ValueError(f"Invalid border constant name: {name}") from err

    @property
    def interpolation_names(self) -> ConstantNames:
        """Get all valid interpolation constant names with cv2 prefix."""
        return self._interpolation_names

    @property
    def border_names(self) -> ConstantNames:
        """Get all valid border constant names with cv2 prefix."""
        return self._border_names

    @staticmethod
    def is_interpolation(value: int) -> bool:
        """Check if a value is a valid interpolation constant."""
        return value in VALID_INTERPOLATION_MODES

    @staticmethod
    def is_border_mode(value: int) -> bool:
        """Check if a value is a valid border mode constant."""
        return value in VALID_BORDER_MODES

    def get_constant_name(self, value: int, prefix: str) -> str:
        """Get the name of a cv2 constant given its value and prefix."""
        # Get all matching constants
        constants = self._get_cv2_constants(prefix)

        # Check if there's a preferred name for this value
        if value in PREFERRED_CONSTANT_NAMES:
            preferred_name = PREFERRED_CONSTANT_NAMES[value]
            if preferred_name.startswith(prefix):
                return preferred_name

        # Look for the value in our constants
        for name, const_value in constants.items():
            if const_value == value:
                return name

        raise ValueError(f"No constant found with value {value} and prefix {prefix}")

    def _get_cv2_constants(self, prefix: str) -> dict[str, int]:
        """Get all cv2 constants that start with the given prefix from our valid sets."""
        # Get all cv2 constants
        all_constants = {
            name: value for name, value in vars(cv2).items() if name.startswith(prefix) and isinstance(value, int)
        }

        # Get the valid set for this prefix
        valid_values = (
            VALID_INTERPOLATION_MODES if prefix == "INTER_" else VALID_BORDER_MODES if prefix == "BORDER_" else set()
        )

        # Create reverse mapping from values to names
        value_to_name = {}
        for name, value in all_constants.items():
            # If we have multiple names for the same value, prefer the one in our valid set
            if value in valid_values or value not in value_to_name:
                value_to_name[value] = name

        # Create final filtered dictionary
        return {
            name: value
            for name, value in all_constants.items()
            if value in valid_values and value_to_name[value] == name
        }

    def _get_interpolation_names(self) -> ConstantNames:
        """Get tuple of all valid interpolation constant names with cv2 prefix."""
        return tuple(f"cv2.{self.get_constant_name(mode, 'INTER_')}" for mode in sorted(VALID_INTERPOLATION_MODES))

    def _get_border_names(self) -> ConstantNames:
        """Get tuple of all valid border constant names with cv2 prefix."""
        return tuple(f"cv2.{self.get_constant_name(mode, 'BORDER_')}" for mode in sorted(VALID_BORDER_MODES))
