{"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, time, timedelta, timezone\nfrom zoneinfo import ZoneInfo\nfrom helpers import is_strict_int, overlaps, parse_aware\n\ndef run(payload, policy):\n    result = {\"selected\": [], \"blocked\": [], \"rejected\": []}\n    if not isinstance(payload, dict):\n        return result\n    appointments = payload.get(\"appointments\")\n    if not isinstance(appointments, list):\n        return result\n    duplicate_policy = policy.get(\"duplicate_policy\", \"last\")\n    deduped = {}\n    for idx, item in enumerate(appointments):\n        if not isinstance(item, dict):\n            continue\n        appt_id = item.get(\"id\")\n        if not isinstance(appt_id, str):\n            continue\n        if appt_id not in deduped:\n            deduped[appt_id] = (idx, item)\n        elif duplicate_policy == \"last\":\n            deduped[appt_id] = (idx, item)\n    entries = [(appt_id, idx, item) for appt_id, (idx, item) in deduped.items()]\n    report_order = policy.get(\"report_order\", \"input\")\n    def sort_key(entry):\n        if report_order == \"id\":\n            return entry[0]\n        return entry[1]\n    selected = []\n    blocked = []\n    rejected = []\n    now_dt = parse_aware(payload.get(\"now\"))\n    try:\n        tz = ZoneInfo(policy.get(\"timezone\", \"UTC\"))\n    except Exception:\n        tz = None\n    if now_dt is None or tz is None:\n        rejected.extend(entries)\n        rejected.sort(key=sort_key)\n        return {\"selected\": [], \"blocked\": [], \"rejected\": [e[0] for e in rejected]}\n    duration_max = policy.get(\"duration_max\")\n    lead_minutes = policy.get(\"lead_minutes\", 0)\n    blackout_day = policy.get(\"blackout_day\")\n    cutoff_hour = policy.get(\"cutoff_hour\")\n    cutoff_inclusive = policy.get(\"cutoff_inclusive\", False)\n    cancelled_blocks = policy.get(\"cancelled_blocks\", False)\n    capacity = policy.get(\"capacity\", 1)\n    adjacency_allowed = policy.get(\"adjacency_allowed\", True)\n    priority_high_first = policy.get(\"priority_high_first\", True)\n    now_utc = now_dt.astimezone(timezone.utc)\n    earliest_utc = now_utc + timedelta(minutes=lead_minutes)\n    candidates = []\n    for appt_id, idx, item in entries:\n        start_raw = item.get(\"start\")\n        duration = item.get(\"duration\")\n        priority = item.get(\"priority\")\n        cancelled = item.get(\"cancelled\")\n        start_dt = parse_aware(start_raw)\n        valid = True\n        if start_dt is None:\n            valid = False\n        if not is_strict_int(duration) or duration <= 0 or (duration_max is not None and duration > duration_max):\n            valid = False\n        if not is_strict_int(priority):\n            valid = False\n        if not isinstance(cancelled, bool):\n            valid = False\n        if not valid:\n            rejected.append((appt_id, idx, item))\n            continue\n        start_utc = start_dt.astimezone(timezone.utc)\n        end_utc = start_utc + timedelta(minutes=duration)\n        if start_utc < earliest_utc:\n            rejected.append((appt_id, idx, item))\n            continue\n        start_local = start_utc.astimezone(tz)\n        if blackout_day is not None and start_local.date().isoformat() == str(blackout_day):\n            rejected.append((appt_id, idx, item))\n            continue\n        if cutoff_hour is not None:\n            cutoff_local = datetime.combine(start_local.date(), time(hour=int(cutoff_hour), minute=0), tzinfo=tz)\n            if cutoff_inclusive:\n                if end_utc > cutoff_local.astimezone(timezone.utc):\n                    rejected.append((appt_id, idx, item))\n                    continue\n            else:\n                if end_utc >= cutoff_local.astimezone(timezone.utc):\n                    rejected.append((appt_id, idx, item))\n                    continue\n        if cancelled and not cancelled_blocks:\n            rejected.append((appt_id, idx, item))\n            continue\n        candidates.append({\"id\": appt_id, \"index\": idx, \"priority\": priority, \"cancelled\": cancelled, \"start_utc\": start_utc, \"end_utc\": end_utc})\n    if priority_high_first:\n        candidates.sort(key=lambda c: (-c[\"priority\"], c[\"index\"]))\n    else:\n        candidates.sort(key=lambda c: (c[\"priority\"], c[\"index\"]))\n    accepted = []\n    for cand in candidates:\n        count = 0\n        for acc in accepted:\n            if overlaps(cand[\"start_utc\"], cand[\"end_utc\"], acc[\"start_utc\"], acc[\"end_utc\"], adjacency_allowed):\n                count += 1\n        if count < capacity:\n            accepted.append(cand)\n            entry = (cand[\"id\"], cand[\"index\"], None)\n            if cand[\"cancelled\"] and cancelled_blocks:\n                blocked.append(entry)\n            else:\n                selected.append(entry)\n        else:\n            rejected.append((cand[\"id\"], cand[\"index\"], None))\n    selected.sort(key=sort_key)\n    blocked.sort(key=sort_key)\n    rejected.sort(key=sort_key)\n    return {\"selected\": [e[0] for e in selected], \"blocked\": [e[0] for e in blocked], \"rejected\": [e[0] for e in rejected]}\n", "policy.py": "POLICY = {\"adjacency_allowed\": false, \"blackout_day\": \"2026-11-02\", \"cancelled_blocks\": false, \"capacity\": 2, \"cutoff_hour\": 18, \"cutoff_inclusive\": false, \"duplicate_policy\": \"last\", \"duration_max\": 90, \"lead_minutes\": 60, \"priority_high_first\": false, \"report_order\": \"input\", \"timezone\": \"UTC\"}\n", "helpers.py": "from datetime import datetime\n\ndef parse_aware(value):\n    if not isinstance(value, str):\n        return None\n    text = value.strip()\n    if text.endswith((\"Z\", \"z\")):\n        text = text[:-1] + \"+00:00\"\n    try:\n        dt = datetime.fromisoformat(text)\n    except ValueError:\n        return None\n    if dt.tzinfo is None or dt.tzinfo.utcoffset(dt) is None:\n        return None\n    return dt\n\ndef is_strict_int(value):\n    return isinstance(value, int) and not isinstance(value, bool)\n\ndef overlaps(start1, end1, start2, end2, adjacency_allowed):\n    if adjacency_allowed:\n        return start1 < end2 and start2 < end1\n    return start1 <= end2 and start2 <= end1\n"}}