import logging
from typing import Any, List, Dict, Tuple, Optional


logger = logging.getLogger(__name__)


class Font:
    """
    Configuration container for OpenCV text rendering parameters.

    This class encapsulates all stylistic and geometric properties required for
    drawing text on video frames, including color, font face, scale, and stroke
    thickness. Designed to provide a type-safe, validated interface for consistent
    typography across computer vision pipelines and ADAS monitoring systems.

    Key features:
    - Strict validation for RGB color tuples [0, 255]
    - Constrained font face selection (OpenCV standard identifiers 0-7)
    - Configurable scale and stroke thickness for visibility tuning
    - Immutable validation on assignment with descriptive error messages

    Designed for seamless integration with downstream rendering utilities such as:
    - Drawer class for frame overlay operations
    - Real-time status/label rendering
    - Performance-optimized embedded deployments (e.g., Raspberry Pi, Jetson)

    Attributes:
        _color (Tuple[int, int, int]): RGB color tuple for text rendering.
        _font (int): OpenCV font type identifier (0-7).
        _font_scale (int): Scale factor multiplied by font-specific base size.
        _thickness (int): Stroke thickness in pixels.

    Note:
        - Parameters map directly to OpenCV's `cv2.putText()` arguments
        - `font_scale` is typed as `int` for consistency, though OpenCV accepts `float`
        - Validation occurs on every property assignment
    """

    def __init__(self,
                 color: tuple,
                 font: int = 1,
                 font_scale: int = 3,
                 thickness: int = 3):
        """
        Initializes a Font object with text rendering parameters.

        Args:
            color (Tuple[int, int, int]): RGB color tuple for text rendering (e.g., (255, 255, 255)).
            font (int, optional): OpenCV font type identifier (0-7). Defaults to 1 (FONT_HERSHEY_PLAIN).
            font_scale (int, optional): Scale factor multiplied by font-specific base size. Defaults to 3.
            thickness (int, optional): Stroke thickness in pixels. Defaults to 3.

        Raises:
            TypeError: If any parameter has incorrect type.
            ValueError: If numeric parameters are outside valid ranges.

        Note:
            - All validation is delegated to property setters
            - `show=False` in Style completely bypasses rendering, but Font config remains valid
        """
        self.color = color
        self.font = font
        self.font_scale = font_scale
        self.thickness = thickness

    @property
    def color(self) -> Tuple[int, int, int]:
        """
        Tuple[int, int, int]: Gets the RGB color used for text rendering.

        Returns:
            Tuple[int, int, int]: RGB color tuple where each component is in [0, 255].
        """
        return self._color

    @color.setter
    def color(self, color: Tuple[int, int, int]) -> None:
        """
        Sets the RGB color for text rendering with comprehensive validation.

        Args:
            color (Tuple[int, int, int]): New color as RGB tuple.

        Raises:
            TypeError: If color is not a tuple or contains non-integer values.
            ValueError: If any RGB component is outside [0, 255].

        Note:
            - Expects exactly 3 integers (R, G, B)
            - Validation ensures OpenCV compatibility and prevents clipping artifacts
        """
        if not isinstance(color, tuple):
            raise TypeError(f"'color' must be '<Tuple[int, int, int]>', but got {type(color).__name__}")

        for value in color:
            if not isinstance(value, int) or isinstance(value, bool):
                raise TypeError(f"All elements in 'color' must be '<int>', but got {type(value).__name__}")
            if not 0 <= value <= 255:
                raise ValueError(f"'value' should be in [0, 255], but got '{value}'")

        self._color = color

    @property
    def font(self) -> int:
        """
        int: Gets the OpenCV font type identifier.

        Returns:
            int: Font identifier corresponding to `cv2.FONT_*` constants (0-7).
        """
        return self._font

    @font.setter
    def font(self, font: int) -> None:
        """
        Sets the OpenCV font type identifier with validation.

        Args:
            font (int): New font type identifier (0-7).

        Raises:
            TypeError: If font is not an integer.
            ValueError: If font is outside [0, 7].

        Note:
            - 0: SIMPLEX, 1: PLAIN, 2: DUPLEX, 3: COMPLEX, 4: TRIPLEX, etc.
            - Directly maps to OpenCV's `putText` `fontFace` parameter
        """
        if not isinstance(font, int) or isinstance(font, bool):
            raise TypeError(f"'smooth' must be '<int>', but got {type(font).__name__}")
        if not 0 <= font <= 7:
            raise ValueError(f"'font' must be in [0, 7], got {font}")
        self._font = font

    @property
    def font_scale(self) -> int:
        """
        int: Gets the font scale factor.

        Returns:
            int: Scale factor multiplied by font-specific base size (must be > 0).
        """
        return self._font_scale

    @font_scale.setter
    def font_scale(self, font_scale: int) -> None:
        """
        Sets the font scale factor with validation.

        Args:
            font_scale (int): New scale value. Must be positive.

        Raises:
            TypeError: If font_scale is not an integer.
            ValueError: If font_scale is <= 0.

        Note:
            - Higher values produce larger text
            - OpenCV accepts float, but this implementation enforces int for consistency
        """
        if not isinstance(font_scale, int) or isinstance(font_scale, bool):
            raise TypeError(f"'font_scale' must be '<int>', but got {type(font_scale).__name__}")
        if font_scale <= 0:
            raise ValueError(f"'font_scale' must be positive, got {font_scale}")
        self._font_scale = font_scale

    @property
    def thickness(self) -> int:
        """
        int: Gets the text stroke thickness.

        Returns:
            int: Stroke thickness in pixels (>= 1).
        """
        return self._thickness

    @thickness.setter
    def thickness(self, thickness: int) -> None:
        """
        Sets the text stroke thickness with validation.

        Args:
            thickness (int): New thickness value in pixels.

        Raises:
            TypeError: If thickness is not an integer.
            ValueError: If thickness is < 1.

        Note:
            - Thickness <= 0 renders invisible text in OpenCV
            - Recommended range: 1-5 for standard resolutions (640p-1080p)
        """
        if not isinstance(thickness, int) or isinstance(thickness, bool):
            raise TypeError(f"'thickness' must be '<int>', but got {type(thickness).__name__}")
        if thickness < 1:
            raise ValueError(f"'thickness' must be >= 0")
        self._thickness = thickness

