import logging
from datetime import datetime

from acmenra_cv.instance.point import Point
from typing import Optional


logger = logging.getLogger(__name__)


class TimedPoint(Point):
    """
    Represents a spatial point in 3D normalized coordinates with an associated timestamp.

    Extends the base Point class to support time-series tracking applications, such as
    trajectory analysis, motion prediction, and temporal filtering in computer vision pipelines.
    Inherits all geometric operations from Point while adding precise temporal metadata.

    Key features:
    - Inherits normalized coordinate validation [0.0, 1.0] from Point
    - Optional timestamp tracking for temporal ordering and velocity calculations
    - Seamless conversion back to base Point for geometry-only operations
    - Strict type validation for temporal metadata

    Designed for seamless integration with downstream processing pipelines such as:
    - Trajectory history management (TimedPointQueue)
    - Motion analysis and speed/direction estimation
    - Multi-object tracking (MOT) and ID association
    - Temporal filtering and smoothing algorithms

    Attributes:
        _timestamp (Optional[datetime]): Time when the point was recorded.
            None if timestamp is not available.

    Note:
        - Coordinates follow normalized space convention (0.0-1.0)
        - Timestamp enables frame-accurate trajectory reconstruction
        - Use to_point() when temporal metadata is not required
    """

    def __init__(self, x: float, y: float, z: float = 0.0, timestamp: datetime = datetime.now() ):
        """
        Initializes a TimedPoint with spatial coordinates and a recording timestamp.

        Args:
            x (float): X coordinate (horizontal, 0.0=left, 1.0=right).
            y (float): Y coordinate (vertical, 0.0=top, 1.0=bottom).
            z (float, optional): Z coordinate (depth). Defaults to 0.0.
            timestamp (datetime, optional): Recording time. Defaults to datetime.now().

        Raises:
            TypeError: If coordinates are not floats or timestamp is not a datetime.
            ValueError: If coordinates are outside [0.0, 1.0].

        Note:
            - Coordinate validation is handled by Point.__init__
            - Default timestamp is evaluated at runtime; pass None or explicit value for deterministic pipelines
            - Used as the atomic unit in trajectory queues
        """
        super().__init__(x=x, y=y, z=z)
        self.timestamp = timestamp

    def __str__(self) -> str:
        """
        Returns a human-readable string representation of the timed point.

        Returns:
            str: Formatted string with coordinates and timestamp.

        Note:
            - Optimized for console logging and quick debugging
            - Timestamp format depends on datetime.__str__()
        """
        return f"TimedPoint(X={self.X}, Y={self.Y}, Z={self.Z}, t={self.timestamp})"

    def __repr__(self) -> str:
        """
        Returns a detailed string representation suitable for debugging and reproduction.

        Returns:
            str: Exact representation including class name, coordinates, and timestamp.

        Note:
            - Can be used to recreate the object in interactive sessions
            - Includes repr-safe formatting for all attributes
        """
        return f"TimedPoint(x={self.X!r}, y={self.Y!r}, z={self.Z!r}, t={self.timestamp!r})"

    @property
    def timestamp(self) -> Optional[datetime]:
        """
        Optional[datetime]: Gets the timestamp when the point was recorded.

        Returns:
            Optional[datetime]: Recording time, or None if not set.

        Note:
            - Used for temporal ordering in trajectory queues
            - Enables velocity and acceleration calculations
            - May be None for static or pre-calculated points
        """
        return self._timestamp

    @timestamp.setter
    def timestamp(self, timestamp: Optional[datetime]) -> None:
        """
        Sets the timestamp when the point was recorded.

        Args:
            timestamp (Optional[datetime]): Recording time to set.
                Pass None to clear the timestamp.

        Raises:
            TypeError: If timestamp is not a datetime object or None.

        Note:
            - Strictly validated to ensure temporal consistency
            - Does not auto-update on access
            - Compatible with queue-based trajectory management
        """
        if timestamp is not None and not isinstance(timestamp, datetime):
            raise TypeError(f"'timestamp' must be '<datetime.datetime>', but got {type(timestamp).__name__}")
        self._timestamp = timestamp

    def to_point(self) -> Point:
        """
        Converts the TimedPoint to a base Point instance, discarding temporal metadata.

        Returns:
            Point: New Point instance with identical X, Y, Z coordinates.
                Coordinates are clamped to [0.0, 1.0] via Point.safe().

        Note:
            - Useful for geometry-only operations (distance, scale, translate)
            - Removes timestamp to reduce memory in spatial calculations
            - Original TimedPoint remains unchanged (immutable operation)
        """
        return Point.safe(x=self.X, y=self.Y, z=self.Z)