import json
import subprocess
from pathlib import Path

import pytest

from tests.dashboard_bundle import dashboard


HANDOFF = Path(__file__).resolve().parents[1] / "frontend" / "update-review-handoff.js"


def run_handoff(script):
    source = f"const createHandoff = require({json.dumps(str(HANDOFF))});\n" + script
    result = subprocess.run(["node", "-e", source], capture_output=True, text=True)
    assert result.returncode == 0, result.stderr
    return json.loads(result.stdout)


def test_review_handoff_only_admits_review_requested_pull_updates():
    result = run_handoff("""
const handoff = createHandoff({openReview:()=>{}, admitRead:async()=>{}, advance:()=>{}});
const cases = [
  {is_review:true, update_reason:'Review requested', subject_type:'Pull', repository:'acme/app', number:7, notification_id:41},
  {is_review:true, update_reason:'Mentioned', subject_type:'Pull', repository:'acme/app', number:7, notification_id:42},
  {is_review:true, update_reason:'Review requested', subject_type:'Issue', repository:'acme/app', number:7, notification_id:43},
  {is_review:false, update_reason:'Review requested', subject_type:'Pull', repository:'acme/app', number:7, notification_id:44},
];
process.stdout.write(JSON.stringify(cases.map(item => handoff.eligible(item))));
""")

    assert result == [True, False, False, False]


def test_review_handoff_preserves_update_on_cancel_and_advances_after_read_admission():
    result = run_handoff("""
(async () => {
  const events = [];
  const update = {is_review:true, update_reason:'Review requested', subject_type:'Pull', repository:'acme/app', number:7, notification_id:41, title:'Ship it'};
  const handoff = createHandoff({
    openReview:item=>events.push(['open', item.repository, item.number]),
    restoreUpdate:item=>events.push(['restore', item.notification_id]),
    admitRead:async id=>events.push(['read', id]),
    advance:()=>events.push(['advance']),
  });
  handoff.begin(update);
  handoff.cancel();
  handoff.begin(update);
  const completed = await handoff.complete();
  process.stdout.write(JSON.stringify({events, completed, active:handoff.active()}));
})().catch(error => { console.error(error); process.exit(1); });
""")

    assert result == {
        "events": [
            ["open", "acme/app", 7],
            ["restore", 41],
            ["open", "acme/app", 7],
            ["read", 41],
            ["advance"],
        ],
        "completed": True,
        "active": False,
    }


def test_review_handoff_keeps_current_update_when_read_admission_fails():
    result = run_handoff("""
(async () => {
  const events = [];
  const update = {is_review:true, update_reason:'Review requested', subject_type:'Pull', repository:'acme/app', number:7, notification_id:41};
  const handoff = createHandoff({
    openReview:()=>events.push('open'),
    admitRead:async()=>{ events.push('read'); throw new Error('queue unavailable'); },
    advance:()=>events.push('advance'),
  });
  handoff.begin(update);
  let message = '';
  try { await handoff.complete(); } catch (error) { message = error.message; }
  process.stdout.write(JSON.stringify({events, message, active:handoff.active()}));
})().catch(error => { console.error(error); process.exit(1); });
""")

    assert result == {"events": ["open", "read"], "message": "queue unavailable", "active": True}


@pytest.mark.anyio
async def test_dashboard_wires_review_requested_update_into_existing_review_workspace():
    html = await dashboard()

    assert '<script src="static/update-review-handoff.js"></script>' in html
    assert 'id="review-update-now"' in html
    assert "updateReviewHandoff.begin(selectedUpdate)" in html
    assert "await updateReviewHandoff.complete()" in html
    assert "updateReviewHandoff.cancel()" in html
    assert "updateReviewHandoff.eligible(detail" in html
