from datetime import date, timedelta
from typing import Optional
from app.models import ChoreFrequencyEnum

def calculate_next_due_date(
    current_due_date: date,
    frequency: ChoreFrequencyEnum,
    custom_interval_days: Optional[int] = None,
    last_completed_date: Optional[date] = None
) -> date:
    """
    Calculates the next due date for a chore.
    Uses current_due_date as a base if last_completed_date is not provided.
    Calculates from last_completed_date if provided.
    """
    if frequency == ChoreFrequencyEnum.one_time:
        if last_completed_date:
            raise ValueError("Cannot calculate next due date for a completed one-time chore.")
        return current_due_date

    base_date = last_completed_date if last_completed_date else current_due_date
    
    if hasattr(base_date, 'date') and callable(getattr(base_date, 'date')):
        base_date = base_date.date() # type: ignore

    next_due: date

    if frequency == ChoreFrequencyEnum.daily:
        next_due = base_date + timedelta(days=1)
    elif frequency == ChoreFrequencyEnum.weekly:
        next_due = base_date + timedelta(weeks=1)
    elif frequency == ChoreFrequencyEnum.monthly:
        month = base_date.month + 1
        year = base_date.year + (month - 1) // 12
        month = (month - 1) % 12 + 1
        
        day_of_target_month_last = (date(year, month % 12 + 1, 1) - timedelta(days=1)).day if month < 12 else 31
        day = min(base_date.day, day_of_target_month_last)
        
        next_due = date(year, month, day)
    elif frequency == ChoreFrequencyEnum.custom:
        if not custom_interval_days or custom_interval_days <= 0:
            raise ValueError("Custom frequency requires a positive custom_interval_days.")
        next_due = base_date + timedelta(days=custom_interval_days)
    else:
        raise ValueError(f"Unknown or unsupported chore frequency: {frequency}")

    today = date.today()
    reference_future_date = max(today, base_date)

    # This loop ensures the next_due date is always in the future relative to the reference_future_date.
    while next_due <= reference_future_date:
        current_base_for_recalc = next_due

        if frequency == ChoreFrequencyEnum.daily:
            next_due = current_base_for_recalc + timedelta(days=1)
        elif frequency == ChoreFrequencyEnum.weekly:
            next_due = current_base_for_recalc + timedelta(weeks=1)
        elif frequency == ChoreFrequencyEnum.monthly:
            month = current_base_for_recalc.month + 1
            year = current_base_for_recalc.year + (month - 1) // 12
            month = (month - 1) % 12 + 1
            day_of_target_month_last = (date(year, month % 12 + 1, 1) - timedelta(days=1)).day if month < 12 else 31
            day = min(current_base_for_recalc.day, day_of_target_month_last)
            next_due = date(year, month, day)
        elif frequency == ChoreFrequencyEnum.custom:
            if not custom_interval_days or custom_interval_days <= 0: # Should have been validated
                 raise ValueError("Custom frequency requires positive interval during recalc.")
            next_due = current_base_for_recalc + timedelta(days=custom_interval_days)
        else: # Should not be reached
            break 
            
        # Safety break: if date hasn't changed, interval is zero or logic error.
        if next_due == current_base_for_recalc:
            # Log error ideally, then advance by one day to prevent infinite loop.
            next_due += timedelta(days=1) 
            break
            
    return next_due
