"""
Python port of the frontend schedule logic in scheduleUtils.ts.

These utilities determine whether a chore is scheduled on a given day and what
its deadline time is.  The frontend is the source of truth for schedule math;
keep this file in sync with any changes to scheduleUtils.ts.
"""

from datetime import date


def interval_hits_today(anchor_date: str, interval_days: int, local_date: date) -> bool:
    """Return True if an interval-mode schedule fires on local_date.

    anchor_date: ISO date string e.g. "2026-02-25", or "" (backward compat →
                 treats local_date itself as anchor, so it always hits today and
                 every interval_days days after).
    Dates before the anchor always return False (scheduling hasn't started yet).
    """
    if anchor_date:
        parts = anchor_date.split('-')
        anchor = date(int(parts[0]), int(parts[1]), int(parts[2]))
    else:
        # Empty anchor = start from today (backward compat)
        anchor = local_date

    diff_days = (local_date - anchor).days
    if diff_days < 0:
        return False
    return diff_days % interval_days == 0


def is_scheduled_today(schedule: dict, local_date: date) -> bool:
    """Return True if the schedule applies on local_date.

    - days mode: today's weekday (0=Sun … 6=Sat) is in day_configs
    - interval mode: interval_hits_today
    - paused (enabled=False): always True (chore shows every day, like no schedule)
    """
    if not schedule.get('enabled', True):
        return True  # paused = show always, like an unscheduled chore

    mode = schedule.get('mode', 'days')
    if mode == 'days':
        # Python's weekday(): Mon=0..Sun=6. Our convention: Sun=0..Sat=6 (JS/TS).
        # Convert: js_day = (python_weekday + 1) % 7
        js_weekday = (local_date.weekday() + 1) % 7
        day_configs = schedule.get('day_configs', [])
        return any(dc.get('day') == js_weekday for dc in day_configs)
    else:
        return interval_hits_today(
            schedule.get('anchor_date', ''),
            schedule.get('interval_days', 2),
            local_date,
        )


def get_due_time_today(schedule: dict, local_date: date) -> tuple[int, int] | None:
    """Return (hour, minute) for the deadline today, or None if no deadline applies.

    Returns None when:
    - The schedule is paused (enabled=False) — no deadline, acts like Anytime
    - The day is not scheduled
    - The schedule has no deadline (default_has_deadline=False or
      interval_has_deadline=False → 'Anytime')

    Callers treat None as 'active all day with no expiry'.
    """
    if not schedule.get('enabled', True):
        return None  # paused = no deadline, no expiry

    mode = schedule.get('mode', 'days')
    if mode == 'days':
        if not schedule.get('default_has_deadline', True):
            return None  # Anytime
        js_weekday = (local_date.weekday() + 1) % 7
        day_configs = schedule.get('day_configs', [])
        dc = next((d for d in day_configs if d.get('day') == js_weekday), None)
        if dc is None:
            return None
        return (dc.get('hour', 0), dc.get('minute', 0))
    else:
        if not interval_hits_today(
            schedule.get('anchor_date', ''),
            schedule.get('interval_days', 2),
            local_date,
        ):
            return None
        if not schedule.get('interval_has_deadline', True):
            return None  # Anytime
        return (schedule.get('interval_hour', 0), schedule.get('interval_minute', 0))
