from datetime import date, datetime, time
from django.utils import timezone
import calendar
from calendar import monthrange
from dateutil.relativedelta import relativedelta

def get_month_days(date):
    return calendar.monthrange(date.year, date.month)[1]

def diff_day(first_date):
    """
    Calculates the difference in days between the current date and the given date.
    Handles both timezone-aware and naive datetime objects, as well as date objects.
    """
    today = date.today()

    if isinstance(first_date, date) and not isinstance(first_date, datetime):
        # If it's already a date (but not a datetime), use it as is
        date_to_compare = first_date
    elif isinstance(first_date, datetime):
        # Convert datetime to date
        if timezone.is_aware(first_date):
            date_to_compare = timezone.localtime(first_date).date()
        else:
            date_to_compare = first_date.date()  # Assume UTC if naive
    else:
        raise ValueError("Invalid date type provided")

    return (today - date_to_compare).days

def current_date(date=None):
    """
    Returns a timezone-aware datetime object with current time.

    Args:
        date: Optional parameter that can be either a date object or a string in 'YYYY-MM-DD' format.
              If not provided, returns current date and time.

    Returns:
        A timezone-aware datetime object
    """
    try:
        current_time = datetime.now().time()  # Get current time

        if date is None:
            # If no date is provided, return current datetime
            return timezone.make_aware(datetime.now())

        elif isinstance(date, str):
            # If date is a string, parse it and combine with current time
            try:
                parsed_date = datetime.strptime(date, '%Y-%m-%d')
                return timezone.make_aware(
                    datetime.combine(parsed_date.date(), current_time)
                )
            except ValueError:
                raise ValueError("Date string must be in 'YYYY-MM-DD' format")

        elif hasattr(date, 'year') and hasattr(date, 'month') and hasattr(date, 'day'):
            # If date is a date object, combine with current time
            return timezone.make_aware(
                datetime.combine(date, current_time)
            )

        else:
            raise ValueError("Invalid date format. Must be either a date object or string in 'YYYY-MM-DD' format")

    except Exception as e:
        raise Exception(f"Error creating timezone-aware datetime: {str(e)}")

def make_timezone_aware(date):
    return timezone.make_aware(
        datetime.combine(date, time.min)
    )

def current_time():
    """
    Returns the current time.
    """
    return datetime.now().time()

def current_datetime():
    """
    Returns the current datetime.
    """
    return datetime.now()

def current_datetime_with_timezone():
    """
    Returns the current datetime with timezone information.
    """
    return timezone.now()

def current_datetime_with_timezone_and_format(format_string):
    """
    Returns the current datetime with timezone information in a specified format.
    """
    return timezone.now().strftime(format_string)

def next_month_due(current_due_date):
    next_month = current_due_date + relativedelta(months=1)

    # Get the last day of next month
    _, last_day = monthrange(next_month.year, next_month.month)

    # If the current day exists in next month, use it
    # Otherwise use the last day of next month
    if current_due_date.day <= last_day:
        next_rent_due = next_month
    else:
        next_rent_due = next_month.replace(day=last_day)

    return next_rent_due
