from dataclasses import dataclass
from datetime import datetime, time, timedelta
from typing import Optional

from app.utils.format import format_time


class VideoTimeInterval:
    """
    Represents a time interval in a video.
    start_msec: start time of the interval in milliseconds
    start_time: start time of the interval as a time object
    end_msec: end time of the interval in milliseconds
    end_time: end time of the interval as a time object
    """

    def __init__(self) -> None:
        self._start_msec: Optional[float] = None
        self._start_time: Optional[time] = None
        self._end_msec: Optional[float] = None
        self._end_time: Optional[time] = None

    @property
    def start_msec(self) -> Optional[float]:
        return self._start_msec

    @start_msec.setter
    def start_msec(self, start_msec: float) -> None:
        self._start_msec = start_msec
        self._start_time = self._convert_msec_to_time(start_msec)

    @property
    def start_time(self) -> Optional[time]:
        return self._start_time

    @property
    def end_msec(self) -> Optional[float]:
        return self._end_msec

    @end_msec.setter
    def end_msec(self, end_msec: float) -> None:
        self._end_msec = end_msec
        self._end_time = self._convert_msec_to_time(end_msec)

    @property
    def end_time(self) -> Optional[time]:
        return self._end_time

    @property
    def duration(self) -> Optional[timedelta]:
        """
        Returns the duration of the time interval.
        :return: duration of the time interval as a timedelta object or None if the start or end time is unknown
        """
        if self.start_msec is None or self.end_msec is None:
            return None
        return timedelta(milliseconds=self.end_msec - self.start_msec)

    def __str__(self) -> str:
        if self.start_time is None or self.end_time is None:
            return "Unknown"
        return f"{format_time(self.start_time)} — {format_time(self.end_time)}"

    @classmethod
    def from_msec(cls, start_msec: float, end_msec: float) -> "VideoTimeInterval":
        """
        Creates a new time interval from start and end time in milliseconds.
        :param start_msec: start time of the interval in milliseconds
        :param end_msec: end time of the interval in milliseconds
        :return: new time interval object
        """
        time_interval = cls()
        time_interval.start_msec = start_msec
        time_interval.end_msec = end_msec
        return time_interval

    @staticmethod
    def _convert_msec_to_time(msec: float) -> time:
        """
        Converts milliseconds to a time object.
        :param msec: milliseconds value
        :return: time object
        """
        td = timedelta(milliseconds=msec)
        t = datetime.combine(datetime.min, time.min) + td
        return t.time()


@dataclass
class DatetimeInterval:
    """
    Represents a datetime interval
    start: start datetime

    """

    start: Optional[datetime] = None
    end: Optional[datetime] = None

    @property
    def duration(self) -> Optional[timedelta]:
        """
        Returns the duration of the datetime interval
        :return: duration of the time interval as a timedelta object or None if the start or end time is unknown
        """
        if self.start is None or self.end is None:
            return None
        return self.end - self.start
