{"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 parse_datetime, overlaps\n\ndef _is_int(value):\n    return isinstance(value, int) and not isinstance(value, bool)\n\ndef _duration_value(appointment):\n    if 'duration' in appointment:\n        value = appointment.get('duration')\n    elif 'duration_minutes' in appointment:\n        value = appointment.get('duration_minutes')\n    elif 'duration_mins' in appointment:\n        value = appointment.get('duration_mins')\n    else:\n        return None\n    if _is_int(value):\n        return value\n    if isinstance(value, float) and value.is_integer():\n        return int(value)\n    return None\n\ndef _sorted_ids(items, report_order):\n    if report_order == 'id':\n        if all(_is_int(item[0]) for item in items):\n            ordered = sorted(items, key=lambda item: item[0])\n        else:\n            ordered = sorted(items, key=lambda item: str(item[0]))\n    else:\n        ordered = sorted(items, key=lambda item: item[1])\n    return [item[0] for item in ordered]\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    policy = dict(policy) if isinstance(policy, dict) else {}\n    adjacency_allowed = policy.get('adjacency_allowed', True)\n    blackout_day = policy.get('blackout_day')\n    cancelled_blocks = policy.get('cancelled_blocks', False)\n    capacity = policy.get('capacity', 1)\n    cutoff_hour = policy.get('cutoff_hour', 18)\n    cutoff_inclusive = policy.get('cutoff_inclusive', True)\n    duplicate_policy = policy.get('duplicate_policy', 'first')\n    duration_max = policy.get('duration_max', 90)\n    lead_minutes = policy.get('lead_minutes', 0)\n    priority_high_first = policy.get('priority_high_first', True)\n    report_order = policy.get('report_order', 'input')\n    timezone_name = policy.get('timezone', 'UTC')\n    try:\n        local_zone = ZoneInfo(timezone_name)\n    except Exception:\n        local_zone = timezone.utc\n    if not _is_int(capacity) or capacity < 0:\n        capacity = 0\n    now = parse_datetime(payload.get('now')) if isinstance(payload, dict) else None\n    now_utc = now.astimezone(timezone.utc) if now is not None else None\n    deduped = {}\n    for index, appointment in enumerate(appointments):\n        if not isinstance(appointment, dict):\n            continue\n        entry_id = appointment.get('id')\n        if isinstance(entry_id, bool) or not isinstance(entry_id, (str, int)):\n            continue\n        if isinstance(entry_id, str) and entry_id == '':\n            continue\n        if entry_id not in deduped or duplicate_policy == 'last':\n            deduped[entry_id] = (appointment, index)\n    candidates = []\n    for entry_id, retained in deduped.items():\n        appointment, index = retained\n        valid = True\n        start = parse_datetime(appointment.get('start'))\n        duration = _duration_value(appointment)\n        priority = appointment.get('priority')\n        cancelled = appointment.get('cancelled', False)\n        if start is None:\n            valid = False\n        if duration is None:\n            valid = False\n        if not _is_int(priority):\n            valid = False\n        if not isinstance(cancelled, bool):\n            valid = False\n            cancelled = False\n        start_utc = None\n        end_utc = None\n        if valid:\n            if duration <= 0 or duration > duration_max:\n                valid = False\n        if valid and now_utc is None:\n            valid = False\n        if valid:\n            start_utc = start.astimezone(timezone.utc)\n            end_utc = start_utc + timedelta(minutes=duration)\n            if start_utc < now_utc + timedelta(minutes=lead_minutes):\n                valid = False\n        if valid:\n            local_start = start.astimezone(local_zone)\n            if blackout_day is not None and local_start.date().isoformat() == str(blackout_day):\n                valid = False\n        if valid:\n            local_start = start.astimezone(local_zone)\n            try:\n                cutoff = datetime(local_start.year, local_start.month, local_start.day, int(cutoff_hour), 0, tzinfo=local_zone)\n            except Exception:\n                valid = False\n            else:\n                if cutoff_inclusive:\n                    if end_utc > cutoff:\n                        valid = False\n                else:\n                    if end_utc >= cutoff:\n                        valid = False\n        if valid and cancelled and not cancelled_blocks:\n            valid = False\n        if not valid:\n            rejected.append((entry_id, index))\n        else:\n            candidates.append({'id': entry_id, 'index': index, 'priority': priority, 'cancelled': cancelled, 'start': start_utc, 'end': end_utc})\n    if priority_high_first:\n        candidates.sort(key=lambda item: (-item['priority'], item['index']))\n    else:\n        candidates.sort(key=lambda item: (item['priority'], item['index']))\n    accepted = []\n    for candidate in candidates:\n        count = 0\n        for other in accepted:\n            if overlaps(candidate['start'], candidate['end'], other['start'], other['end'], adjacency_allowed):\n                count += 1\n        if count < capacity:\n            accepted.append(candidate)\n            if candidate['cancelled'] and cancelled_blocks:\n                blocked.append((candidate['id'], candidate['index']))\n            else:\n                selected.append((candidate['id'], candidate['index']))\n        else:\n            rejected.append((candidate['id'], candidate['index']))\n    return {'selected': _sorted_ids(selected, report_order), 'blocked': _sorted_ids(blocked, report_order), 'rejected': _sorted_ids(rejected, report_order)}\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_datetime(value):\n    if not isinstance(value, str):\n        return None\n    text = value.strip()\n    if text.endswith('Z') or text.endswith('z'):\n        text = text[:-1] + '+00:00'\n    try:\n        parsed = datetime.fromisoformat(text)\n    except ValueError:\n        return None\n    if parsed.tzinfo is None or parsed.utcoffset() is None:\n        return None\n    return parsed\n\ndef overlaps(start_a, end_a, start_b, end_b, adjacency_allowed):\n    if adjacency_allowed:\n        return start_a < end_b and start_b < end_a\n    return start_a <= end_b and start_b <= end_a\n"}}