{"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_aware_iso, is_integer_value, overlaps\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    try:\n        tz = ZoneInfo(policy.get('timezone', 'UTC'))\n    except Exception:\n        tz = timezone.utc\n    duplicate_policy = policy.get('duplicate_policy', 'first')\n    retained = {}\n    for idx, item in enumerate(appointments):\n        if not isinstance(item, dict):\n            continue\n        appt_id = item.get('id')\n        if isinstance(appt_id, bool) or not isinstance(appt_id, (str, int)):\n            continue\n        if isinstance(appt_id, str) and appt_id == '':\n            continue\n        if duplicate_policy == 'last':\n            retained[appt_id] = (idx, item)\n        else:\n            if appt_id not in retained:\n                retained[appt_id] = (idx, item)\n    retained_rows = list(retained.values())\n    now_raw = payload.get('now')\n    now_dt = parse_aware_iso(now_raw)\n    if now_dt is None:\n        for idx, item in retained_rows:\n            rejected.append((item.get('id'), idx))\n        return _format(selected, blocked, rejected, policy)\n    now_utc = now_dt.astimezone(timezone.utc)\n    lead = policy.get('lead_minutes', 0)\n    if isinstance(lead, bool) or not isinstance(lead, (int, float)):\n        lead = 0\n    earliest_utc = now_utc + timedelta(minutes=lead)\n    duration_max = policy.get('duration_max')\n    capacity = policy.get('capacity', 1)\n    if isinstance(capacity, bool) or not isinstance(capacity, int):\n        try:\n            capacity = int(capacity)\n        except Exception:\n            capacity = 1\n    cutoff_hour = policy.get('cutoff_hour', 0)\n    if isinstance(cutoff_hour, bool):\n        cutoff_hour = 0\n    try:\n        cutoff_hour = int(cutoff_hour)\n    except Exception:\n        cutoff_hour = 0\n    cutoff_inclusive = bool(policy.get('cutoff_inclusive', False))\n    cancelled_blocks = bool(policy.get('cancelled_blocks', False))\n    blackout_day = policy.get('blackout_day')\n    adjacency_allowed = bool(policy.get('adjacency_allowed', True))\n    priority_high_first = bool(policy.get('priority_high_first', True))\n    candidates = []\n    for idx, item in retained_rows:\n        appt_id = item.get('id')\n        if 'start' not in item or 'duration' not in item or 'priority' not in item or 'cancelled' not in item:\n            rejected.append((appt_id, idx))\n            continue\n        start_dt = parse_aware_iso(item.get('start'))\n        if start_dt is None:\n            rejected.append((appt_id, idx))\n            continue\n        duration_raw = item.get('duration')\n        if isinstance(duration_raw, bool):\n            rejected.append((appt_id, idx))\n            continue\n        if isinstance(duration_raw, int):\n            duration = duration_raw\n        elif isinstance(duration_raw, float) and duration_raw.is_integer():\n            duration = int(duration_raw)\n        else:\n            rejected.append((appt_id, idx))\n            continue\n        if duration <= 0 or (duration_max is not None and duration > duration_max):\n            rejected.append((appt_id, idx))\n            continue\n        priority_raw = item.get('priority')\n        if not is_integer_value(priority_raw):\n            rejected.append((appt_id, idx))\n            continue\n        priority = int(priority_raw)\n        cancelled_raw = item.get('cancelled')\n        if not isinstance(cancelled_raw, bool):\n            rejected.append((appt_id, idx))\n            continue\n        cancelled = cancelled_raw\n        if cancelled and not cancelled_blocks:\n            rejected.append((appt_id, idx))\n            continue\n        start_utc = start_dt.astimezone(timezone.utc)\n        if start_utc < earliest_utc:\n            rejected.append((appt_id, idx))\n            continue\n        local_start = start_utc.astimezone(tz)\n        if blackout_day is not None and local_start.date().isoformat() == str(blackout_day):\n            rejected.append((appt_id, idx))\n            continue\n        end_utc = start_utc + timedelta(minutes=duration)\n        try:\n            cutoff_local = datetime(local_start.year, local_start.month, local_start.day, cutoff_hour, 0, tzinfo=tz)\n        except Exception:\n            rejected.append((appt_id, idx))\n            continue\n        cutoff_utc = cutoff_local.astimezone(timezone.utc)\n        if cutoff_inclusive:\n            if not (end_utc <= cutoff_utc):\n                rejected.append((appt_id, idx))\n                continue\n        else:\n            if not (end_utc < cutoff_utc):\n                rejected.append((appt_id, idx))\n                continue\n        candidates.append({'id': appt_id, 'index': idx, 'priority': priority, 'start': start_utc, 'end': end_utc, 'cancelled': cancelled})\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 overlaps(cand['start'], cand['end'], other['start'], other['end'], adjacency_allowed):\n                count += 1\n        if count < capacity:\n            accepted.append(cand)\n            if cand['cancelled']:\n                blocked.append((cand['id'], cand['index']))\n            else:\n                selected.append((cand['id'], cand['index']))\n        else:\n            rejected.append((cand['id'], cand['index']))\n    return _format(selected, blocked, rejected, policy)\n\ndef _format(selected, blocked, rejected, policy):\n    report_order = policy.get('report_order', 'input')\n    def sort_entries(entries):\n        if report_order == 'id':\n            return [appt_id for appt_id, idx in sorted(entries, key=lambda e: e[0])]\n        return [appt_id for appt_id, idx in sorted(entries, key=lambda e: e[1])]\n    return {'selected': sort_entries(selected), 'blocked': sort_entries(blocked), 'rejected': sort_entries(rejected)}\n", "policy.py": "POLICY = {\n    'cutoff_hour': 18,\n    'cutoff_inclusive': False,\n    'lead_minutes': 60,\n    'timezone': 'UTC',\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\n\ndef parse_aware_iso(value):\n    if not isinstance(value, str):\n        return None\n    text = value.strip()\n    if not text:\n        return None\n    if text.endswith('Z') or text.endswith('z'):\n        text = text[:-1] + '+00:00'\n    try:\n        parsed = datetime.fromisoformat(text)\n    except Exception:\n        return None\n    if parsed.tzinfo is None:\n        return None\n    try:\n        if parsed.utcoffset() is None:\n            return None\n    except Exception:\n        return None\n    return parsed\n\ndef is_integer_value(value):\n    if isinstance(value, bool):\n        return False\n    if isinstance(value, int):\n        return True\n    if isinstance(value, float) and value.is_integer():\n        return True\n    return False\n\ndef overlaps(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"}}