{"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\nfrom zoneinfo import ZoneInfo\nfrom helpers import is_plain_int, parse_aware_iso\n\ndef run(payload, policy):\n    policy = dict(policy) if isinstance(policy, dict) else {}\n    selected = []\n    blocked = []\n    rejected = []\n    if not isinstance(payload, dict):\n        return {'selected': selected, 'blocked': blocked, 'rejected': rejected}\n    appointments = payload.get('appointments')\n    if not isinstance(appointments, list):\n        appointments = []\n    duplicate_policy = policy.get('duplicate_policy', 'first')\n    retained = {}\n    for idx, row in enumerate(appointments):\n        if not isinstance(row, dict):\n            continue\n        appt_id = row.get('id')\n        if not isinstance(appt_id, str) or appt_id == '':\n            continue\n        if duplicate_policy == 'last':\n            retained[appt_id] = (idx, row)\n        else:\n            if appt_id not in retained:\n                retained[appt_id] = (idx, row)\n    entries = [(idx, row.get('id'), row) for idx, row in retained.values()]\n    index_by_id = {appt_id: idx for idx, appt_id, _ in entries}\n    def finish():\n        order = policy.get('report_order', 'input')\n        if order == 'id':\n            selected_out = sorted(selected)\n            blocked_out = sorted(blocked)\n            rejected_out = sorted(rejected)\n        else:\n            selected_out = sorted(selected, key=lambda x: index_by_id.get(x, 0))\n            blocked_out = sorted(blocked, key=lambda x: index_by_id.get(x, 0))\n            rejected_out = sorted(rejected, key=lambda x: index_by_id.get(x, 0))\n        return {'selected': selected_out, 'blocked': blocked_out, 'rejected': rejected_out}\n    try:\n        tz = ZoneInfo(str(policy.get('timezone', 'UTC')))\n    except Exception:\n        rejected.extend([appt_id for _, appt_id, _ in entries])\n        return finish()\n    now_dt = parse_aware_iso(payload.get('now'))\n    if now_dt is None:\n        rejected.extend([appt_id for _, appt_id, _ in entries])\n        return finish()\n    now_utc = now_dt.astimezone(timezone.utc)\n    try:\n        lead_minutes = policy.get('lead_minutes', 0)\n        duration_max = policy.get('duration_max')\n        cutoff_hour = int(policy.get('cutoff_hour', 18))\n        capacity = int(policy.get('capacity', 1))\n    except Exception:\n        rejected.extend([appt_id for _, appt_id, _ in entries])\n        return finish()\n    cutoff_inclusive = bool(policy.get('cutoff_inclusive', False))\n    cancelled_blocks = bool(policy.get('cancelled_blocks', False))\n    adjacency_allowed = bool(policy.get('adjacency_allowed', True))\n    priority_high_first = bool(policy.get('priority_high_first', True))\n    blackout_day = policy.get('blackout_day')\n    earliest = now_utc + timedelta(minutes=lead_minutes)\n    candidates = []\n    for idx, appt_id, row in entries:\n        start_dt = parse_aware_iso(row.get('start'))\n        duration = row.get('duration')\n        priority = row.get('priority')\n        cancelled = row.get('cancelled')\n        valid = True\n        if start_dt is None:\n            valid = False\n        if not is_plain_int(duration) or duration <= 0 or (duration_max is not None and duration > duration_max):\n            valid = False\n        if not is_plain_int(priority):\n            valid = False\n        if not isinstance(cancelled, bool):\n            valid = False\n        if not valid:\n            rejected.append(appt_id)\n            continue\n        start_utc = start_dt.astimezone(timezone.utc)\n        end_utc = start_utc + timedelta(minutes=duration)\n        if start_utc < earliest:\n            rejected.append(appt_id)\n            continue\n        local_start = start_utc.astimezone(tz)\n        local_day = local_start.date()\n        if blackout_day is not None and local_day.isoformat() == str(blackout_day):\n            rejected.append(appt_id)\n            continue\n        try:\n            cutoff_local = datetime(local_day.year, local_day.month, local_day.day, cutoff_hour, 0, tzinfo=tz)\n        except Exception:\n            rejected.append(appt_id)\n            continue\n        cutoff_utc = cutoff_local.astimezone(timezone.utc)\n        if cutoff_inclusive:\n            if end_utc > cutoff_utc:\n                rejected.append(appt_id)\n                continue\n        else:\n            if end_utc >= cutoff_utc:\n                rejected.append(appt_id)\n                continue\n        if cancelled and not cancelled_blocks:\n            rejected.append(appt_id)\n            continue\n        candidates.append({'id': appt_id, 'index': idx, 'priority': priority, 'cancelled': cancelled, 'start': start_utc, 'end': 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 other in accepted:\n            if adjacency_allowed:\n                overlaps = cand['start'] < other['end'] and other['start'] < cand['end']\n            else:\n                overlaps = cand['start'] <= other['end'] and other['start'] <= cand['end']\n            if overlaps:\n                count += 1\n        if count < capacity:\n            accepted.append(cand)\n            if cand['cancelled'] and cancelled_blocks:\n                blocked.append(cand['id'])\n            else:\n                selected.append(cand['id'])\n        else:\n            rejected.append(cand['id'])\n    return finish()\n", "policy.py": "POLICY = {\n    'timezone': 'UTC',\n    'cutoff_hour': 18,\n    'cutoff_inclusive': False,\n    'lead_minutes': 60,\n    'adjacency_allowed': False,\n    'duration_max': 90,\n    'duplicate_policy': 'last',\n    'cancelled_blocks': False,\n    'blackout_day': '2026-11-02',\n    'priority_high_first': False,\n    'capacity': 2,\n    'report_order': 'input',\n}\n", "helpers.py": "from datetime import datetime\n\ndef is_plain_int(value):\n    return isinstance(value, int) and not isinstance(value, bool)\n\ndef parse_aware_iso(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"}}