{"files": {"main.py": "from core import run\nfrom policy import POLICY\ndef evaluate(payload):\n    return run(payload, POLICY)\n", "core.py": "from datetime import datetime, timedelta, timezone, date, time\nfrom zoneinfo import ZoneInfo\nfrom helpers import parse_datetime, intervals_overlap, get_duration\n\ndef run(payload, policy):\n    selected = []\n    blocked = []\n    rejected = []\n    if not isinstance(payload, dict):\n        return {'selected': [], 'blocked': [], 'rejected': []}\n    appointments = payload.get('appointments')\n    if not isinstance(appointments, list):\n        appointments = []\n    tz_name = policy.get('timezone', 'UTC')\n    try:\n        tz = ZoneInfo(tz_name) if tz_name else timezone.utc\n    except Exception:\n        tz = timezone.utc\n    duplicate_policy = policy.get('duplicate_policy', 'first')\n    deduped = {}\n    order = []\n    for idx, item in enumerate(appointments):\n        if not isinstance(item, dict):\n            continue\n        appt_id = item.get('id')\n        if appt_id is None:\n            continue\n        entry = (idx, dict(item))\n        if appt_id not in deduped:\n            deduped[appt_id] = entry\n            order.append(appt_id)\n        elif duplicate_policy == 'last':\n            deduped[appt_id] = entry\n    rows = []\n    for appt_id in order:\n        idx, item = deduped[appt_id]\n        rows.append((idx, item))\n    now = parse_datetime(payload.get('now'))\n    lead_minutes = policy.get('lead_minutes', 0)\n    try:\n        lead_minutes_val = float(lead_minutes)\n    except Exception:\n        lead_minutes_val = 0\n    duration_max = policy.get('duration_max')\n    cutoff_hour = policy.get('cutoff_hour', 23)\n    cutoff_inclusive = bool(policy.get('cutoff_inclusive', False))\n    blackout_day = policy.get('blackout_day')\n    if isinstance(blackout_day, date) and not isinstance(blackout_day, datetime):\n        blackout_str = blackout_day.isoformat()\n    elif blackout_day is None:\n        blackout_str = None\n    else:\n        blackout_str = str(blackout_day)\n    cancelled_blocks = bool(policy.get('cancelled_blocks', False))\n    priority_high_first = bool(policy.get('priority_high_first', True))\n    try:\n        capacity = int(policy.get('capacity', 1))\n    except Exception:\n        capacity = 1\n    adjacency_allowed = bool(policy.get('adjacency_allowed', True))\n    report_order = policy.get('report_order', 'input')\n    def priority_of(item):\n        p = item.get('priority', 0)\n        if isinstance(p, bool):\n            return 0\n        if isinstance(p, (int, float)):\n            return p\n        try:\n            return int(p)\n        except Exception:\n            return 0\n    if priority_high_first:\n        rows_sorted = sorted(rows, key=lambda r: (-priority_of(r[1]), r[0]))\n    else:\n        rows_sorted = sorted(rows, key=lambda r: (priority_of(r[1]), r[0]))\n    accepted = []\n    results = []\n    for idx, item in rows_sorted:\n        appt_id = item.get('id')\n        cancelled = item.get('cancelled', False)\n        is_cancelled = bool(cancelled) if isinstance(cancelled, bool) else bool(cancelled)\n        valid = True\n        reason_blocked = False\n        if is_cancelled and not cancelled_blocks:\n            valid = False\n        duration = get_duration(item)\n        start = parse_datetime(item.get('start'))\n        if valid:\n            if duration is None or isinstance(duration, bool) or not isinstance(duration, (int, float)):\n                valid = False\n            elif duration <= 0:\n                valid = False\n            elif duration_max is not None and duration > duration_max:\n                valid = False\n        end_utc = None\n        start_utc = None\n        if valid:\n            if start is None or now is None:\n                valid = False\n        if valid:\n            start_utc = start.astimezone(timezone.utc)\n            end_utc = start_utc + timedelta(minutes=float(duration))\n            earliest = now.astimezone(timezone.utc) + timedelta(minutes=lead_minutes_val)\n            if start_utc < earliest:\n                valid = False\n        if valid:\n            try:\n                start_local = start_utc.astimezone(tz)\n                if blackout_str is not None and start_local.date().isoformat() == blackout_str:\n                    valid = False\n                else:\n                    cutoff_local = datetime.combine(start_local.date(), time(hour=int(cutoff_hour), minute=0), tzinfo=tz)\n                    cutoff_utc = cutoff_local.astimezone(timezone.utc)\n                    if cutoff_inclusive:\n                        if end_utc > cutoff_utc:\n                            valid = False\n                    else:\n                        if end_utc >= cutoff_utc:\n                            valid = False\n            except Exception:\n                valid = False\n        if valid:\n            overlap_count = 0\n            for (a_start, a_end) in accepted:\n                if intervals_overlap(start_utc, end_utc, a_start, a_end, adjacency_allowed):\n                    overlap_count += 1\n            if overlap_count >= capacity:\n                valid = False\n        if valid:\n            accepted.append((start_utc, end_utc))\n            if is_cancelled and cancelled_blocks:\n                results.append((idx, appt_id, 'blocked'))\n            else:\n                results.append((idx, appt_id, 'selected'))\n        else:\n            results.append((idx, appt_id, 'rejected'))\n    selected_entries = [(idx, i) for idx, i, kind in results if kind == 'selected']\n    blocked_entries = [(idx, i) for idx, i, kind in results if kind == 'blocked']\n    rejected_entries = [(idx, i) for idx, i, kind in results if kind == 'rejected']\n    def sort_entries(entries):\n        if report_order == 'id':\n            return [i for _, i in sorted(entries, key=lambda x: str(x[1]))]\n        return [i for _, i in sorted(entries, key=lambda x: x[0])]\n    return {'selected': sort_entries(selected_entries), 'blocked': sort_entries(blocked_entries), 'rejected': sort_entries(rejected_entries)}\n", "policy.py": "POLICY = {\n    'timezone': 'UTC',\n    'cutoff_hour': 18,\n    'cutoff_inclusive': False,\n    'lead_minutes': 60,\n    'adjacency_allowed': False,\n    'cancelled_blocks': False,\n    'duplicate_policy': 'last',\n    'duration_max': 90,\n    'capacity': 2,\n    'report_order': 'input',\n    'blackout_day': '2026-11-02',\n    'priority_high_first': False,\n}\n", "helpers.py": "from datetime import datetime, timezone\n\ndef parse_datetime(value):\n    if isinstance(value, datetime):\n        dt = value\n    elif isinstance(value, str):\n        text = value.strip()\n        if text.endswith('Z') or text.endswith('z'):\n            text = text[:-1] + '+00:00'\n        try:\n            dt = datetime.fromisoformat(text)\n        except Exception:\n            return None\n    else:\n        return None\n    if dt.tzinfo is None or dt.utcoffset() is None:\n        return None\n    return dt\n\ndef get_duration(item):\n    for key in ('duration_minutes', 'duration', 'duration_min', 'minutes', 'length_minutes'):\n        if key in item:\n            return item.get(key)\n    return None\n\ndef intervals_overlap(start, end, other_start, other_end, adjacency_allowed):\n    if adjacency_allowed:\n        return start < other_end and other_start < end\n    return start <= other_end and other_start <= end\n"}}