import json
from pathlib import Path

data = json.loads(Path('/Users/iggy/.hermes/profiles/ignite_team/outbound/krb-about-us-source-content.json').read_text())
js_data = json.dumps(data, ensure_ascii=False)
script = f"""// KRB About Us text-content population pass. No publishing. Generated from live WP scrape.
const SOURCE = {js_data};
const TARGETS = [
  {{ name: 'Our Campus', id: '6a2b8d959cfde20f4b4e0535', path: '/about-us/our-campus' }},
  {{ name: 'Our People', id: '6a2b903289294179fd567656', path: '/about-us/our-people' }},
  {{ name: 'School Board', path: '/about-us/our-people/school-board' }},
  {{ name: 'Senior Executive', path: '/about-us/our-people/senior-executive' }},
  {{ name: 'Employment', id: '6a2b903b944f448e28b408c3', path: '/about-us/our-people/employment' }},
  {{ name: 'Our Policies', id: '6a2b904caada4c776865154b', path: '/about-us/our-policies' }},
];
const safe = async (fn, fb = null) => {{ try {{ return await fn(); }} catch (e) {{ return fb; }} }};
const norm = s => String(s || '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
const unwrap = v => v && typeof v === 'object' && 'value' in v ? v.value : v;
const preview = v => {{ v = unwrap(v); if (v == null) return ''; if (typeof v === 'string') return v.replace(/\\s+/g,' ').trim().slice(0,120); if (typeof v === 'object' && typeof v.html === 'string') return v.html.replace(/<[^>]*>/g,' ').replace(/\\s+/g,' ').trim().slice(0,120); return JSON.stringify(v).slice(0,120); }};
async function pageInfo(p) {{ const d = await helpers.describe(p); return {{ id: await safe(() => p.getId(), d.id), name: await safe(() => p.getName(), d.name), publishPath: await safe(() => p.getPublishPath(), d.publishPath), type: await safe(() => p.getType(), d.type), title: await safe(() => p.getTitle(), d.title) }}; }}
async function compName(el) {{ const c = await safe(() => el.getComponent(), null); return c ? await safe(() => c.getName(), null) : null; }}
async function walk(el, out=[]) {{ out.push(el); const kids = await safe(() => el.getChildren(), []); for (const k of (Array.isArray(kids) ? kids : [])) await walk(k, out); return out; }}
async function componentRows() {{ const rows=[]; for (const el of await walk(await webflow.getRootElement())) {{ const name = await compName(el); if (name) rows.push({{ el, name }}); }} return rows; }}
async function props(el) {{ return await safe(() => el.searchProps(), []); }}
function valueFor(prop, value) {{
  const vt = prop.valueType || '';
  if (vt === 'boolean') return Boolean(value);
  if (vt === 'number') return Number(value) || 0;
  return String(value == null ? '' : value);
}}
async function setByLabel(el, labelMatchers, value, opts={{}}) {{
  const ps = await props(el);
  const matches = ps.filter(p => {{ const l = norm(p.display?.label || p.label || p.name || ''); return labelMatchers.some(m => typeof m === 'string' ? l === norm(m) : m.test(l)); }});
  const target = opts.last ? matches[matches.length - 1] : matches[0];
  if (!target) return {{ ok:false, reason:'prop_not_found', labels: ps.map(p => p.display?.label || p.label || p.name || '').slice(0,40) }};
  await el.setProps([{{ propId: target.propId || target.id, value: valueFor(target, value) }}]);
  return {{ ok:true, label: target.display?.label || target.label || target.name || '', propId: target.propId || target.id, valuePreview: String(value).slice(0,80) }};
}}
async function setBooleanByLabel(el, labelMatchers, value) {{
  const ps = await props(el);
  const target = ps.find(p => {{ const l = norm(p.display?.label || p.label || p.name || ''); return labelMatchers.some(m => typeof m === 'string' ? l === norm(m) : m.test(l)); }});
  if (!target) return {{ ok:false, reason:'prop_not_found' }};
  await el.setProps([{{ propId: target.propId || target.id, value: Boolean(value) }}]);
  return {{ ok:true, label: target.display?.label || target.label || target.name || '', value }};
}}
async function populateTextContent(el, block) {{
  const updates=[];
  if (block.heading) updates.push(['heading', await setByLabel(el, [/^heading$/, /^title$/, /section heading/, /main heading/], block.heading)]);
  updates.push(['text', await setByLabel(el, [/paragraph/, /rich text/, /^text$/, /content/, /body/], block.text || block.html || '', {{ last:false }})]);
  updates.push(['hide', await setBooleanByLabel(el, [/hide section/, /^hide$/], false)]);
  return updates;
}}
async function populateTwoColumn(el, block, index) {{
  const updates=[];
  updates.push(['heading', await setByLabel(el, [/^heading$/, /^title$/, /section heading/, /main heading/], block.heading || '')]);
  updates.push(['text', await setByLabel(el, [/paragraph/, /rich text/, /^text$/, /content/, /body/], block.text || block.html || '')]);
  updates.push(['hide', await setBooleanByLabel(el, [/hide section/, /^hide$/], false)]);
  // Alternate layout if a recognised layout/image-position prop exists. Ignore if component lacks it.
  const leftRight = index % 2 === 0 ? 'Right' : 'Left';
  const layoutAttempt = await setByLabel(el, [/image position/, /media position/, /layout/], leftRight);
  if (layoutAttempt.ok) updates.push(['layout', layoutAttempt]);
  return updates;
}}
async function populateHero(el, pageName) {{
  const updates=[];
  updates.push(['heading', await setByLabel(el, [/^heading$/, /^title$/, /page heading/, /hero heading/], pageName)]);
  updates.push(['hide', await setBooleanByLabel(el, [/hide section/, /^hide$/], false)]);
  return updates;
}}
async function populateDownloads(el, block) {{
  const updates=[];
  updates.push(['heading', await setByLabel(el, [/^heading$/, /^title$/, /section heading/, /downloads heading/], block.heading || '')]);
  updates.push(['hide', await setBooleanByLabel(el, [/hide section/, /^hide$/], false)]);
  return updates;
}}
const pages=[]; for (const p of await webflow.getAllPagesAndFolders()) pages.push({{page:p, info: await pageInfo(p)}});
const report=[];
for (const t of TARGETS) {{
  const source = SOURCE[t.name];
  const matches = pages.filter(({{info}}) => info.id === t.id || info.publishPath === t.path || info.publishPath === t.path + '/' || (info.name === t.name && info.type === 'Page'));
  if (!source || matches.length !== 1) {{ report.push({{target:t.name,status:matches.length?'ambiguous_or_missing_source':'missing', matches:matches.map(m=>m.info)}}); continue; }}
  await webflow.switchPage(matches[0].page); await new Promise(r=>setTimeout(r,550));
  const rows = await componentRows();
  const byName = name => rows.filter(r => r.name === name).map(r => r.el);
  const pageReport={{ target:t.name, page:matches[0].info.publishPath, updates:[], counts:{{}} }};
  for (const r of rows) pageReport.counts[r.name]=(pageReport.counts[r.name]||0)+1;
  const heroes=byName('Section / Hero');
  if (heroes[0]) pageReport.updates.push({{component:'Section / Hero', order:0, updates: await populateHero(heroes[0], t.name)}});
  const textEls=byName('Section / Text Content');
  const textBlocks=[...(source.textContent || [])];
  // For Employment, use the accordion group title/intro as the second text-content block if present.
  if (t.name === 'Employment' && source.accordions?.length && textBlocks.length < textEls.length) textBlocks.push({{heading: source.accordions[0].groupHeading || 'The benefits of working at our School', text: ''}});
  for (let i=0; i<Math.min(textEls.length, textBlocks.length); i++) pageReport.updates.push({{component:'Section / Text Content', order:i+1, source:textBlocks[i].source || '', updates: await populateTextContent(textEls[i], textBlocks[i])}});
  const twoEls=byName('Section / Two Column Text & Image');
  for (let i=0; i<Math.min(twoEls.length, (source.twoColumn||[]).length); i++) pageReport.updates.push({{component:'Section / Two Column Text & Image', order:i+1, heading:source.twoColumn[i].heading, updates: await populateTwoColumn(twoEls[i], source.twoColumn[i], i)}});
  const dlEls=byName('Section / Downloads');
  for (let i=0; i<Math.min(dlEls.length, (source.downloads||[]).length); i++) pageReport.updates.push({{component:'Section / Downloads', order:i+1, heading:source.downloads[i].heading, fileCount:source.downloads[i].files?.length||0, updates: await populateDownloads(dlEls[i], source.downloads[i])}});
  const nextEls=byName('Section / Next Pages');
  for (let i=0; i<nextEls.length; i++) pageReport.updates.push({{component:'Section / Next Pages', order:i+1, updates:[['hide', await setBooleanByLabel(nextEls[i], [/hide section/, /^hide$/], false)]]}});
  report.push(pageReport);
}}
await webflow.notify({{ type:'Info', message:`KRB About Us text population completed for ${{report.filter(r=>r.updates).length}} pages` }});
return {{ ok:true, reportJson: JSON.stringify(report) }};
"""
Path('/Users/iggy/.hermes/profiles/ignite_team/outbound/krb-about-us-populate-text-content.js').write_text(script)
print('wrote /Users/iggy/.hermes/profiles/ignite_team/outbound/krb-about-us-populate-text-content.js', len(script))
