from __future__ import annotations

import json
from pathlib import Path


def markdown(source: str) -> dict:
    return {
        "cell_type": "markdown",
        "metadata": {},
        "source": [line + "\n" for line in source.strip().splitlines()],
    }


def code(source: str) -> dict:
    return {
        "cell_type": "code",
        "execution_count": None,
        "metadata": {},
        "outputs": [],
        "source": [line + "\n" for line in source.strip().splitlines()],
    }


cells = [
    markdown(
        """
# PolicyFaultBench — الاستئناف اليومي الآمن V1P2

هذا الدفتر لا يعيد ما نجح:

- يحتفظ ببوابة Claude الناجحة 12/12.
- يستأنف حالتي Gemini المتبقيتين فقط.
- يتعرف على حد Gemini المجاني اليومي ويتوقف فورًا من دون محاولات مهدرة.
- يبدأ التجربتين التأكيديتين فقط بعد نجاح بوابة Gemini.
- يشغّل بحد أقصى 20 طلب Gemini يوميًا ويحفظ كل تقدم في My Drive.
- يمكن تشغيله يوميًا؛ ولا يكرر أي سجل ناجح.
- يحفظ ZIP النهائي في My Drive ويبدأ تنزيله تلقائيًا.

فعّل `ANTHROPIC_API_KEY` و`GEMINI_API_KEY`، ثم اضغط **Run all** وارفع
`PolicyFaultBench_v0.4.2_V1P2_DAILY_RESUME.zip`.
"""
    ),
    code(
        """
from google.colab import drive, files, userdata
from pathlib import Path
import hashlib, json, os, shutil, subprocess, sys, time, zipfile

drive.mount('/content/drive')
drive_root = Path('/content/drive/MyDrive/PolicyFaultBench_Definitive_v1_2')
assert drive_root.is_dir(), (
    'مجلد V1P2 غير موجود؛ استخدم حساب Google نفسه الذي شُغلت منه البوابات'
)

uploaded = files.upload()
archives = [
    name for name in uploaded
    if name.endswith('.zip') and 'v0.4.2_V1P2_DAILY_RESUME' in name
]
assert len(archives) == 1, (
    'ارفع PolicyFaultBench_v0.4.2_V1P2_DAILY_RESUME.zip فقط'
)

work = Path('/content/pfb_v1p2_daily_resume_work')
if work.exists():
    shutil.rmtree(work)
work.mkdir(parents=True)
with zipfile.ZipFile(archives[0]) as archive:
    for item in archive.infolist():
        target = (work / item.filename).resolve()
        assert work.resolve() in target.parents or target == work.resolve(), (
            'مسار ZIP غير آمن'
        )
    archive.extractall(work)

root = work / 'PolicyFaultBench'
assert (root / 'pyproject.toml').is_file(), 'بنية الحزمة غير صحيحة'
print({'project_loaded': True, 'version': '0.4.2'})
"""
    ),
    code(
        """
required = ('ANTHROPIC_API_KEY', 'GEMINI_API_KEY')
env = os.environ.copy()
for name in required:
    value = userdata.get(name)
    assert value and len(value) >= 20, (
        f'السر {name} غير متاح لهذا الدفتر'
    )
    env[name] = value
env['PFB_LIVE_ENABLED'] = 'true'
env['PFB_STUDY_ENABLED'] = 'true'
env['PYTHONPATH'] = str(root / 'src')

config_path = root / 'config' / 'experiment.json'
config = json.loads(config_path.read_text())
config['live_enabled'] = True
config['study_enabled'] = True
config['debug_only'] = True
config['models']['anthropic'] = 'claude-sonnet-5'
config['models']['gemini'] = 'gemini-3.6-flash'
study = config['definitive_study']
study['experiment_id'] = 'pfb-definitive-v1p2'
study['protocol_version'] = '1.2'
study['seed'] = 20260724
study['repetitions'] = 10
study['planned_trials_per_provider'] = 400
study['provider_delay_seconds']['anthropic'] = 0.0
study['provider_delay_seconds']['gemini'] = 15.5
config_path.write_text(json.dumps(config, indent=2) + '\\n')
"""
    ),
    code(
        """
checks = [
    [sys.executable, '-m', 'unittest', 'discover',
     '-s', str(root / 'tests'), '-q'],
    [sys.executable, '-m', 'policyfaultbench.cli',
     '--root', str(root), 'validate'],
    [sys.executable, '-m', 'policyfaultbench.cli',
     '--root', str(root), 'study-plan', '--output-dir', str(drive_root)],
]
for command in checks:
    result = subprocess.run(command, env=env, text=True, capture_output=True)
    print(result.stdout)
    if result.returncode:
        print(result.stderr)
        raise RuntimeError('فشل التحقق المحلي')

anthropic_gate_path = (
    drive_root / 'anthropic_conformance_gate_v1p2.json'
)
assert anthropic_gate_path.is_file(), 'بوابة Claude السابقة غير موجودة'
anthropic_gate = json.loads(anthropic_gate_path.read_text())
assert (
    anthropic_gate['gate_passed'] is True
    and anthropic_gate['passed'] == 12
), 'بوابة Claude ليست 12/12'
print('بوابة Claude محفوظة وناجحة 12/12؛ لن تُعاد.')
"""
    ),
    markdown(
        """
## استئناف بوابة Gemini

تعاد الحالات غير الناجحة فقط. إذا ظهر حد الطلبات اليومية المجانية، يتوقف
الدفتر مباشرة ويحفظ نقطة متابعة سليمة بدل تكرار الطلب نفسه.
"""
    ),
    code(
        """
gemini_gate_path = drive_root / 'gemini_conformance_gate_v1p2.json'
gate_command = [
    sys.executable,
    str(root / 'scripts' / 'provider_conformance_gate.py'),
    'gemini',
    '--root', str(root),
    '--output', str(gemini_gate_path),
    '--delay-seconds', '15.5',
    '--max-attempts', '4',
    '--retry-wait-seconds', '90',
]
gate_result = subprocess.run(
    gate_command, env=env, text=True, capture_output=True
)
print(gate_result.stdout)
if gate_result.stderr:
    print(gate_result.stderr)

gemini_gate = json.loads(gemini_gate_path.read_text())
gate_ready = gemini_gate['gate_passed'] is True
print({
    'gemini_gate_passed': gemini_gate['passed'],
    'gemini_gate_failed': gemini_gate['failed'],
    'api_calls_this_run':
        gemini_gate.get('invocation_attempts_this_run', 0),
    'daily_quota_exhausted':
        gemini_gate.get('daily_quota_exhausted', False),
})
if gate_result.returncode:
    if gemini_gate.get('daily_quota_exhausted', False):
        from datetime import datetime, timedelta
        from zoneinfo import ZoneInfo

        now_pt = datetime.now(ZoneInfo('America/Los_Angeles'))
        reset_pt = (now_pt + timedelta(days=1)).replace(
            hour=0, minute=0, second=0, microsecond=0
        )
        reset_iq = reset_pt.astimezone(ZoneInfo('Asia/Baghdad'))
        print({
            'status': 'نقطة التقدم سليمة؛ لا يوجد خطأ برمجي',
            'next_quota_reset_iraq':
                reset_iq.strftime('%Y-%m-%d %H:%M %Z'),
            'next_action': 'شغّل الدفتر نفسه بعد هذا الموعد',
        })
    else:
        raise RuntimeError(
            'فشلت بوابة Gemini لسبب غير متعلق بالحصة اليومية'
        )
else:
    assert gemini_gate['passed'] == 12
    print('نجحت بوابة Gemini 12/12.')
"""
    ),
    code(
        """
def run_provider(provider, delay, max_trials=None, max_cycles=20):
    status_path = drive_root / f'study_status_{provider}.json'
    for cycle in range(1, max_cycles + 1):
        command = [
            sys.executable, '-m', 'policyfaultbench.cli',
            '--root', str(root), 'study-run', provider,
            '--output-dir', str(drive_root),
            '--delay-seconds', str(delay),
        ]
        if max_trials is not None:
            command.extend(['--max-trials', str(max_trials)])
        result = subprocess.run(
            command, env=env, text=True, capture_output=True
        )
        print(result.stdout)
        if result.stderr:
            print(result.stderr[-1500:])
        assert status_path.is_file(), f'لم يُنشأ ملف حالة {provider}'
        status = json.loads(status_path.read_text())
        print({
            'provider': provider,
            'cycle': cycle,
            'complete': status['primary_complete'],
            'remaining': status['remaining'],
            'conformance_failures':
                status['model_conformance_failure_count'],
            'containment_failures':
                status['invocation_containment_failures'],
            'cost_usd': status['completed_cost_usd'],
            'stop_reason': status['stop_reason'],
        })
        if status['invocation_containment_failures']:
            raise RuntimeError(f'توقف آمن بسبب احتواء في {provider}')
        if status['remaining'] == 0:
            return status
        reason = status.get('stop_reason', '')
        if reason == 'invocation_trial_limit':
            return status
        if reason.startswith('transient_abort:'):
            if provider == 'gemini':
                print(
                    'توقف Gemini عابرًا؛ حُفظ التقدم وسيُستأنف لاحقًا.'
                )
                return status
            wait_seconds = 90 if provider == 'gemini' else 45
            time.sleep(wait_seconds)
            continue
        raise RuntimeError(f'توقف غير عابر في {provider}: {reason}')
    raise RuntimeError(f'تجاوز دورات الاستئناف في {provider}')

anthropic_status = None
gemini_status = None
study_complete = False
if gate_ready:
    anthropic_status = run_provider('anthropic', 0.0)
    assert anthropic_status['remaining'] == 0

    daily_limit = 20
    gate_calls = int(
        gemini_gate.get('invocation_attempts_this_run', 0)
    )
    gemini_daily_budget = max(0, daily_limit - gate_calls)
    print({
        'gemini_daily_limit': daily_limit,
        'gate_calls_today': gate_calls,
        'main_study_calls_allowed_now': gemini_daily_budget,
    })
    gemini_status = run_provider(
        'gemini',
        15.5,
        max_trials=gemini_daily_budget,
        max_cycles=1,
    )
    study_complete = gemini_status['remaining'] == 0
"""
    ),
    code(
        """
audit_script = root / 'scripts' / 'audit_proposal_conformance.py'

def final_audit(provider):
    event_log = drive_root / f'pfb-definitive-v1p2-{provider}.jsonl'
    output = drive_root / f'{provider}_v1p2_final_audit.json'
    command = [
        sys.executable, str(audit_script),
        '--root', str(root),
        '--event-log', str(event_log),
        '--output', str(output),
        '--require-conformant',
    ]
    result = subprocess.run(command, env=env, text=True, capture_output=True)
    print(result.stdout)
    if result.stderr:
        print(result.stderr)
    if result.returncode:
        raise RuntimeError(f'فشل التدقيق النهائي لـ{provider}')
    report = json.loads(output.read_text())
    assert report['audited_proposal_count'] == 400
    assert report['proposal_conformance_failed'] == 0
    assert report['oracle_failure_count'] == 0
    assert report['containment_failure_count'] == 0
    return report

if study_complete:
    anthropic_final = final_audit('anthropic')
    gemini_final = final_audit('gemini')

    config = json.loads(config_path.read_text())
    config['live_enabled'] = False
    config['study_enabled'] = False
    config_path.write_text(json.dumps(config, indent=2) + '\\n')

    result_manifest = {
        'schema_version': '1.0',
        'study': 'pfb-definitive-v1p2',
        'protocol_version': '1.2',
        'confirmatory_records': {
            'openai_v1': 400,
            'anthropic_v1p2': 400,
            'gemini_v1p2': 400,
        },
        'statuses': {
            'anthropic': anthropic_status,
            'gemini': gemini_status,
        },
        'files': {},
    }
    for path in sorted(drive_root.rglob('*')):
        if path.is_file():
            relative = str(path.relative_to(drive_root))
            result_manifest['files'][relative] = {
                'bytes': path.stat().st_size,
                'sha256': hashlib.sha256(path.read_bytes()).hexdigest(),
            }
    (drive_root / 'RESULT_MANIFEST_V1P2.json').write_text(
        json.dumps(result_manifest, indent=2, sort_keys=True) + '\\n'
    )
    archive_name = 'PolicyFaultBench_CONFIRMATORY_V1P2_results'
    outcome = 'اكتملت الدراسة والتدقيق النهائي'
else:
    archive_name = 'PolicyFaultBench_V1P2_DAILY_CHECKPOINT'
    remaining = (
        gemini_status['remaining'] if gemini_status is not None else 400
    )
    days_estimate = (remaining + 19) // 20
    outcome = (
        f'حُفظت نقطة المتابعة؛ متبقي Gemini: {remaining}، '
        f'نحو {days_estimate} أيام من الحصة المجانية'
    )

archive_base = Path('/content/drive/MyDrive') / archive_name
archive_path = shutil.make_archive(
    str(archive_base), 'zip', root_dir=drive_root
)
with zipfile.ZipFile(archive_path) as handle:
    assert handle.testzip() is None
print({
    'status': outcome,
    'archive_saved_to_drive': archive_path,
    'download_starting': True,
})
files.download(archive_path)
"""
    ),
]

notebook = {
    "cells": cells,
    "metadata": {
        "colab": {
            "name": "PolicyFaultBench_Colab_V1P2_DAILY_RESUME.ipynb",
            "provenance": [],
        },
        "kernelspec": {
            "display_name": "Python 3",
            "language": "python",
            "name": "python3",
        },
        "language_info": {"name": "python"},
    },
    "nbformat": 4,
    "nbformat_minor": 5,
}

output = (
    Path(__file__).resolve().parents[2]
    / "PolicyFaultBench_Colab_V1P2_DAILY_RESUME.ipynb"
)
output.write_text(
    json.dumps(notebook, indent=2, ensure_ascii=False) + "\n",
    encoding="utf-8",
)
print(output)
