// Deterministic score computation. Gates are the LEAD AUDITOR's reconciled
// judgment (G3 = requested+persisted+refresh-preserved; enforcement=finding;
// shadow-explicit-tag does NOT fail G5 dry-run clause; G6=0 everywhere — no live proof).
const W = { 1: 3, 2: 2, 3: 1 };

// [tier, gate, label]
const caps = {
  meta: [
    // Tier1
    [1,5,"campaign budget update"],[1,5,"ad set budget update"],[1,5,"campaign status"],[1,5,"ad set status"],[1,5,"ad status"],[1,5,"bid strategy change"],[1,5,"bid cap/cost cap set"],[1,0,"CBO toggle"],[1,0,"schedule/dayparting"],[1,0,"spend cap set"],
    // Tier2
    [2,5,"campaign create"],[2,5,"ad set create"],[2,5,"ad create"],[2,5,"targeting edit"],[2,4,"placement edit (create-time only)"],[2,4,"opt-goal/conv-event change (create-time only)"],[2,4,"attribution setting (create-time only)"],[2,5,"creative image upload"],[2,4,"video upload (base64-vs-multipart contract defect)"],[2,0,"thumbnail (field, not a mutation)"],[2,5,"ad creative create"],[2,4,"asset feed/dynamic creative (DPA/carousel only, no asset_feed_spec)"],[2,5,"duplicate campaign/ad set"],[2,5,"Advantage+ shopping (ASC) create"],
    // Tier3
    [3,5,"custom audience create/delete"],[3,5,"customer list upload w/ hashing"],[3,5,"lookalike create"],[3,0,"audience share across accounts"],[3,5,"pixel create"],[3,5,"CAPI server event send w/ dedup"],[3,5,"dataset/event set config (real=createPixel; provisionCapi=DB-masquerade)"],[3,5,"product catalog create"],[3,5,"product set create"],[3,0,"catalog feed schedule (DB-flag masquerade)"],[3,5,"DPA campaign wiring"],[3,0,"Ad Rules API create"],[3,0,"naming convention enforce (DB, not platform write)"],[3,0,"UTM/url tag write (link-build, not mutation)"],[3,0,"ad label create"],[3,0,"comment hide/delete/reply"],[3,0,"page post boost"],[3,0,"business asset assignment"],
  ],
  google: [
    // Tier1
    [1,5,"campaign budget mutate"],[1,5,"campaign status mutate"],[1,5,"ad group status mutate"],[1,5,"bidding strategy change"],[1,5,"target value set"],[1,1,"ad status mutate (only bundled in editRsa; no standalone -> fails G2)"],[1,5,"keyword status mutate (bid-update missing)"],[1,0,"campaign end date/schedule mutate"],
    // Tier2
    [2,5,"campaign create"],[2,5,"ad group create"],[2,5,"RSA create w/ pinning"],[2,5,"keyword add"],[2,5,"negative keyword add (campaign+adgroup; no shared-list)"],[2,0,"shared negative list create+attach"],[2,5,"audience signal attach"],[2,5,"PMax asset group create (no update)"],[2,0,"listing group/product partition tree edit"],[2,0,"ad extension (asset) create+link"],[2,0,"geo & language targeting mutate"],[2,0,"device bid modifier"],[2,4,"drafts & experiments create (experiments only, self-flagged unverified; no drafts)"],
    // Tier3
    [3,5,"conversion action create"],[3,5,"conversion action update"],[3,0,"offline conversion upload/enhanced conversions"],[3,4,"customer match list create+upload (untyped cast, self-flagged unverified)"],[3,0,"conversion value rules"],[3,0,"recommendations apply/dismiss"],[3,0,"label create+apply"],[3,0,"portfolio bid strategy create"],[3,1,"campaign-level exclusions (only neg-kw)"],[3,0,"budget order/account edits"],
  ],
  shopify: [
    // Tier1 (staleness 2024-01 flagged in staleness lens; kept G5, see report)
    [1,5,"product price/variant update (REST-product deprecation risk)"],[1,5,"inventory level set"],[1,5,"product publish/unpublish (REST-product deprecation risk)"],[1,5,"discount code create/deactivate"],[1,0,"automatic discount create (needs GraphQL; none)"],
    // Tier2
    [2,0,"product create/update (only status-flip)"],[2,0,"variant create"],[2,5,"collection create + product assignment"],[2,0,"metafield write"],[2,0,"draft order create"],[2,0,"customer tag write"],[2,5,"marketing activity create"],
    // Tier3
    [3,0,"webhook subscribe/unsubscribe"],[3,0,"script tag/app embed write"],[3,0,"price rule update"],[3,0,"catalog publication write"],[3,0,"translation/SEO field write"],[3,0,"ACP/agentic-commerce feed writes"],
  ],
  klaviyo: [
    // Tier1
    [1,0,"profile create/update"],[1,0,"list subscribe/unsubscribe"],[1,0,"suppression add/remove"],
    // Tier2
    [2,5,"segment create/update"],[2,0,"campaign create"],[2,3,"campaign send/schedule (invented jobs/send endpoint -> fails G4)"],[2,0,"template create (update only)"],[2,0,"flow create/update (status-change only)"],[2,5,"flow status change"],
    // Tier3
    [3,0,"metric/event push"],[3,0,"coupon code write"],[3,0,"back-in-stock subscribe"],[3,0,"tag write"],[3,0,"webhook config"],
  ],
  tiktok: [
    // Tier1 (G3 marginal-pass via BC-config; rubber-stamp readback = finding; cap G4, fail G5 on preState={}/fail-open cap)
    [1,4,"campaign/ad group budget update (rollback broken: preState={})"],[1,4,"status change campaign/adgroup/ad"],[1,4,"bid update"],
    // Tier2
    [2,4,"campaign create"],[2,4,"ad group create w/ targeting"],[2,4,"ad create"],[2,4,"video/image upload (no idempotency -> dupes)"],[2,0,"identity create (DB read only)"],
    // Tier3
    [3,0,"custom audience create+upload"],[3,0,"lookalike create"],[3,0,"pixel create"],[3,0,"events API server event send"],[3,0,"catalog & DPA wiring"],
  ],
  ga4: [
    // Tier1
    [1,4,"key event/conversion event create"],[1,4,"custom dimension create"],[1,0,"custom metric create"],
    // Tier2
    [2,4,"audience create"],[2,0,"data stream create/update"],[2,0,"measurement protocol event send"],[2,0,"Google Ads link create"],[2,0,"enhanced measurement settings"],
    // Tier3
    [3,0,"property settings"],[3,0,"data retention"],[3,0,"attribution settings"],[3,0,"BigQuery link"],
  ],
};

function scores(rows) {
  const total = rows.reduce((s,[t])=>s+W[t],0);
  const strict = rows.filter(([,g])=>g>=6).reduce((s,[t])=>s+W[t],0)/total;
  const plausible = rows.filter(([,g])=>g>=4).reduce((s,[t])=>s+W[t],0)/total;
  const maturity = rows.reduce((s,[t,g])=>s+W[t]*(g/6),0)/total;
  return { total, strict, plausible, maturity, n: rows.length };
}
function hist(rows){ const h={0:0,1:0,2:0,3:0,4:0,5:0,6:0}; rows.forEach(([,g])=>h[g]++); return h; }
function tier1(rows){ return rows.filter(([t])=>t===1); }

const all = Object.values(caps).flat();
const pct = x => (x*100).toFixed(1)+"%";

console.log("=== PER PLATFORM ===");
for (const [p, rows] of Object.entries(caps)) {
  const s = scores(rows);
  const t1 = scores(tier1(rows));
  console.log(`${p.padEnd(8)} n=${s.n}  Strict=${pct(s.strict)}  Plausible(G4+)=${pct(s.plausible)}  Maturity=${pct(s.maturity)}  | Tier1: Plausible=${pct(t1.plausible)} Maturity=${pct(t1.maturity)}  hist=${JSON.stringify(hist(rows))}`);
}
console.log("\n=== OVERALL (unweighted union of canonical caps) ===");
const S = scores(all);
console.log(`n=${S.n} totalWeight=${S.total}  Strict=${pct(S.strict)}  Plausible(G4+)=${pct(S.plausible)}  Maturity=${pct(S.maturity)}`);
console.log("histogram:", JSON.stringify(hist(all)));
console.log("\n=== TIER-1 ONLY (money-moving, overall) ===");
const T1 = scores(tier1(all));
console.log(`n=${T1.n} totalWeight=${T1.total}  Strict=${pct(T1.strict)}  Plausible(G4+)=${pct(T1.plausible)}  Maturity=${pct(T1.maturity)}`);
console.log("Tier1 histogram:", JSON.stringify(hist(tier1(all))));

// Business-day weighted (Meta40/Google25/Shopify10/Klaviyo10/GA4-10/TikTok5) for Plausible
const bizW = { meta:0.40, google:0.25, shopify:0.10, klaviyo:0.10, ga4:0.10, tiktok:0.05 };
let bizPlaus=0, bizMat=0;
for (const [p,rows] of Object.entries(caps)){ const s=scores(rows); bizPlaus+=bizW[p]*s.plausible; bizMat+=bizW[p]*s.maturity; }
console.log(`\n=== BUSINESS-DAY-WEIGHTED === Plausible=${pct(bizPlaus)}  Maturity=${pct(bizMat)}  Strict=0.0%`);
