"""This file is fully generated by ChatGPT"""
from datetime import timedelta, date


class DateRange:
    def __init__(self, start_date: date, end_date: date):
        """
        Initialize a DateRange object.

        Parameters:
        - start_date (date): The start date of the range.
        - end_date (date): The end date of the range.

        Raises:
        - ValueError: If start_date is after end_date.
        """
        self.start_date: date = start_date
        self.end_date: date = end_date

        if start_date > end_date:
            raise ValueError("Start date must be on or before end date")

    def __repr__(self) -> str:
        """
        Return a string representation of the DateRange object.
        """
        return f"DateRange({self.start_date}, {self.end_date})"

    def __str__(self) -> str:
        """
        Return a human-readable string representation of the DateRange object.
        """
        return f"{self.start_date} to {self.end_date}"

    def __contains__(self, other_date: date) -> bool:
        """
        Check if a given date is within the date range.

        Parameters:
        - other_date (date): The date to check.

        Returns:
        - bool: True if other_date is within the date range, False otherwise.
        """
        return self.start_date <= other_date <= self.end_date

    def __iter__(self):
        """
        Iterate over the dates in the date range.
        """
        current_date = self.start_date
        while current_date <= self.end_date:
            yield current_date
            current_date += timedelta(days=1)

    def length(self) -> int:
        """
        Calculate the length of the date range in days.

        Returns:
        - int: The length of the date range.
        """
        return (self.end_date - self.start_date).days + 1

    def overlaps(self, other_range: 'DateRange') -> bool:
        """
        Check if the current date range overlaps with another date range.

        Parameters:
        - other_range (DateRange): The other date range to check.

        Returns:
        - bool: True if the date ranges overlap, False otherwise.
        """
        return (
                self.start_date <= other_range.end_date
                and self.end_date >= other_range.start_date
        )

    def merge(self, other_range: 'DateRange') -> 'DateRange':
        """
        Merge the current date range with another overlapping date range.

        Parameters:
        - other_range (DateRange): The other date range to merge.

        Returns:
        - DateRange: A new DateRange representing the merged date range.

        Raises:
        - ValueError: If the date ranges do not overlap.
        """
        if not self.overlaps(other_range):
            raise ValueError("Ranges do not overlap and cannot be merged")

        new_start_date = min(self.start_date, other_range.start_date)
        new_end_date = max(self.end_date, other_range.end_date)

        return DateRange(new_start_date, new_end_date)

    @classmethod
    def union(cls, range1: 'DateRange', range2: 'DateRange') -> 'DateRange':
        """
        Create a new DateRange from the union of two overlapping date ranges.

        Parameters:
        - range1 (DateRange): The first date range.
        - range2 (DateRange): The second date range.

        Returns:
        - DateRange: A new DateRange representing the union of the two date ranges.

        Raises:
        - ValueError: If the date ranges do not overlap.
        """
        if not range1.overlaps(range2):
            raise ValueError("Ranges do not overlap and cannot be unioned")

        new_start_date = min(range1.start_date, range2.start_date)
        new_end_date = max(range1.end_date, range2.end_date)

        return cls(new_start_date, new_end_date)
