import logging
from typing import Optional
from acmenra_cv.instance.instance import Instance
from acmenra_cv.tracker.timed_point_queue import TimedPointQueue


logger = logging.getLogger(__name__)


class TrackedObject:
    """
    Represents a single tracked entity with detection metadata and spatial-temporal history.

    Encapsulates all state related to a tracked object, providing a unified interface between
    the tracking infrastructure and downstream business logic (zone analysis, ADAS alerts,
    trajectory validation). Combines a persistent tracking ID, latest YOLO detection instance,
    and a chronological trajectory history.

    Key features:
    - Strongly typed attributes with strict validation on assignment
    - Encapsulation of detection geometry (Box/Polygon/OBB), class, and confidence
    - Time-series trajectory management via TimedPointQueue
    - Seamless integration with multi-object tracking and event detection pipelines

    Designed for seamless integration with downstream processing pipelines such as:
    - ADAS zone crossing and trajectory validation
    - Real-time object behavior analysis (speed, direction, dwell time)
    - Event triggering and safety alert generation
    - Multi-camera object re-identification and tracking

    Attributes:
        _id (Optional[int]): Unique tracking identifier across frames.
        _instance (Instance): Latest detection data (geometry, class, confidence).
        _trajectory (TimedPointQueue): Chronological history of spatial positions.

    Note:
        - ID is assigned and maintained by the Tracker infrastructure
        - Instance is updated every frame with the latest detection results
        - Trajectory history length is controlled by `Tracker.max_length`
        - Business logic should access data via properties, not internal attributes
    """

    def __init__(self, id: Optional[int], instance: Instance, trajectory: TimedPointQueue) -> None:
        """
        Initializes a TrackedObject with detection data and trajectory history.

        Args:
            id (Optional[int]): Unique tracking identifier. May be None in detection-only mode.
            instance (Instance): Detection data containing geometry, class, and confidence.
            trajectory (TimedPointQueue): Queue for storing historical spatial positions.

        Raises:
            TypeError: If instance is not YOLOInstance or trajectory is not TimedPointQueue.

        Note:
            - All validation is delegated to property setters
            - Typically instantiated internally by the Tracker class
            - Maintains 1:1 mapping between a physical object and its track state
        """
        self.id = id
        self.instance  = instance
        self.trajectory = trajectory

    @property
    def id(self) -> int:
        """
        Optional[int]: Gets the unique tracking identifier.

        Returns:
            Optional[int]: Track ID assigned by the tracking system.
                May be None if tracking is inactive or ID is unassigned.

        Note:
            - Used for cross-frame object association and logging
            - Consistent with YOLO tracker ID assignments
            - Business logic should handle None gracefully
        """
        return self._id

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

        Args:
            id (int): New tracking ID to assign.

        Raises:
            TypeError: If id is not an integer or is a boolean.

        Note:
            - Setter enforces `int` type; `None` must be handled before assignment
            - Negative IDs are currently allowed (commented validation) for flexibility
            - Typically managed internally by the Tracker
        """
        if not isinstance(id, int) or isinstance(id, bool):
            raise TypeError(f"'id' must be 'int', but got {type(id).__name__}")
        # if id < 0:
        #     raise ValueError(f"'id' must be >= 0")
        self._id = id

    @property
    def instance(self) -> Instance:
        """
        YOLOInstance: Gets the latest detection data for the tracked object.

        Returns:
            Instance: Complete detection instance including geometry, class, and confidence.

        Note:
            - Updated every frame by the Tracker with fresh detection results
            - Contains normalized coordinates (0.0-1.0) for all spatial data
            - Never None; always holds valid detection state
        """
        return self._instance

    @instance.setter
    def instance(self, instance: Instance) -> None:
        """
        Sets the detection instance with strict type validation.

        Args:
            instance (Instance): New detection data to assign.

        Raises:
            TypeError: If instance is not a YOLOInstance.

        Note:
            - Typically updated internally by Tracker during frame processing
            - Changing mid-tracking may cause state inconsistency in downstream logic
            - Business logic should use this for class/confidence access
        """
        if not isinstance(instance, Instance):
            raise TypeError(f"'instance' must be '<YOLOInstance>', but got {type(instance).__name__}")
        self._instance = instance

    @property
    def trajectory(self) -> TimedPointQueue:
        """
        TimedPointQueue: Gets the chronological position history of the object.

        Returns:
            TimedPointQueue: Queue containing TimedPoint objects representing past positions.

        Note:
            - Length controlled by `Tracker.max_length` configuration
            - Used for trajectory smoothing, zone crossing logic, and velocity estimation
            - Automatically managed by Tracker; direct reassignment is discouraged
        """
        return self._trajectory

    @trajectory.setter
    def trajectory(self, trajectory: TimedPointQueue) -> None:
        """
        Sets the trajectory history with strict type validation.

        Args:
            trajectory (TimedPointQueue): New position history queue.

        Raises:
            TypeError: If trajectory is not a TimedPointQueue instance.

        Note:
            - Typically set only during initialization or track reset
            - Trajectory is usually updated via `enqueue()` through the Tracker
            - Direct assignment bypasses incremental history management
        """
        if not isinstance(trajectory, TimedPointQueue):
            raise TypeError(f"'trajectory' must be '<TimedPointQueue>', but got {type(trajectory).__name__}")
        self._trajectory = trajectory