import json
import subprocess
from pathlib import Path

import pytest

from tests.dashboard_bundle import dashboard


OFFLINE_WORK = Path(__file__).parents[1] / "frontend" / "offline-work.js"


def run_scenario(scenario: str) -> dict:
    script = f"""
const createOfflineWorkStore = require({json.dumps(str(OFFLINE_WORK))});
const values = new Map();
const storage = {{
  getItem: key => values.has(key) ? values.get(key) : null,
  setItem: (key, value) => values.set(key, value),
  removeItem: key => values.delete(key),
}};
{scenario}
"""
    result = subprocess.run(
        ["node", "-e", script], check=True, capture_output=True, text=True
    )
    return json.loads(result.stdout)


def run_async_scenario(scenario: str) -> dict:
    script = f"""
const createOfflineWorkStore = require({json.dumps(str(OFFLINE_WORK))});
const values = new Map();
const records = new Map();
const storage = {{
  getItem: key => values.has(key) ? values.get(key) : null,
  setItem: (key, value) => values.set(key, value),
  removeItem: key => values.delete(key),
}};
const transaction = async work => work({{
  get: async key => records.get(key),
  getAll: async () => [...records.values()],
  put: async value => records.set(value.id, structuredClone(value)),
  delete: async key => records.delete(key),
  clear: async () => records.clear(),
}});
(async () => {{
{scenario}
}})().catch(error => {{ console.error(error); process.exit(1); }});
"""
    result = subprocess.run(
        ["node", "-e", script], check=True, capture_output=True, text=True
    )
    return json.loads(result.stdout)


def test_indexeddb_commit_persists_snapshot_and_details_without_localstorage_payloads():
    result = run_async_scenario("""
const store = createOfflineWorkStore({storage, transaction, now:() => new Date('2026-08-07T12:00:00Z')});
store.setEnabled(true);
const snapshotSaved = await store.save({
  user:{login:'timmy', full_name:'Timmy'}, issues:[{number:8, title:'Large offline review'}],
  pull_requests:[], notifications:[],
});
const item = {kind:'pull', repository:'stackchain/dashboard', number:8, is_review:true};
const detailSaved = await store.saveDetail('timmy', item, {
  title:'Large offline review', head_sha:'abc123',
  files:[{filename:'large.diff', diff_lines:['+private review line']}],
});
const restarted = createOfflineWorkStore({storage, transaction, now:() => new Date('2026-08-07T12:01:00Z')});
process.stdout.write(JSON.stringify({
  snapshotSaved, detailSaved,
  loaded:await restarted.load('timmy'),
  detail:await restarted.loadDetail('timmy', item),
  localValues:[...values.values()].join(' '),
  recordIds:[...records.keys()].sort(),
}));
""")

    assert result["snapshotSaved"] is True
    assert result["detailSaved"] is True
    assert result["loaded"]["user"]["login"] == "timmy"
    assert result["detail"]["head_sha"] == "abc123"
    assert "Large offline review" not in result["localValues"]
    assert "private review line" not in result["localValues"]
    assert result["recordIds"] == ["detail:timmy:pull:stackchain/dashboard:8", "snapshot"]


def test_legacy_localstorage_payloads_migrate_only_after_indexeddb_commit():
    result = run_async_scenario("""
values.set('stackchain.offline-work.enabled.v1', 'true');
values.set('stackchain.offline-work.snapshot.v1', JSON.stringify({
  version:1, user_login:'timmy', saved_at:'2026-08-07T12:00:00.000Z',
  data:{user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]},
}));
values.set('stackchain.offline-work.details.v1', JSON.stringify([{
  key:'issue:stackchain/dashboard:9', user_login:'timmy', saved_at:'2026-08-07T12:00:00.000Z',
  data:{title:'Migrated issue'},
}]));
const store = createOfflineWorkStore({storage, transaction, now:() => new Date('2026-08-07T12:01:00Z')});
const snapshot = await store.load('timmy');
const detail = await store.loadDetail('timmy', {kind:'issue', repository:'stackchain/dashboard', number:9});
process.stdout.write(JSON.stringify({
  snapshot, detail,
  legacySnapshot:values.has('stackchain.offline-work.snapshot.v1'),
  legacyDetails:values.has('stackchain.offline-work.details.v1'),
  recordIds:[...records.keys()].sort(),
}));
""")

    assert result["snapshot"]["user"]["login"] == "timmy"
    assert result["detail"]["title"] == "Migrated issue"
    assert result["legacySnapshot"] is False
    assert result["legacyDetails"] is False
    assert result["recordIds"] == ["detail:timmy:issue:stackchain/dashboard:9", "snapshot"]


def test_failed_migration_keeps_legacy_snapshot_readable_and_untouched():
    result = run_async_scenario("""
values.set('stackchain.offline-work.enabled.v1', 'true');
values.set('stackchain.offline-work.snapshot.v1', JSON.stringify({
  version:1, user_login:'timmy', saved_at:'2026-08-07T12:00:00.000Z',
  data:{user:{login:'timmy'}, issues:[{number:7, title:'Keep me'}], pull_requests:[], notifications:[]},
}));
let attempts = 0;
const flakyTransaction = async work => {
  attempts += 1;
  if (attempts === 1) throw new Error('quota admission failed');
  return transaction(work);
};
const store = createOfflineWorkStore({storage, transaction:flakyTransaction, now:() => new Date('2026-08-07T12:01:00Z')});
const snapshot = await store.load('timmy');
process.stdout.write(JSON.stringify({
  snapshot, attempts,
  legacySnapshot:values.has('stackchain.offline-work.snapshot.v1'),
  recordIds:[...records.keys()],
}));
""")

    assert result["snapshot"]["issues"][0]["title"] == "Keep me"
    assert result["legacySnapshot"] is True
    assert result["recordIds"] == []


def test_ready_atomically_prunes_all_expired_indexeddb_records():
    result = run_async_scenario("""
const writer = createOfflineWorkStore({storage, transaction, now:() => new Date('2026-08-07T12:00:00Z'), maxAgeMs:1000});
writer.setEnabled(true);
await writer.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]});
await writer.saveDetail('timmy', {kind:'issue', repository:'stackchain/dashboard', number:1}, {title:'One'});
await writer.saveDetail('timmy', {kind:'pull', repository:'stackchain/dashboard', number:2}, {title:'Two'});
const expired = createOfflineWorkStore({storage, transaction, now:() => new Date('2026-08-07T12:00:02Z'), maxAgeMs:1000});
await expired.ready();
process.stdout.write(JSON.stringify({recordIds:[...records.keys()]}));
""")

    assert result["recordIds"] == []


def test_ready_degrades_within_deadline_when_indexeddb_never_settles():
    result = run_async_scenario("""
values.set('stackchain.offline-work.enabled.v1', 'true');
values.set('stackchain.offline-work.snapshot.v1', JSON.stringify({
  version:1, user_login:'timmy', saved_at:'2026-08-07T12:00:00.000Z',
  data:{user:{login:'timmy'}, issues:[{number:7, title:'Still available'}], pull_requests:[], notifications:[]},
}));
const neverTransaction = async () => new Promise(() => {});
const store = createOfflineWorkStore({
  storage, transaction:neverTransaction, initializationTimeoutMs:20,
  now:() => new Date('2026-08-07T12:01:00Z'),
});
const started = Date.now();
const outcome = await Promise.race([
  store.ready().then(value => ({value, status:store.status(), saved:store.load('timmy')})),
  new Promise(resolve => setTimeout(() => resolve({timedOut:true}), 100)),
]);
process.stdout.write(JSON.stringify({...outcome, elapsed:Date.now() - started,
  legacySnapshot:values.has('stackchain.offline-work.snapshot.v1')}));
""")

    assert result.get("timedOut", False) is False
    assert result["value"] is False
    assert result["status"] == {"state": "degraded", "reason": "deadline"}
    assert result["saved"]["issues"][0]["title"] == "Still available"
    assert result["legacySnapshot"] is True
    assert result["elapsed"] < 100


def test_degraded_store_rejects_saves_without_waiting_for_stalled_indexeddb():
    result = run_async_scenario("""
values.set('stackchain.offline-work.enabled.v1', 'true');
const neverTransaction = async () => new Promise(() => {});
const store = createOfflineWorkStore({storage, transaction:neverTransaction, initializationTimeoutMs:10});
await store.ready();
const outcome = await Promise.race([
  store.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]})
    .then(value => ({value})),
  new Promise(resolve => setTimeout(() => resolve({timedOut:true}), 80)),
]);
process.stdout.write(JSON.stringify(outcome));
""")

    assert result == {"value": False}


def test_degraded_store_clears_legacy_data_without_waiting_for_indexeddb():
    result = run_async_scenario("""
values.set('stackchain.offline-work.snapshot.v1', '{"private":"snapshot"}');
values.set('stackchain.offline-work.details.v1', '[{"private":"detail"}]');
const neverTransaction = async () => new Promise(() => {});
const store = createOfflineWorkStore({storage, transaction:neverTransaction, initializationTimeoutMs:10});
await store.ready();
const outcome = await Promise.race([
  store.clear().then(value => ({value, keys:[...values.keys()]})),
  new Promise(resolve => setTimeout(() => resolve({timedOut:true}), 80)),
]);
process.stdout.write(JSON.stringify(outcome));
""")

    assert result == {"value": True, "keys": []}


def test_retry_recovers_durable_saving_after_initialization_deadline():
    result = run_async_scenario("""
let stalled = true;
const recoveringTransaction = async work => stalled ? new Promise(() => {}) : transaction(work);
const store = createOfflineWorkStore({storage, transaction:recoveringTransaction, initializationTimeoutMs:10});
const initial = await store.ready();
stalled = false;
const recovered = await store.retry();
store.setEnabled(true);
const saved = await store.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]});
process.stdout.write(JSON.stringify({initial, recovered, saved, status:store.status(), recordIds:[...records.keys()]}));
""")

    assert result == {
        "initial": False,
        "recovered": True,
        "saved": True,
        "status": {"state": "ready", "reason": ""},
        "recordIds": ["snapshot"],
    }


def test_opted_in_snapshot_survives_restart_with_only_queue_card_fields():
    result = run_scenario("""
const store = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:00:00Z')});
store.setEnabled(true);
store.save({
  user:{login:'timmy', full_name:'Timmy', token:'secret'},
  issues:[{id:1, number:8, title:'Ship offline work', body:'private body', state:'open',
    repository:'stackchain/dashboard', labels:['P0'], assignees:['timmy'],
    updated_at:'2026-08-07T11:00:00Z', url:'https://forge.example/issues/8'}],
  pull_requests:[],
  notifications:[{id:9, number:8, title:'Updated', unread:true, subject_body:'private comment',
    repository:'stackchain/dashboard', subject_type:'Issue', updated_at:'2026-08-07T11:30:00Z',
    url:'https://forge.example/issues/8'}],
  work_pagination:{issue:{page:1,total:1,has_more:false}},
  repos:[{private:true}], events:[{body:'private event'}],
});
const restarted = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:01:00Z')});
process.stdout.write(JSON.stringify({loaded:restarted.load('timmy'), raw:[...values.values()].join(' ')}));
""")

    assert result["loaded"]["user"] == {"login": "timmy", "full_name": "Timmy"}
    assert result["loaded"]["issues"][0]["title"] == "Ship offline work"
    assert result["loaded"]["notifications"][0]["unread"] is True
    assert result["loaded"]["saved_at"] == "2026-08-07T12:00:00.000Z"
    assert "private body" not in result["raw"]
    assert "private comment" not in result["raw"]
    assert "private event" not in result["raw"]
    assert "secret" not in result["raw"]


def test_expired_snapshot_is_deleted_and_wrong_user_cannot_load_it():
    result = run_scenario("""
const writer = createOfflineWorkStore({storage, now:() => new Date('2026-08-01T12:00:00Z'), maxAgeMs:1000});
writer.setEnabled(true);
writer.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]});
const wrongUser = writer.load('alexander');
const expired = createOfflineWorkStore({storage, now:() => new Date('2026-08-01T12:00:02Z'), maxAgeMs:1000});
const expiredValue = expired.load('timmy');
process.stdout.write(JSON.stringify({wrongUser, expiredValue, snapshotStillStored:values.has('stackchain.offline-work.snapshot.v1')}));
""")

    assert result == {
        "wrongUser": None,
        "expiredValue": None,
        "snapshotStillStored": False,
    }


def test_opting_out_deletes_the_persisted_snapshot():
    result = run_scenario("""
const store = createOfflineWorkStore({storage});
store.setEnabled(true);
store.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]});
store.setEnabled(false);
process.stdout.write(JSON.stringify({enabled:store.enabled(), loaded:store.load()}));
""")

    assert result == {"enabled": False, "loaded": None}


def test_opted_in_detail_cache_is_allowlisted_account_bound_and_bounded():
    result = run_scenario("""
const store = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:00:00Z'), maxDetails:2});
store.setEnabled(true);
const issue = {kind:'issue', repository:'stackchain/dashboard', number:8};
store.saveDetail('timmy', issue, {
  title:'Ship offline work', body:'Safe description', state:'open', labels:['P0'], assignees:['timmy'],
  url:'https://forge.example/issues/8', token:'secret', files:[{patch:'private diff'}],
  conversation:{comments:[{id:1, author:'alexander', body:'Newest message', created_at:'2026-08-07T11:00:00Z',
    token:'comment-secret'}], page:1, older_page:null, total:1},
});
store.saveDetail('timmy', {kind:'pull', repository:'stackchain/dashboard', number:9}, {title:'PR 9', body:'Body 9'});
store.saveDetail('timmy', {kind:'issue', repository:'stackchain/dashboard', number:10}, {title:'Issue 10', body:'Body 10'});
const raw = [...values.values()].join(' ');
process.stdout.write(JSON.stringify({
  evicted:store.loadDetail('timmy', issue),
  loaded:store.loadDetail('timmy', {kind:'pull', repository:'stackchain/dashboard', number:9}),
  wrongUser:store.loadDetail('alexander', {kind:'pull', repository:'stackchain/dashboard', number:9}),
  raw,
}));
""")

    assert result["evicted"] is None
    assert result["wrongUser"] is None
    assert result["loaded"]["title"] == "PR 9"
    assert result["loaded"]["saved_at"] == "2026-08-07T12:00:00.000Z"
    assert "secret" not in result["raw"]
    assert "private diff" not in result["raw"]


def test_unread_update_conversation_detail_is_private_bounded_and_account_bound():
    result = run_scenario("""
const store = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:00:00Z')});
store.setEnabled(true);
const update = {kind:'update', notification_id:42};
store.saveDetail('timmy', update, {
  id:42, repository:'stackchain/dashboard', title:'Deployment blocked',
  subject_type:'Issue', state:'open', subject_body:'Safe subject context',
  url:'https://forge.example/issues/8#issuecomment-9', token:'secret',
  issue:{number:8, assignees:[], claimable:true}, files:[{patch:'private diff'}],
  conversation:{
    comments:Array.from({length:25}, (_, index) => ({
      id:index + 1, author:'alexander', body:'Message ' + (index + 1),
      created_at:'2026-08-07T11:00:00Z', token:'comment-secret',
    })),
    page:1, older_page:2, total:40,
  },
});
const raw = [...values.values()].join(' ');
process.stdout.write(JSON.stringify({
  loaded:store.loadDetail('timmy', update),
  wrongUser:store.loadDetail('alexander', update),
  raw,
}));
""")

    assert result["wrongUser"] is None
    assert result["loaded"]["title"] == "Deployment blocked"
    assert result["loaded"]["subject_body"] == "Safe subject context"
    assert result["loaded"]["subject_type"] == "Issue"
    assert len(result["loaded"]["conversation"]["comments"]) == 20
    assert result["loaded"]["conversation"]["comments"][0]["body"] == "Message 6"
    assert result["loaded"]["saved_at"] == "2026-08-07T12:00:00.000Z"
    assert "secret" not in result["raw"]
    assert "private diff" not in result["raw"]


def test_requested_review_cache_is_sha_scoped_allowlisted_and_bounded():
    result = run_scenario("""
const store = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:00:00Z')});
store.setEnabled(true);
const review = {kind:'pull', repository:'stackchain/dashboard', number:12, is_review:true};
store.saveDetail('timmy', review, {
  title:'Review offline work', body:'Review description', author:'alexander', head_sha:'abc123',
  ci_state:'success', token:'top-secret', source_updated_at:'2026-08-07T11:00:00Z',
  files:Array.from({length:55}, (_, fileIndex) => ({
    filename:'src/file-' + fileIndex + '.js', status:'modified', additions:2, deletions:1,
    diff_available:true, diff_binary:false, diff_truncated:false, patch:'must-not-persist',
    diff_lines:Array.from({length:405}, (_, lineIndex) => '+' + fileIndex + ':' + lineIndex),
  })),
  reviews:Array.from({length:25}, (_, index) => ({
    id:index + 1, state:'COMMENT', body:'Review ' + (index + 1),
    submitted_at:'2026-08-07T10:00:00Z', user:{login:'reviewer-' + index, email:'private@example.test'},
    token:'review-secret',
  })),
});
const loaded = store.loadDetail('timmy', review);
process.stdout.write(JSON.stringify({
  loaded,
  wrongUser:store.loadDetail('alexander', review),
  raw:[...values.values()].join(' '),
}));
""")

    assert result["wrongUser"] is None
    assert result["loaded"]["head_sha"] == "abc123"
    assert result["loaded"]["ci_state"] == "success"
    assert len(result["loaded"]["files"]) == 50
    assert len(result["loaded"]["files"][0]["diff_lines"]) == 400
    assert result["loaded"]["files"][0]["filename"] == "src/file-0.js"
    assert len(result["loaded"]["reviews"]) == 20
    assert result["loaded"]["reviews"][0]["body"] == "Review 6"
    assert result["loaded"]["reviews"][0]["user"] == {"login": "reviewer-5"}
    assert "top-secret" not in result["raw"]
    assert "must-not-persist" not in result["raw"]
    assert "private@example.test" not in result["raw"]
    assert "review-secret" not in result["raw"]


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

    assert '<script src="static/offline-work.js"></script>' in html
    assert 'id="keep-work-offline"' in html
    assert 'Keep My Work available offline' in html
    assert 'id="clear-offline-work"' in html
    assert 'id="offline-work-status"' in html
    assert 'offlineWorkStore.save({' in html
    assert 'offlineWorkStore.load()' in html
    assert 'Offline · saved ' in html
    assert "setOfflineWorkMode(true)" in html
    assert "if (offlineWorkMode)" in html
    assert '.offline-work-controls button { min-height:44px;' in html


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

    assert "createOfflineWorkStore({ storage: localStorage, indexedDB:window.indexedDB })" in html
    assert "let offlineStorageReady = await offlineWorkStore.ready();" in html
    assert 'id="retry-offline-storage"' in html
    assert "Offline saving unavailable · online work remains live." in html
    assert "await offlineWorkStore.retry()" in html
    assert "async function hydrateOfflineWork" in html
    assert "const saved = await offlineWorkStore.load();" in html
    assert "await offlineWorkStore.save({" in html
    assert "await offlineWorkStore.clear();" in html
    assert "Offline saving unavailable · retry after reconnect." in html


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

    assert "if (!hasContextSnapshot && await hydrateOfflineWork('outage')) return;" in html
    assert "Outage · saved " in html
    assert "Server unavailable · showing private My Work saved " in html
    assert "Live details and actions will return automatically." in html
    assert "function renderLiveSnapshot(snapshot, changedSections" in html
    assert "setOfflineWorkMode(false);\n    offlineStatus.hidden = true;" in html


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

    assert "offlineWorkStore.loadDetail(offlineLogin, item)" in html
    assert "Details not saved—reconnect to open this item." in html
    assert "openIssueSheet(item, trigger, savedDetail)" in html
    assert "openPullSheet(item, trigger, savedDetail)" in html
    assert "offlineWorkStore.saveDetail(confirmedOwnerLogin, item, detail)" in html
    assert "todayWork.contains(item)" in html
    assert "Offline copy · saved " in html
    assert "setOfflineDetailControls('issue')" in html
    assert "setOfflineDetailControls('pull')" in html
    assert "qs('#issue-planning').inert = true;" in html
    assert "qs('#issue-handoff').inert = true;" in html
    assert "qs('#pull-review').inert = true;" in html
    assert "qs('#issue-planning').inert = false;" in html
    assert "qs('#pull-review').inert = false;" in html
    assert "confirmedOwnerLogin = String(saved.user?.login || '').trim();" in html


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

    assert "notificationReader.open(item, savedDetail)" in html
    assert "openRoutedWork(item, button);" in html
    assert "offlineWorkStore.saveDetail(confirmedOwnerLogin, selectedUpdate, detail)" in html
    assert "Offline update · saved " in html
    assert "setOfflineUpdateControls(true)" in html
    assert "qs('#mark-update-read-next').disabled = false;" in html
    assert "offline ? 'Queue read & next' : 'Mark read & next'" in html
    assert "qs('#update-ownership-action').disabled = offline;" in html
    assert "qs('#load-older-update-comments').disabled = offline;" in html
    assert "document.querySelectorAll('[data-notification-id], [data-later-preset], [data-today-add]')" in html
    assert "replies and read acknowledgements queue for sync" in html


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

    assert "item.is_review ? reviewController.load(item)" in html
    assert "openReviewSheet(item, trigger, savedDetail)" in html
    assert "async function openReviewSheet(item, trigger, cachedDetail = null)" in html
    assert "cachedDetail || await reviewController.load(selectedReview)" in html
    assert "Offline review · saved " in html
    assert "Queue review for reconnect" in html
    assert "kind: 'pull-review'" in html
    assert "await authoredOutbox.enqueueDurably" in html
    assert "expectedHeadSha: selectedReviewHead" in html
    assert "comments: snapshot.comments" in html
    assert "draftKey: draft.storageKey" in html
    assert "progressKey: progress?.storageKey" in html
    assert "draftFingerprint: localStorage.getItem(draft.storageKey) || ''" in html
    assert "progressFingerprint: localStorage.getItem(progress?.storageKey) || ''" in html
    assert "Review queued · it will submit after reconnect." in html
    assert "if (selectedReview && offlineReview) openReviewSheet(selectedReview, reviewTrigger);" in html


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

    assert "function reviewingActiveTodayItem()" in html
    assert "offlineReview && reviewingActiveTodayItem() ? 'Queue review & next'" in html
    handler = html.split("qs('#submit-review').addEventListener('click'", 1)[1].split(
        "qs('#continue-review-to-merge').addEventListener", 1
    )[0]
    admission = handler.index("await authoredOutbox.enqueueDurably")
    completion = handler.index("completeTodayItem(selectedReview")
    assert admission < completion
    assert "successMessage: 'Review queued. Next Today item opened.'" in handler
    assert "failureMessage: 'Review queued, but Today still needs completion.'" in handler
    assert "if (!advanced)" in handler
    assert "Review queued, but Today still needs completion." in handler


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

    assert '<script src="static/offline-issue-close.js"></script>' in html
    assert "const closeOfflineIssue = createOfflineIssueClose({" in html
    assert "enqueueDurably: message => authoredOutbox.enqueueDurably(message)" in html
    assert "completeToday: (item, options) => completeTodayItem(item, options)" in html
    assert "workSession.active() ? 'Queue close & next' : 'Queue issue closure'" in html
    handler = html.split("qs('#close-issue').addEventListener('click'", 1)[1].split(
        "qs('#close-pull-sheet').addEventListener", 1
    )[0]
    admission = handler.index("await closeOfflineIssue(closing)")
    sheet_close = handler.index("closeIssueSheet()")
    assert admission < sheet_close
    assert "Issue closure queued for reconnect." in handler
    assert "The issue remains in Today; retry." in handler
