/* Live pricing reports and configuration. Scoped API saves never write a retail price. */
(function () {
  const {useState,useEffect,useMemo} = React;
  const money = c => c == null ? "—" : "$" + (c/100).toLocaleString("en-AU",{minimumFractionDigits:2,maximumFractionDigits:2});
  const percent = n => n == null ? "—" : Number(n).toFixed(1) + "%";
  const signed = c => c == null ? "—" : (c>0 ? "+" : c<0 ? "−" : "") + money(Math.abs(c));
  const date = v => v ? new Date(v).toLocaleDateString("en-AU",{day:"numeric",month:"short",year:"numeric"}) : "Never observed";
  const clone = v => JSON.parse(JSON.stringify(v));
  function useLoad(loader,deps=[]) {
    const [data,setData]=useState(null),[error,setError]=useState(null),[busy,setBusy]=useState(true),[reload,setReload]=useState(0);
    useEffect(()=>{const refresh=()=>setReload(k=>k+1);window.addEventListener("office-pricing-refresh",refresh);return()=>window.removeEventListener("office-pricing-refresh",refresh);},[]);
    useEffect(()=>{let alive=true;setBusy(true);setError(null);loader().then(d=>{if(alive)setData(d);}).catch(e=>{if(alive)setError(e.message||"Couldn't load pricing data.");}).finally(()=>{if(alive)setBusy(false);});return()=>{alive=false;};},[reload,...deps]);
    return {data,setData,error,setError,busy,refresh:()=>setReload(k=>k+1)};
  }
  function Message({error,children}) {return <div className={"off-pricing-note"+(error?" off-pricing-error":"")} role={error?"alert":"status"}>{children}</div>;}
  function Frame({children,actions}) {return <div className="off-screen"><window.CompetitorPricingHeader>{actions}</window.CompetitorPricingHeader><div className="off-screen-body off-pricing-body">{children}</div></div>;}
  function LoadState({r}) {return <>{r.error&&<Message error>{r.error}</Message>}{r.busy&&<Message>Loading live pricing data…</Message>}</>;}
  function useOpen(from) {const {setScreen,setScreenState}=window.useOffice();return productId=>{setScreenState(st=>({...st,priceProductId:productId,priceFrom:from,priceMethod:undefined,priceStrategy:undefined}));setScreen("priceDetail");};}
  function Pager({page,setPage,total,size=50}) {return total>size?<div className="off-pricing-pagination"><span>{page*size+1}–{Math.min((page+1)*size,total)} of {total}</span><button className="btn btn--ghost btn--sm" disabled={!page} onClick={()=>setPage(p=>p-1)}>Previous</button><button className="btn btn--ghost btn--sm" disabled={(page+1)*size>=total} onClick={()=>setPage(p=>p+1)}>Next</button></div>:null;}
  function RuleList({config}) {
    const rules=config ? [{id:"default",name:"Everything",scope:"group",methodId:config.defaultMethodId,enabled:true},...(config.rules||[])] : [];
    return <div className="off-pricing-rule-list">{rules.map(r=><div className="off-pricing-rule" key={r.id}><span className="off-pricing-badge">{r.id==="default"?"Group default":r.scope}</span><div><b>{r.name||r.value||"Everything"}</b><span className="off-pricing-small">{r.reason||(!r.enabled?"Disabled":"")}</span></div><span>{config.methods.find(m=>m.id===r.methodId)?.name||r.methodId}</span></div>)}</div>;
  }
  function Breakdown() {
    const r=useLoad(()=>Promise.all([window.OfficeAPI.pricingBreakdown(),window.OfficeAPI.pricingConfig()]).then(([report,config])=>({report,config})));
    const [grouping,setGrouping]=useState("categories"),[selected,setSelected]=useState(null),[page,setPage]=useState(0);
    const {setScreen,setScreenState}=window.useOffice(),open=useOpen("pricingBreakdown"),Kpi=window.KpiCard;
    const d=r.data?.report,cfg=r.data?.config,s=d?.summary,groups=d?.[grouping]||[];
    const toggle=id=>{setSelected(current=>current===id?null:id);setPage(0);};
    const addRule=()=>{setScreenState(st=>({...st,priceEditRule:true}));setScreen("pricingMethods");};
    const columns='minmax(180px,1.5fr) 118px 76px 110px 96px 104px 110px minmax(160px,1fr)';
    return <Frame actions={<button className="btn btn--secondary btn--sm" disabled={r.busy} onClick={r.refresh}><window.OIcon name="refresh"/> Recompute</button>}>
      <LoadState r={r}/>{d&&<>
        <div className="off-pricing-kpis"><Kpi label="Front-of-shop GP" value={percent(s.currentMarginPct)} delta={{text:money(s.currentGpCents)+" · one unit of each costed line"}}/><Kpi label="Lines the engine would move" value={s.rises+s.falls} delta={{text:"Across "+groups.length+" "+grouping}}/><Kpi label="Rules in force" value={cfg.rules.filter(r=>r.enabled).length+1} delta={{text:"Most specific assignment wins"}}/><Kpi label="Lines locked" value={s.lockedCount} delta={{text:"Held at their recorded lock price"}}/></div>
        <div className="pc-note"><window.OIcon name="info" width={15} height={15} style={{flexShrink:0,marginTop:1}}/><div>A rule set at group level covers everything. Anything set at category, brand or line level overrides it, and the most specific assignment always wins. The detail screen names which one governed the price and which ones lost.</div></div>
        <window.PricingScopeDisclosure>{d.basisNote}<p>Sales: {date(d.period?.from)} to {date(d.period?.to)}. Margin comparisons use the same costed unit basket.</p></window.PricingScopeDisclosure>
        <div className="off-table">
          <div className="off-table-toolbar">
            <div className="off-filters">{[["categories","By category"],["brands","By brand"]].map(([key,label])=><button key={key} className={"off-filter"+(grouping===key?" off-filter--active":"")} aria-pressed={grouping===key} onClick={()=>{setGrouping(key);setSelected(null);setPage(0);}}>{label}</button>)}</div>
            <div style={{marginLeft:"auto",fontSize:12,color:"var(--text-muted)"}}>Click a row to see the lines inside it</div>
          </div>
          <div className="off-pricing-table-wrap"><div className="off-grid" style={{gridTemplateColumns:columns,minWidth:1054}}>
            {[grouping==="categories"?"Category":"Brand","KVIs","Lines","Sales ex-GST","Margin","If repriced","Moves","Method in force"].map((label,i)=><div className="off-grid-headcell" key={label} style={i>=2&&i<=6?{justifyContent:"flex-end"}:undefined}>{label}</div>)}
            {groups.map(g=>{
              const expanded=selected===g.id;
              return <React.Fragment key={g.id}>
                <div className={"off-grid-row"+(expanded?" pc-row--open":"")} onClick={()=>toggle(g.id)} style={{cursor:"pointer"}}>
                  <div className="off-grid-cell"><button className="off-pricing-text-button" aria-expanded={expanded} aria-label={"Show lines in "+g.name} onClick={e=>{e.stopPropagation();toggle(g.id);}}><window.OIcon name={expanded?"chevron-down":"chevron-right"} width={13} height={13} style={{color:"var(--text-subtle)",marginRight:6}}/>{g.name||"Unassigned"}</button></div>
                  <div className="off-grid-cell">{g.kviCount?g.kviCount+" of "+g.lines:<span className="off-muted">none</span>}</div>
                  <div className="off-grid-cell off-cell-num">{g.lines}</div>
                  <div className="off-grid-cell off-cell-num">{money(g.salesExGstCents)}</div>
                  <div className="off-grid-cell off-cell-num off-cell-strong">{percent(g.currentMarginPct)}</div>
                  <div className="off-grid-cell off-cell-num"><span className={g.recommendedMarginPct!=null&&g.currentMarginPct!=null?(g.recommendedMarginPct>g.currentMarginPct?"pc-down":g.recommendedMarginPct<g.currentMarginPct?"pc-up":""):""}>{percent(g.recommendedMarginPct)}</span></div>
                  <div className="off-grid-cell off-cell-num">{g.rises+g.falls}<div className="off-muted" style={{fontSize:11}}>{g.rises} rise · {g.falls} fall</div></div>
                  <div className="off-grid-cell" style={{whiteSpace:"normal"}}>{g.methods.map(m=><span className="pc-mchip" key={m.id}>{m.name}</span>)}</div>
                </div>
                {expanded&&<>
                  <div className="off-grid-row pc-subhead">{["Product","Rule from","Cost","Now inc-GST","Margin","Recommended","Move","Method"].map((label,i)=><div key={label} className="off-grid-cell" style={i===0?{paddingLeft:26}:i>=2&&i<=6?{textAlign:"right"}:undefined}>{label}</div>)}</div>
                  {g.products.slice(page*50,(page+1)*50).map(p=>{
                    const rule=cfg.rules.find(rule=>rule.id===p.ruleId);
                    const cost=p.currentNetCents!=null&&p.currentGpCents!=null?p.currentNetCents-p.currentGpCents:p.recommendedNetCents!=null&&p.recommendedGpCents!=null?p.recommendedNetCents-p.recommendedGpCents:null;
                    const scope=rule?.scope||"group",labels={group:"Group default",category:"Category",brand:"Brand",product:"This line"},tones={group:"flat",category:"brand",brand:"sched",product:"hold"};
                    return <div key={p.productId} className="off-grid-row pc-subrow" onClick={()=>open(p.productId)} style={{cursor:"pointer"}}>
                      <div className="off-grid-cell" style={{whiteSpace:"normal",paddingLeft:26}}><button className="off-pricing-text-button" style={{fontWeight:400}} onClick={e=>{e.stopPropagation();open(p.productId);}}>{p.name}</button><div className="off-cell-mono off-muted" style={{fontSize:11}}>{p.sku||"No SKU"}{p.kvi?" · KVI":""}</div></div>
                      <div className="off-grid-cell" style={{whiteSpace:"normal"}}><span className={"pc-why pc-why--"+tones[scope]}>{labels[scope]}</span>{p.locked&&<div style={{fontSize:11,color:"var(--hold-text)",marginTop:3}}>Locked</div>}</div>
                      <div className="off-grid-cell off-cell-num off-muted" style={{fontSize:12}}>{money(cost)}</div>
                      <div className="off-grid-cell off-cell-num">{money(p.currentCents)}</div>
                      <div className="off-grid-cell off-cell-num">{percent(p.marginNowPct)}</div>
                      <div className="off-grid-cell off-cell-num"><span className={p.locked?"pc-struck":"off-cell-strong"}>{money(p.recommendedCents)}</span></div>
                      <div className="off-grid-cell off-cell-num">{p.locked?<span className="pc-lock"><window.OIcon name="lock" width={10} height={10}/> held</span>:<span className={p.deltaCents>0?"pc-up":p.deltaCents<0?"pc-down":"off-muted"}>{signed(p.deltaCents)}</span>}</div>
                      <div className="off-grid-cell" style={{whiteSpace:"normal",fontSize:11.5,color:"var(--text-muted)",lineHeight:1.4}}>{p.methodName}{p.warn&&<div style={{fontSize:11,color:"var(--text-subtle)"}}>{p.warn}</div>}</div>
                    </div>;
                  })}
                  <div style={{gridColumn:"1 / -1",display:"flex",alignItems:"center",justifyContent:"space-between",gap:12,padding:"0 12px",background:"var(--bg-subtle)",borderBottom:"1px solid var(--border)"}}><button className="btn btn--ghost btn--sm" onClick={()=>setSelected(null)}>Close lines</button><Pager page={page} setPage={setPage} total={g.products.length}/></div>
                </>}
              </React.Fragment>;
            })}
          </div></div>
          {!groups.length&&<div className="off-pricing-empty">No eligible front-of-shop lines in this scope.</div>}
        </div>
        <section className="off-card pc-card" style={{marginTop:16}}><div className="off-pricing-section-head"><div><h3 className="pc-card-h">Rules in force</h3><div className="pc-card-s">Ordered from general to specific. The working names the rule that wins.</div></div><button className="btn btn--secondary btn--sm" onClick={addRule}><window.OIcon name="plus"/> Add a rule</button></div><RuleList config={cfg}/></section>
      </>}
    </Frame>;
  }
  function Checks() {
    const r=useLoad(()=>window.OfficeAPI.pricingChecks()),d=r.data,s=d?.summary,open=useOpen("pricingChecks"),{setScreen}=window.useOffice(),Kpi=window.KpiCard;
    return <Frame actions={<button className="btn btn--secondary btn--sm" disabled={r.busy} onClick={r.refresh}><window.OIcon name="refresh"/> Recheck</button>}><LoadState r={r}/>{d&&<>
      <div className="off-pricing-kpis"><Kpi label="Broken pack ladders" value={s.brokenPackCount==null?"—":s.brokenPackCount} delta={{text:s.packGroupCount+" confirmed pack groups inspected"}}/><Kpi label="Chasing a promo price" value={s.promoCount} delta={{text:"Cheapest observation is an inferred sale"}}/><Kpi label="Lines priced differently by store" value={s.crossStoreLines==null?"—":s.crossStoreLines} delta={{text:d.crossStore.available?"Within authorised stores":"Store-specific shelf prices unavailable"}}/><Kpi label="Widest store spread" value={money(s.widestSpreadCents)} delta={{text:d.crossStore.available?"Current observed store spread":"Organisation-wide shelf prices"}}/></div>
      <section className="off-card pc-card" style={{marginBottom:16}}>
        <div className="pc-card-h">Pack ladders</div>
        <div className="pc-card-s">A bigger pack should cost less per unit. Confirmed pack units establish the comparison; a price ladder also needs a recorded selling price for each pack.</div>
        {d.packLadders.length?<div className="pc-ladders">{d.packLadders.map(p=><div className={"pc-ladder"+(p.broken?" is-broken":"")} key={p.productId}>
          <div className="pc-ladder-h"><button className="off-pricing-text-button pc-ladder-n" onClick={()=>open(p.productId)}>{p.name}</button><span className={"pc-why pc-why--"+(p.broken?"void":"flat")}>{p.broken?"Broken":"Confirmed units"}</span></div>
          <div className="pc-lrow">{p.levels.map((l,i)=><div className="pc-lp" key={i}><div className="pc-lp-s">{l.name}</div><div className="pc-lp-p">{l.unitsInBase==null?"—":l.unitsInBase}</div><div className="pc-lp-u">base units{l.isSell?" · sold at till":""}</div>{l.priceCents!=null&&<div className="pc-lp-p">{money(l.priceCents)}</div>}</div>)}</div>
          <div className="pc-card-s" style={{marginTop:9}}>{p.note}</div>
        </div>)}</div>:<div className="off-pricing-empty">No confirmed pack group has enough comparable selling-price evidence to assess a price ladder.</div>}
        <div className="pc-submit"><div className="pc-submit-s">Only confirmed levels within the same product are grouped. Missing pack prices remain unassessed.</div><button className="btn btn--secondary btn--sm" onClick={()=>setScreen("packReview")}>Open pack review</button></div>
      </section>
      <div className="pc-grid">
        <section className="off-card pc-card">
          <div className="pc-card-h">You may be chasing a promotion</div>
          <div className="pc-card-s">Where the cheapest observed price is inferred to be on sale, matching it may follow a temporary price. No end date or return price is assumed.</div>
          {d.promos.length?<div className="pc-cycles">{d.promos.map((p,i)=><div className="pc-cycle" key={p.productId+":"+i}>
            <div className="pc-cycle-h"><button className="off-pricing-text-button pc-cycle-n" onClick={()=>open(p.productId)}>{p.name}</button><span className="pc-why pc-why--hold">Inferred sale</span></div>
            <div className="pc-cycle-p"><div><span>Observed</span><strong>{money(p.priceCents)}</strong></div><div><span>Retailer</span><strong style={{fontFamily:"inherit",fontSize:12}}>{p.retailerName}</strong></div><div><span>Last seen</span><strong style={{fontSize:12}}>{date(p.observedAt)}</strong></div><div><span>Return price</span><strong>—</strong></div></div>
            <div className="pc-cycle-d">{p.evidence}</div>
          </div>)}</div>:<div className="off-pricing-empty">No cheapest-observed prices currently meet the sale-history threshold.</div>}
        </section>
        <section className="off-card pc-card">
          <div className="pc-card-h">Same line, different price by store</div>
          <div className="pc-card-s">Independent shelf prices at each store are needed to distinguish deliberate local pricing from drift.</div>
          <div className="pc-note" style={{marginTop:11,marginBottom:0}}><window.OIcon name="info" width={15} height={15} style={{flexShrink:0,marginTop:1}}/><div>{d.crossStore.reason}</div></div>
          <div className="pc-submit"><div className="pc-submit-s">Pricing assignments can vary by site; recorded shelf prices currently apply across the organisation.</div><button className="btn btn--secondary btn--sm" onClick={()=>setScreen("pricingMethods")}>Review policy</button></div>
        </section>
      </div>
      {d.costIssues.length>0&&<details className="off-pricing-disclosure"><summary>{d.costIssues.length} unit-cost checks</summary><div className="off-pricing-check-list">{d.costIssues.map(p=><div key={p.productId}><button className="off-pricing-text-button" onClick={()=>open(p.productId)}>{p.name}</button><span className="off-pricing-small">{p.note}</span></div>)}</div></details>}
    </>}</Frame>;
  }
  function Register() {
    const [search,setSearch]=useState(""),[query,setQuery]=useState(""),[page,setPage]=useState(0),[changes,setChanges]=useState({}),[editRevision,setEditRevision]=useState(null),[saving,setSaving]=useState(false),[notice,setNotice]=useState(null);
    useEffect(()=>{const t=setTimeout(()=>{setQuery(search.trim());setPage(0);},250);return()=>clearTimeout(t);},[search]);
    const r=useLoad(()=>window.OfficeAPI.pricingRegister({search:query,limit:50,offset:page*50}),[query,page]),d=r.data,Kpi=window.KpiCard;
    const change=(row,key,value)=>{setNotice(null);if(!Object.keys(changes).length)setEditRevision(d.revision);setChanges(prev=>({...prev,[row.productId]:{productId:row.productId,enabled:row.enabled,reason:row.reason||"Manually selected",cadenceDays:row.cadenceDays||14,...prev[row.productId],[key]:value}}));};
    const save=async()=>{setSaving(true);r.setError(null);try{await window.OfficeAPI.savePricingRegister({revision:editRevision,kviOverrides:Object.values(changes)});setChanges({});setEditRevision(null);setNotice("Register saved. Future recommendations use these KVI selections and recheck dates.");r.refresh();}catch(e){r.setError(e.message||"Couldn't save the register.");}finally{setSaving(false);}};
    return <Frame actions={<button className="btn btn--secondary btn--sm" disabled={r.busy||saving||Object.keys(changes).length>0} onClick={r.refresh}><window.OIcon name="refresh"/> Refresh</button>}><LoadState r={r}/>{notice&&<Message>{notice}</Message>}{d&&<>
      <div className="off-pricing-kpis"><window.KpiCard label="On the register" value={d.enabledCount} delta={{text:"Saved KVI selections for this scope"}}/><Kpi label="Observed this week" value={d.rechecked7DaysCount??"—"} delta={{text:"Actual competitor observations in the last 7 days"}}/><Kpi label="Added last 90 days" value={d.added90DaysCount??"—"} delta={{text:"Manually added entries with a recorded date"}}/><Kpi label="Basket share of sales" value={percent(d.salesSharePct)} delta={{text:"Registered lines · last 30 days of retail sales"}}/></div>
      <div className="pc-note"><window.OIcon name="info"/><span>Registered lines get the KVI second pass on every recommendation. Their cadence identifies when fresh competitor evidence is due.</span></div>
      <window.PricingScopeDisclosure>{d.scopeNote}<p>Last observed is a captured competitor price, not a record of staff approval. The cadence tracks due dates; it does not add a new collection service.</p></window.PricingScopeDisclosure>
      <div className="off-card off-pricing-data-card"><div className="off-pricing-toolbar"><h3 className="off-card-title">Known value items</h3><input className="off-input off-pricing-search" aria-label="Search register products" placeholder="Product, SKU or brand…" value={search} onChange={e=>setSearch(e.target.value)}/></div>
        <div className="off-pricing-table-wrap"><table className="off-pricing-table off-pricing-register-table"><colgroup><col style={{width:56}}/><col style={{width:"calc(61.538462% - 262.15385px)"}}/><col style={{width:130}}/><col/><col style={{width:130}}/><col style={{width:110}}/></colgroup><thead><tr><th>KVI</th><th>Product</th><th>Category</th><th>Why it's on the list</th><th>Recheck</th><th>Last observed</th></tr></thead><tbody>{d.rows.map(row=>{const p={...row,...changes[row.productId]};return <tr key={p.productId} className={!p.enabled?"off-pricing-inactive":""}>
          <td><button className="off-pricing-switch" role="switch" aria-checked={p.enabled} aria-label={"KVI for "+p.name} disabled={saving} onClick={()=>change(row,"enabled",!p.enabled)}><span/></button></td>
          <td className="off-pricing-product"><b>{p.name}</b><span className="off-pricing-small">{p.sku||"No SKU"}</span></td><td>{p.category||"Unassigned"}</td>
          <td><input className="off-input off-pricing-reason-input" aria-label={"KVI reason for "+p.name} value={p.reason||""} placeholder="Why customers compare this line" disabled={!p.enabled||saving} onChange={e=>change(row,"reason",e.target.value)}/></td>
          <td><select className="off-input" aria-label={"Recheck cadence for "+p.name} value={p.cadenceDays||14} disabled={!p.enabled||saving} onChange={e=>change(row,"cadenceDays",Number(e.target.value))}><option value={7}>Weekly</option><option value={14}>Fortnightly</option><option value={30}>Monthly</option></select></td><td>{date(p.lastObservedAt)}{p.enabled&&p.due&&<span className="off-pricing-small off-pricing-up">Due for recheck</span>}</td>
        </tr>;})}</tbody></table></div>{!d.rows.length&&<div className="off-pricing-empty">No eligible products match this search.</div>}
      </div><Pager page={page} setPage={setPage} total={d.total}/>
      <div className="off-pricing-save"><span>Turning a line on enables its KVI second pass after saving. {Object.keys(changes).length} unsaved changes.</span><button className="btn btn--primary btn--sm" disabled={saving||r.busy||!Object.keys(changes).length} onClick={save}>{saving?"Saving…":"Save register"}</button></div>
    </>}</Frame>;
  }
  function Methods() {
    const r=useLoad(()=>window.OfficeAPI.pricingConfig());
    const reports=useLoad(()=>Promise.all([window.OfficeAPI.pricingBreakdown(),window.OfficeAPI.pricingChecks()]).then(([breakdown,checks])=>({breakdown,checks})));
    const [draft,setDraft]=useState(null),[methodId,setMethodId]=useState("hlprice"),[dirty,setDirty]=useState(false),[saving,setSaving]=useState(false),[notice,setNotice]=useState(null);
    const {screenState={},setScreenState}=window.useOffice();
    const [showRule,setShowRule]=useState(!!screenState.priceEditRule),[ruleScope,setRuleScope]=useState("category"),[ruleValue,setRuleValue]=useState(""),[ruleName,setRuleName]=useState(""),[ruleMethod,setRuleMethod]=useState("hlprice"),[picked,setPicked]=useState(null),[lock,setLock]=useState(false),[lockPrice,setLockPrice]=useState(""),[ruleError,setRuleError]=useState(null);
    useEffect(()=>{if(r.data&&!dirty){setDraft(clone(r.data));}},[r.data,dirty]);
    useEffect(()=>{if(screenState.priceEditRule)setScreenState(st=>({...st,priceEditRule:false}));},[]);
    const edit=fn=>{setDraft(prev=>{const next=clone(prev);fn(next);return next;});setDirty(true);setNotice(null);};
    const method=draft?.methods.find(m=>m.id===methodId);
    const editMethod=(key,value)=>edit(d=>{d.methods.find(m=>m.id===methodId)[key]=value;});
    const editBand=(i,key,value)=>edit(d=>{d.methods.find(m=>m.id===methodId).bands[i][key]=Math.round(Number(value)*100);});
    const save=async()=>{setSaving(true);r.setError(null);try{const result=await window.OfficeAPI.savePricingConfig(draft);setNotice("Configuration saved. Recommendations will use these rules on the next read. Shelf prices are unchanged.");if(result.methods){r.setData(result);}else{r.refresh();}reports.refresh();setDirty(false);}catch(e){r.setError(e.message||"Couldn't save pricing configuration.");}finally{setSaving(false);}};
    const addRule=()=>{
      const value=ruleScope==="group"?null:ruleScope==="product"?picked?.id:ruleValue.trim();
      if(ruleScope!=="group"&&!value){setRuleError("Choose a target for this rule.");return;}
      if(lock&&ruleScope==="product"&&(!Number.isFinite(Number(lockPrice))||Number(lockPrice)<=0||!ruleName.trim())){setRuleError("A lock needs a positive price and a reason.");return;}
      if(draft.rules.some(r=>r.enabled&&r.scope===ruleScope&&r.value===value)){setRuleError("An enabled rule already targets this level and value. Remove it before adding a replacement.");return;}
      edit(d=>{d.rules.push({id:"rule-"+Date.now().toString(36),name:ruleName.trim()||picked?.name||value||"Everything",scope:ruleScope,value,methodId:ruleMethod,enabled:true});if(lock&&ruleScope==="product"){d.productLocks=(d.productLocks||[]).filter(l=>l.productId!==picked.id).concat({productId:picked.id,priceCents:Math.round(Number(lockPrice)*100),reason:ruleName.trim()});}});
      setShowRule(false);setRuleError(null);setRuleName("");setRuleValue("");setPicked(null);setLock(false);
    };
    return <Frame actions={<button className="btn btn--secondary btn--sm" disabled={r.busy||saving||!dirty} onClick={save}>{saving?"Saving…":"Save changes"}</button>}><LoadState r={r}/>{notice&&<Message>{notice}</Message>}{draft&&method&&<fieldset className="off-pricing-form-shell" disabled={saving}>
      <div className="pc-note"><window.OIcon name="info"/><span>Three methods run side by side: anchored to one retailer, capped against the cheapest, or driven by category margin.</span></div>
      <div className="off-pricing-methods">{draft.methods.map(m=><button key={m.id} className="off-pricing-method" aria-pressed={methodId===m.id} onClick={()=>setMethodId(m.id)}><div><b>{m.name}</b><span className={"pc-method-k pc-method-k--"+m.kind}>{m.kind}</span></div><div className="pc-method-s">{draft.defaultMethodId===m.id?"Default for this scope":draft.rules.filter(r=>r.enabled&&r.methodId===m.id).length+" active assignments"}</div><span className="off-pricing-small">{m.blurb}</span></button>)}</div>
      <div className="off-pricing-method-layout"><section className="off-card"><div className="off-pricing-section-head"><div><h3 className="off-card-title">{method.name} · configuration</h3><span className="off-pricing-small">{method.blurb}</span></div></div>
        {method.kind==="reference"&&<div className="off-pricing-table-wrap"><table className="off-pricing-table off-pricing-band-table"><colgroup><col/><col style={{width:96}}/><col style={{width:110}}/><col style={{width:100}}/></colgroup><thead><tr><th>Reference price band</th><th>Add</th><th>Normal floor</th><th>Sale floor</th></tr></thead><tbody>{method.bands.map((b,i)=><tr key={i}><td>{money(i?method.bands[i-1].max+1:0)}–{b.max==null?"up":money(b.max)}</td>{["add","normal","sale"].map(k=><td key={k}><input className="off-input off-pricing-number" type="number" min="0" step="0.01" aria-label={"Band "+(i+1)+" "+k} value={b[k]/100} onChange={e=>editBand(i,k,e.target.value)}/>{k==="add"&&b.addPct!=null&&<span className="off-pricing-small">or {b.addPct}%</span>}</td>)}</tr>)}</tbody></table></div>}
        {method.kind==="competitor"&&<div className="off-pricing-form-grid"><label>Maximum gap (%)<input className="off-input" type="number" min="0" max="100" step="0.1" value={method.gapPct} onChange={e=>editMethod("gapPct",Number(e.target.value))}/></label><label>Cost-plus floor (%)<input className="off-input" type="number" min="0" max="100" step="0.1" value={method.floorPct} onChange={e=>editMethod("floorPct",Number(e.target.value))}/></label></div>}
        {method.kind==="margin"&&<><label>Add category target<select className="off-input" aria-label="Add category margin target" value="" onChange={e=>{if(e.target.value)editMethod("targets",{...method.targets,[e.target.value]:40});}}><option value="">Choose a category</option>{(reports.data?.breakdown.categories||[]).filter(g=>g.id!=="__uncategorised"&&method.targets?.[g.name]==null).map(g=><option key={g.id} value={g.name}>{g.name}</option>)}</select></label><div className="off-pricing-form-grid">{Object.entries(method.targets||{}).map(([category,target])=><label key={category}>{category} target (%)<input className="off-input" type="number" min="0" max="99" step="0.1" value={target} onChange={e=>editMethod("targets",{...method.targets,[category]:Number(e.target.value)})}/></label>)}</div><label>Competitor ceiling tolerance (%)<input className="off-input" type="number" min="0" max="100" step="0.1" value={method.tolerancePct} onChange={e=>editMethod("tolerancePct",Number(e.target.value))}/></label></>}
        <div className="off-pricing-explainer">{method.kind==="reference"?<><b>Rule 1</b><span>Reference price plus the band’s add.</span><b>Cost floor</b><span>Cost plus the normal or inferred-sale floor, grossed up for taxable products.</span><b>Band recompute</b><span>A floor above its band is recomputed in the band it reaches.</span><b>Higher wins</b><span>The higher of the anchored price and cost floor.</span><b>KVI second pass</b><span>The cheapest watched KVI price plus {money(method.kviAdd)}, checked against its floor.</span></>:method.kind==="competitor"?<><b>Cheapest observed</b><span>The cheapest comparable price across the watched retailers.</span><b>Cost floor</b><span>The cost-plus floor takes priority when the gap cap would breach it.</span></>:<><b>Margin target</b><span>Category target on the ex-GST selling price. Unassigned categories use the method’s fallback.</span><b>Ceiling check</b><span>Prices beyond the competitor tolerance are flagged for review.</span></>}
          <b>Rounding</b><span>Nearest of {(method.rounding||[]).map(n=>"."+String(n).padStart(2,"0")).join(" or ")}, ties up. KVI stance overrides use exact cents.</span>
        </div><div className="off-pricing-save"><span>{draft.updatedAt?"Last saved "+date(draft.updatedAt):"Using the initial method configuration"}</span><button className="btn btn--primary btn--sm" disabled={!dirty||saving} onClick={save}>{saving?"Saving…":"Save method"}</button></div>
      </section><aside className="off-pricing-method-aside"><section className="off-card"><h3 className="off-card-title">Precedence</h3><p className="off-pricing-small">The working records the winning rule and the resulting advice.</p>{[["1","Regulated lines","Excluded from retail advice."],["2","Manual lock","Keep the recorded price until the lock is removed."],["3","Rule assignment","Product, then brand, category and group default."],["4","Method and cost floor","Use the chosen method’s cost protection."],["5","KVI stance","Apply the saved stance to registered lines."],["6","Price ending","Method rounding, or exact cents for a stance."]].map(([n,t,body])=><div className="off-pricing-precedence" key={n}><span>{n}</span><div><b>{t}</b><small>{body}</small></div></div>)}</section><section className="off-card"><h3 className="off-card-title">Capture</h3><p className="off-pricing-small">Comparable prices observed in the last 90 days.</p>{(reports.data?.checks.capture||[]).map(c=><div className="off-pricing-rule" key={c.retailerId}><b>{c.name}</b><span>{c.productCount} products</span><span className="off-pricing-small">{date(c.lastObservedAt)}</span></div>)}{!reports.data?.checks.capture?.length&&<span className="off-pricing-small">No comparable retailer capture evidence available.</span>}</section></aside></div>
      <section className="off-pricing-section"><div className="off-pricing-section-head"><h3>Rules in force</h3><button className="btn btn--secondary btn--sm" onClick={()=>{setShowRule(v=>!v);setRuleError(null);}}><window.OIcon name="plus"/> Add a rule</button></div><label className="off-pricing-default-method">Group default <select className="off-input" value={draft.defaultMethodId} onChange={e=>edit(d=>{d.defaultMethodId=e.target.value;})}>{draft.methods.map(m=><option key={m.id} value={m.id}>{m.name}</option>)}</select></label>
        {showRule&&<div className="off-card off-pricing-rule-form"><h3 className="off-card-title">New rule</h3>{ruleError&&<Message error>{ruleError}</Message>}<div className="off-pricing-form-grid"><label>Level<select className="off-input" value={ruleScope} onChange={e=>{setRuleScope(e.target.value);setRuleValue("");}}><option value="category">Category</option><option value="brand">Brand</option><option value="product">This line</option><option value="group">Group</option></select></label>
          {ruleScope==="category"&&<label>Category<select className="off-input" value={ruleValue} onChange={e=>setRuleValue(e.target.value)}><option value="">Choose category</option>{(reports.data?.breakdown.categories||[]).filter(g=>g.id && g.id!=="__uncategorised").map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select></label>}
          {ruleScope==="brand"&&<label>Brand<select className="off-input" value={ruleValue} onChange={e=>setRuleValue(e.target.value)}><option value="">Choose brand</option>{(reports.data?.breakdown.brands||[]).filter(g=>g.name && g.name!=="Unbranded").map(g=><option key={g.id} value={g.name}>{g.name}</option>)}</select></label>}
          {ruleScope==="product"&&<div><label>Product</label><window.PricingProductPicker value={picked} onPick={p=>{setPicked(p);if(p)setLockPrice(String((p.sellPriceCents||0)/100));}}/></div>}
          <label>Method<select className="off-input" value={ruleMethod} onChange={e=>setRuleMethod(e.target.value)}>{draft.methods.map(m=><option key={m.id} value={m.id}>{m.name}</option>)}</select></label><label>Name / reason<input className="off-input" value={ruleName} onChange={e=>setRuleName(e.target.value)} placeholder="Why this assignment is needed"/></label></div>
          {ruleScope==="product"&&<div className="off-pricing-actions"><label><input type="checkbox" checked={lock} onChange={e=>setLock(e.target.checked)}/> Lock this line’s advisory price</label>{lock&&<label>Lock price ($)<input className="off-input" type="number" min="0.01" step="0.01" value={lockPrice} onChange={e=>setLockPrice(e.target.value)}/></label>}</div>}
          <div className="off-pricing-actions"><button className="btn btn--primary btn--sm" onClick={addRule}>Add to draft</button><button className="btn btn--ghost btn--sm" onClick={()=>setShowRule(false)}>Cancel</button><span className="off-pricing-small">Save changes to put this rule into effect.</span></div>
        </div>}
        <div className="off-pricing-rule-list">{draft.rules.map(rule=><div className="off-pricing-rule" key={rule.id}><span className="off-pricing-badge">{rule.scope}</span><div><b>{rule.name||rule.value||"Everything"}</b><span className="off-pricing-small">{rule.value}</span></div><span>{draft.methods.find(m=>m.id===rule.methodId)?.name}</span><label><input type="checkbox" checked={rule.enabled} onChange={e=>edit(d=>{d.rules.find(x=>x.id===rule.id).enabled=e.target.checked;})}/> Enabled</label><button className="btn btn--ghost btn--sm" aria-label={"Remove rule "+rule.name} onClick={()=>edit(d=>{d.rules=d.rules.filter(x=>x.id!==rule.id);})}>Remove</button></div>)}</div>
        {!!draft.productLocks?.length&&<section className="off-pricing-section"><h3>Manual locks</h3>{draft.productLocks.map(l=><div className="off-pricing-rule" key={l.productId}><span className="off-pricing-badge">Locked</span><div><b>{l.reason}</b><span className="off-pricing-small">{l.productId}</span></div><b>{money(l.priceCents)}</b><button className="btn btn--ghost btn--sm" onClick={()=>edit(d=>{d.productLocks=d.productLocks.filter(x=>x.productId!==l.productId);})}>Remove lock</button></div>)}</section>}
        <div className="off-pricing-save"><span>{dirty?"Unsaved changes":"Configuration is up to date"}</span><button className="btn btn--primary btn--sm" disabled={!dirty||saving} onClick={save}>{saving?"Saving…":"Save changes"}</button></div>
      </section><window.PricingScopeDisclosure>{draft.scopeNote}</window.PricingScopeDisclosure>
    </fieldset>}</Frame>;
  }
  window.OFFICE_SCREENS=Object.assign(window.OFFICE_SCREENS||{},{pricingBreakdown:Breakdown,pricingChecks:Checks,pricingRegister:Register,pricingMethods:Methods});
})();
