// OM Group — Premium Interactive Analytics Charts
// Chart 1: Revenue Trend Bar Chart (Canvas, drill-down Month→Week→Day→Txns)
// Chart 2: Financial Performance Line Chart (SVG, Revenue + GP + NP toggles)

const {
  useState: acSt, useEffect: acEf, useRef: acRef, useMemo: acMemo
} = React;

// ── Easing ────────────────────────────────────────────────────────────────────
function acEaseOutExpo(t) { return t >= 1 ? 1 : 1 - Math.pow(2, -10 * t); }
function acEaseInOutCubic(t) { return t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2,3)/2; }

// ── Constants ─────────────────────────────────────────────────────────────────
const AC_MON = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
const AC_COL = {
  revenue:     { line:'#F97316', area:'rgba(249,115,22,.10)'  },
  grossProfit: { line:'#22C55E', area:'rgba(34,197,94,.08)'   },
  netProfit:   { line:'#3B82F6', area:'rgba(59,130,246,.08)'  },
};

// ── Formatting ────────────────────────────────────────────────────────────────
const acC  = v => window.fmtCur ? window.fmtCur(v) : '₹' + Math.round(v||0).toLocaleString('en-IN');
const acN  = v => {
  const n = Math.round(v||0);
  if (n >= 1e7)  return (n/1e7).toFixed(1)+'Cr';
  if (n >= 1e5)  return (n/1e5).toFixed(1)+'L';
  if (n >= 1e3)  return (n/1e3).toFixed(0)+'K';
  return String(n);
};

// ── Period ranges ─────────────────────────────────────────────────────────────
function acPeriodRange(preset) {
  const now = new Date(), tod = now.toISOString().slice(0,10);
  const yr = now.getFullYear(), mo = now.getMonth();
  const p = n => String(n).padStart(2,'0');
  if (preset === 'month')   return { from:`${yr}-${p(mo+1)}-01`, to:tod };
  if (preset === 'quarter') { const qs=Math.floor(mo/3)*3; return { from:`${yr}-${p(qs+1)}-01`, to:tod }; }
  if (preset === 'prevfy')  { const fys=(mo>=3?yr:yr-1)-1; return { from:`${fys}-04-01`, to:`${fys+1}-03-31` }; }
  // default = FY
  const fys = mo >= 3 ? yr : yr-1;
  return { from:`${fys}-04-01`, to:tod };
}

// ── Data computation ──────────────────────────────────────────────────────────
function acMonthly(companyId, from, to) {
  const allS = (Store && Store.all) ? (Store.all('salesOrders')   || []) : [];
  const allP = (Store && Store.all) ? (Store.all('purchases')     || []) : [];
  const allD = (Store && Store.all) ? (Store.all('dieselRecords') || []) : [];
  const isAll = !companyId || companyId === '';
  const fco  = r => isAll || r.companyId === companyId;
  const fdr  = d => { if(!d) return false; if(from && d<from) return false; if(to && d>to) return false; return true; };
  const map  = {};
  const add  = (m,k,v) => { if(!map[m]) map[m]={m,revenue:0,purchCost:0,dieselCost:0,txCount:0}; map[m][k]+=v; };
  allS.filter(s=>fco(s)&&fdr(s.date)).forEach(s => {
    const m=(s.date||'').slice(0,7); if(m){add(m,'revenue',window.gAmt(s));add(m,'txCount',1);}
  });
  allP.filter(p=>fco(p)&&fdr(p.date)).forEach(p => {
    const m=(p.date||'').slice(0,7); if(m){const c=window.gAmt(p);add(m,'purchCost',c);}
  });
  allD.filter(d=>fco(d)&&fdr(d.periodStart||d.date)).forEach(d => {
    const dt=d.periodStart||d.date||''; const m=dt.slice(0,7);
    if(m) add(m,'dieselCost',parseFloat(d.amount||d.total||0));
  });
  return Object.values(map).sort((a,b)=>a.m.localeCompare(b.m)).map(r=>({
    ...r,
    grossProfit: r.revenue - r.purchCost,
    netProfit:   r.revenue - r.purchCost - r.dieselCost,
    monthLabel:  AC_MON[parseInt(r.m.slice(5,7),10)-1]+' \''+r.m.slice(2,4),
  }));
}

function acWeekly(companyId, month) {
  const allS = (Store&&Store.all)?(Store.all('salesOrders')||[]):[];
  const allP = (Store&&Store.all)?(Store.all('purchases')||[]):[];
  const isAll = !companyId||companyId==='';
  const fco  = r => isAll||r.companyId===companyId;
  const [yr,mo] = month.split('-').map(Number);
  const days = new Date(yr,mo,0).getDate();
  const p = n => String(n).padStart(2,'0');
  const weeks=[]; let ws=1,wk=1;
  while(ws<=days){
    const we=Math.min(ws+6,days), wF=`${month}-${p(ws)}`, wT=`${month}-${p(we)}`;
    const inW=d=>d>=wF&&d<=wT;
    const wS=allS.filter(s=>fco(s)&&inW(s.date||''));
    const wP=allP.filter(p=>fco(p)&&inW(p.date||''));
    const rev=wS.reduce((s,r)=>s+window.gAmt(r),0);
    const cost=wP.reduce((s,p)=>s+window.gAmt(p),0);
    weeks.push({label:`Week ${wk}`,from:wF,to:wT,revenue:rev,purchCost:cost,grossProfit:rev-cost,txCount:wS.length});
    ws+=7;wk++;
  }
  return weeks;
}

function acDaily(companyId, from, to) {
  const allS=(Store&&Store.all)?(Store.all('salesOrders')||[]):[];
  const isAll=!companyId||companyId==='';
  const fco=r=>isAll||r.companyId===companyId;
  const days={};
  allS.filter(s=>fco(s)&&(s.date||'')>=from&&(s.date||'')<=to).forEach(s=>{
    if(!days[s.date]) days[s.date]={date:s.date,revenue:0,txCount:0};
    days[s.date].revenue+=window.gAmt(s); days[s.date].txCount++;
  });
  return Object.values(days).sort((a,b)=>a.date.localeCompare(b.date))
    .map(d=>({...d,dayLabel:d.date.slice(8)}));
}

function acTxns(companyId, date) {
  const allS=(Store&&Store.all)?(Store.all('salesOrders')||[]):[];
  const isAll=!companyId||companyId==='';
  return allS.filter(s=>(isAll||s.companyId===companyId)&&s.date===date);
}

// ── Tooltip ───────────────────────────────────────────────────────────────────
function ACTip({ vis, x, y, data, cRef }) {
  const ref=acRef(null);
  const [pos,setPos]=acSt({l:0,t:0,ok:false});
  acEf(()=>{
    if(!vis||!data||!ref.current||!cRef.current){setPos(p=>({...p,ok:false}));return;}
    const tip=ref.current.getBoundingClientRect(), box=cRef.current.getBoundingClientRect();
    let l=x-tip.width/2, t=y-tip.height-14;
    if(l<4)l=4;
    if(l+tip.width>box.width-4)l=box.width-tip.width-4;
    if(t<4)t=y+16;
    setPos({l,t,ok:true});
  },[vis,x,y,data]);
  return (
    <div ref={ref} style={{
      position:'absolute',left:pos.l,top:pos.t,zIndex:200,
      background:'rgba(255,255,255,.97)',border:'1px solid rgba(228,226,222,.9)',borderRadius:14,
      padding:'12px 16px',minWidth:200,
      boxShadow:'0 8px 32px rgba(0,0,0,.11),0 2px 8px rgba(0,0,0,.06),0 0 0 0.5px rgba(0,0,0,.03)',
      backdropFilter:'blur(12px)',WebkitBackdropFilter:'blur(12px)',
      pointerEvents:'none',fontFamily:'var(--font)',
      opacity:vis&&pos.ok?1:0,
      transform:vis&&pos.ok?'scale(1)':'scale(0.96)',
      transition:'opacity 140ms ease,transform 140ms ease',
    }}>
      {data?.title&&<div style={{fontSize:10,fontWeight:700,color:'var(--txt2)',textTransform:'uppercase',letterSpacing:'.06em',marginBottom:7,paddingBottom:6,borderBottom:'1px solid #F3F4F6'}}>{data.title}</div>}
      {(data?.rows||[]).map((r,i)=>(
        <div key={i} style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:14,marginBottom:i<(data.rows.length-1)?4:0}}>
          <div style={{display:'flex',alignItems:'center',gap:5}}>
            {r.dot&&<span style={{width:6,height:6,borderRadius:'50%',background:r.dot,display:'inline-block',flexShrink:0}}/>}
            <span style={{fontSize:11,color:'var(--txt3)'}}>{r.label}</span>
          </div>
          <span style={{fontSize:11.5,fontWeight:600,color:r.vc||'var(--txt)'}}>{r.value}</span>
        </div>
      ))}
    </div>
  );
}

// ── Period selector ───────────────────────────────────────────────────────────
function ACPeriod({ value, onChange }) {
  const opts=[{id:'month',l:'Month'},{id:'quarter',l:'Quarter'},{id:'fy',l:'FY 25-26'},{id:'prevfy',l:'Prev FY'}];
  return (
    <div style={{display:'flex',gap:2,background:'#F5F4F2',borderRadius:8,padding:3,flexShrink:0}}>
      {opts.map(o=>(
        <button key={o.id} onClick={()=>onChange(o.id)} style={{
          padding:'3px 9px',borderRadius:6,border:'none',cursor:'pointer',
          fontFamily:'var(--font)',fontSize:11,
          fontWeight:value===o.id?700:500,
          background:value===o.id?'#fff':'transparent',
          color:value===o.id?'var(--or)':'var(--txt2)',
          boxShadow:value===o.id?'0 1px 3px rgba(0,0,0,.08)':'none',
          transition:'all .15s',
        }}>{o.l}</button>
      ))}
    </div>
  );
}

// ── Breadcrumb ────────────────────────────────────────────────────────────────
function ACCrumb({ crumbs, onNav }) {
  return (
    <div style={{display:'flex',alignItems:'center',gap:4,flexWrap:'wrap',marginBottom:12,padding:'4px 0'}}>
      {crumbs.map((c,i)=>(
        <React.Fragment key={i}>
          <button onClick={()=>onNav(i)} style={{
            padding:'3px 9px',borderRadius:6,border:'none',fontFamily:'var(--font)',
            cursor:i<crumbs.length-1?'pointer':'default',
            background:i===crumbs.length-1?'var(--or-lt)':'transparent',
            color:i<crumbs.length-1?'var(--or)':'var(--txt)',
            fontWeight:i===crumbs.length-1?700:500,fontSize:11.5,
          }}>{c.label}</button>
          {i<crumbs.length-1&&<span style={{color:'var(--txt3)',fontSize:11,userSelect:'none'}}>›</span>}
        </React.Fragment>
      ))}
    </div>
  );
}

// ── Empty ─────────────────────────────────────────────────────────────────────
function ACEmpty({ msg }) {
  return (
    <div style={{display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'38px 20px',color:'var(--txt3)'}}>
      <svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{marginBottom:9,opacity:.45}}>
        <rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/>
      </svg>
      <div style={{fontSize:13,fontWeight:600,color:'var(--txt2)',marginBottom:3}}>No data available</div>
      <div style={{fontSize:11.5}}>{msg}</div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// CHART 1 — PREMIUM CANVAS BAR CHART
// ═══════════════════════════════════════════════════════════════════════════════

function ACBarCanvas({ data, onBarClick, onBarDrill }) {
  const cRef=acRef(null), wRef=acRef(null);
  const st=acRef({prog:0,hov:-1,bars:[]});
  const raf=acRef(null);
  const [hov,setHov]=acSt(-1);
  const [tip,setTip]=acSt({vis:false,x:0,y:0,d:null});
  const [sz,setSz]=acSt({w:600,h:220});
  const DPR=Math.min(window.devicePixelRatio||1,2);

  // ROOT CAUSE (infinite replay bug): this ResizeObserver used to feed a new
  // {w,h} object straight into state on every firing, and the animation
  // effect below used to list that `sz` object as a restart dependency.
  // Sub-pixel width jitter (scrollbar show/hide, sidebar transitions,
  // fractional zoom, font metrics settling) makes ResizeObserver fire far
  // more often than the box's *visible* size actually changes, so the bar
  // animation was restarting from 0 over and over — the "infinite loop".
  // Fix: round to whole px and bail out early when nothing perceptible
  // changed, so setSz (and any effect keyed on sz) only fires on a real resize.
  acEf(()=>{
    if(!wRef.current) return;
    const ro=new ResizeObserver(e=>{
      const w=Math.round(e[0].contentRect.width);
      setSz(prev=>{
        const h=Math.min(220,Math.max(150,w*.3));
        if(prev.w===Math.max(260,w)&&prev.h===h) return prev;
        return {w:Math.max(260,w),h};
      });
    });
    ro.observe(wRef.current); return()=>ro.disconnect();
  },[]);

  // Animation restart is now keyed ONLY on `data` (a genuine data/filter/
  // period change — acMemo upstream only produces a new array reference
  // when the underlying query actually changes). A resize alone no longer
  // replays the animation: it just redraws the current frame at the new
  // size, holding whatever progress (usually 1, i.e. settled) was reached.
  acEf(()=>{
    if(!data.length) return;
    if(raf.current) cancelAnimationFrame(raf.current);
    st.current.prog=0;
    const t0=performance.now(), dur=880;
    const tick=t=>{
      st.current.prog=acEaseOutExpo(Math.min((t-t0)/dur,1));
      draw();
      if(st.current.prog<1) raf.current=requestAnimationFrame(tick);
    };
    raf.current=requestAnimationFrame(tick);
    return()=>cancelAnimationFrame(raf.current);
  },[data]);

  // Resize-only redraw: paints the already-settled (or in-progress) frame
  // at the new canvas size without touching st.current.prog, so it never
  // looks like the chart is re-animating.
  acEf(()=>{ if(data.length) draw(); },[sz]);

  function lay() {
    const {w,h}=sz;
    const pL=54,pR=14,pT=20,pB=36;
    const cW=w-pL-pR,cH=h-pT-pB,n=data.length;
    const bSp=n>0?cW/n:60;
    const bW=Math.max(6,Math.min(46,bSp*.56));
    const maxV=Math.max(...data.map(d=>d.revenue),1);
    const mag=Math.pow(10,Math.floor(Math.log10(maxV)));
    const yMax=Math.ceil(maxV*1.18/mag)*mag;
    return {pL,pR,pT,pB,cW,cH,bSp,bW,yMax,n};
  }

  function draw() {
    const c=cRef.current; if(!c) return;
    const ctx=c.getContext('2d');
    const {w,h}=sz;
    const {pL,pT,pB,cW,cH,bSp,bW,yMax,n}=lay();
    const prog=st.current.prog, hov=st.current.hov;
    ctx.clearRect(0,0,w*DPR,h*DPR);
    ctx.save(); ctx.scale(DPR,DPR);

    // Grid lines + Y labels
    for(let g=0;g<=4;g++){
      const gy=pT+cH-(g/4)*cH, gv=(g/4)*yMax;
      ctx.save();
      ctx.strokeStyle=g===0?'rgba(0,0,0,.07)':'rgba(0,0,0,.026)';
      ctx.lineWidth=.75; ctx.setLineDash(g===0?[]:[4,5]);
      ctx.beginPath(); ctx.moveTo(pL,gy); ctx.lineTo(pL+cW,gy); ctx.stroke();
      ctx.restore();
      ctx.fillStyle='#A8A4A0';
      ctx.font='10px -apple-system,"Inter",system-ui,sans-serif';
      ctx.textAlign='right';
      ctx.fillText(acN(gv),pL-8,gy+3.5);
    }

    const bars=[];
    data.forEach((m,i)=>{
      const bx=pL+i*bSp+(bSp-bW)/2;
      const fullH=(m.revenue/yMax)*cH;
      const bh=fullH*prog;
      const by=pT+cH-bh;
      const isH=hov===i;
      const scl=isH?1.07:1;
      const bxS=bx+(bW-bW*scl)/2, bwS=bW*scl;
      const alpha=hov>=0&&!isH?.38:1;

      // Gradient fill
      const g=ctx.createLinearGradient(0,by,0,by+Math.max(bh,1));
      if(isH){
        g.addColorStop(0,`rgba(253,186,116,${alpha})`);
        g.addColorStop(1,`rgba(249,115,22,${alpha})`);
      } else {
        g.addColorStop(0,`rgba(249,115,22,${alpha})`);
        g.addColorStop(1,`rgba(234,88,12,${alpha*.72})`);
      }

      if(isH){ctx.save();ctx.shadowColor='rgba(249,115,22,.48)';ctx.shadowBlur=20;}

      const r=Math.min(9,bwS/2,bh>2?bh:99);
      if(bh>0.5){
        ctx.beginPath();
        ctx.moveTo(bxS+r,by); ctx.lineTo(bxS+bwS-r,by);
        ctx.quadraticCurveTo(bxS+bwS,by,bxS+bwS,by+r);
        ctx.lineTo(bxS+bwS,by+bh); ctx.lineTo(bxS,by+bh);
        ctx.lineTo(bxS,by+r); ctx.quadraticCurveTo(bxS,by,bxS+r,by);
        ctx.closePath(); ctx.fillStyle=g; ctx.fill();
      }
      if(isH) ctx.restore();

      // Value label above bar when hovered
      if(isH&&prog>0.85){
        ctx.fillStyle='rgba(249,115,22,.9)';
        ctx.font='bold 10px -apple-system,"Inter",system-ui,sans-serif';
        ctx.textAlign='center';
        ctx.fillText(acC(m.revenue),bxS+bwS/2,by-5);
      }

      // X label
      ctx.fillStyle=hov>=0&&!isH?'rgba(168,164,160,.45)':'#A8A4A0';
      ctx.font=(isH?'bold ':'')+`10px -apple-system,"Inter",system-ui,sans-serif`;
      ctx.textAlign='center';
      ctx.fillText(m.monthLabel,pL+i*bSp+bSp/2,pT+cH+20);

      bars.push({i,bx,bw:bW,bh:bh,by,cx:pL+i*bSp+bSp/2,topY:by,m});
    });
    st.current.bars=bars;
    ctx.restore();
  }

  function getIdx(cx,cy) {
    const {pL,pT,pB,bSp,n}=lay(); const cH=sz.h-pT-pB;
    if(cy<pT||cy>pT+cH+28) return -1;
    const i=Math.floor((cx-pL)/bSp);
    return (i>=0&&i<n)?i:-1;
  }

  function cpos(e) {
    const r=cRef.current.getBoundingClientRect();
    const t=e.touches?.[0];
    return {cx:(t?t.clientX:e.clientX)-r.left, cy:(t?t.clientY:e.clientY)-r.top};
  }

  function onMove(e) {
    const {cx,cy}=cpos(e); const i=getIdx(cx,cy);
    if(i!==st.current.hov){st.current.hov=i;setHov(i);draw();}
    if(i>=0){
      const m=data[i]; const bar=st.current.bars[i];
      setTip({vis:true,x:bar?bar.cx:cx,y:bar?bar.topY:cy,d:{
        title:m.m,
        rows:[
          {label:'Revenue',     value:acC(m.revenue),     dot:AC_COL.revenue.line,     vc:AC_COL.revenue.line},
          {label:'Gross Profit',value:acC(m.grossProfit),  dot:AC_COL.grossProfit.line, vc:m.grossProfit>=0?AC_COL.grossProfit.line:'var(--err)'},
          {label:'Net Profit',  value:acC(m.netProfit),    dot:AC_COL.netProfit.line,   vc:m.netProfit>=0?AC_COL.netProfit.line:'var(--err)'},
          {label:'Transactions',value:m.txCount},
        ],
      }});
    } else setTip(t=>({...t,vis:false}));
  }

  function onLeave() {st.current.hov=-1;setHov(-1);setTip(t=>({...t,vis:false}));draw();}

  function onClick(e) {
    const {cx,cy}=cpos(e); const i=getIdx(cx,cy);
    if(i>=0){onBarClick&&onBarClick(data[i].m);onBarDrill&&onBarDrill(data[i]);}
  }

  return (
    <div ref={wRef} style={{position:'relative',width:'100%'}}>
      <canvas ref={cRef} width={sz.w*DPR} height={sz.h*DPR}
        style={{width:'100%',height:sz.h,display:'block',cursor:hov>=0?'pointer':'default'}}
        onMouseMove={onMove} onMouseLeave={onLeave} onClick={onClick}
        onTouchStart={onMove} onTouchMove={onMove} onTouchEnd={onLeave}/>
      <ACTip vis={tip.vis} x={tip.x} y={tip.y} data={tip.d} cRef={wRef}/>
    </div>
  );
}

// ── Drill: weekly view ────────────────────────────────────────────────────────
function ACWeekView({ month, companyId, onWeekClick }) {
  const [tick,setTick]=acSt(0);
  acEf(()=>{ const unsub=Store.on(()=>setTick(t=>t+1)); return unsub; },[]);
  const weeks=acMemo(()=>acWeekly(companyId,month),[month,companyId,tick]);
  const maxR=Math.max(...weeks.map(w=>w.revenue),1);
  return weeks.length===0?<ACEmpty msg="No transactions this month"/>:(
    <div style={{display:'flex',flexDirection:'column',gap:8}}>
      {weeks.map((w,i)=>(
        <div key={i} onClick={()=>onWeekClick(w)} style={{cursor:'pointer',padding:'10px 12px',borderRadius:10,border:'1px solid var(--bdr)',transition:'background .12s'}}
          onMouseEnter={e=>e.currentTarget.style.background='#FFF7ED'}
          onMouseLeave={e=>e.currentTarget.style.background=''}>
          <div style={{display:'flex',justifyContent:'space-between',marginBottom:6,alignItems:'center'}}>
            <span style={{fontWeight:600,fontSize:12.5,color:'var(--txt)'}}>{w.label}
              <span style={{fontSize:10,color:'var(--txt3)',fontWeight:400,marginLeft:6}}>{w.from} – {w.to}</span>
            </span>
            <span style={{fontWeight:700,fontSize:12.5,color:'var(--or)'}}>{acC(w.revenue)}</span>
          </div>
          <window.PremiumProgress pct={w.revenue/maxR*100} color="var(--or)" height={8} style={{marginBottom:4}} />
          <div style={{fontSize:10.5,color:'var(--txt2)'}}>GP: {acC(w.grossProfit)} · {w.txCount} transaction{w.txCount!==1?'s':''} · click to see daily</div>
        </div>
      ))}
    </div>
  );
}

// ── Drill: daily view ─────────────────────────────────────────────────────────
function ACDayView({ from, to, companyId, onDayClick }) {
  const [tick,setTick]=acSt(0);
  acEf(()=>{ const unsub=Store.on(()=>setTick(t=>t+1)); return unsub; },[]);
  const days=acMemo(()=>acDaily(companyId,from,to),[from,to,companyId,tick]);
  const maxR=Math.max(...days.map(d=>d.revenue),1);
  return days.length===0?<ACEmpty msg="No transactions in this period"/>:(
    <div style={{display:'flex',flexDirection:'column',gap:5}}>
      {days.map((d,i)=>(
        <div key={i} onClick={()=>onDayClick(d)} style={{cursor:'pointer',display:'grid',gridTemplateColumns:'50px 1fr 100px',gap:10,alignItems:'center',padding:'6px 8px',borderRadius:8,transition:'background .12s'}}
          onMouseEnter={e=>e.currentTarget.style.background='#FFF7ED'}
          onMouseLeave={e=>e.currentTarget.style.background=''}>
          <div style={{textAlign:'center'}}>
            <div style={{fontSize:16,fontWeight:800,color:'var(--or)',lineHeight:1}}>{d.dayLabel}</div>
            <div style={{fontSize:9,color:'var(--txt3)',marginTop:1}}>{d.txCount} tx</div>
          </div>
          <window.PremiumProgress pct={d.revenue/maxR*100} color="var(--or)" height={10} />
          <div style={{fontSize:12.5,fontWeight:700,textAlign:'right',color:'var(--txt)'}}>{acC(d.revenue)}</div>
        </div>
      ))}
    </div>
  );
}

// ── Drill: transactions ───────────────────────────────────────────────────────
function ACTxnView({ date, companyId }) {
  const [tick,setTick]=acSt(0);
  acEf(()=>{ const unsub=Store.on(()=>setTick(t=>t+1)); return unsub; },[]);
  const txns=acMemo(()=>acTxns(companyId,date),[date,companyId,tick]);
  const totR=txns.reduce((s,t)=>s+window.gAmt(t),0);
  const totQ=txns.reduce((s,t)=>s+(parseFloat(t.quantity)||0),0);
  const sn = (k,id) => (Store&&Store.name)?Store.name(k,id)||'—':'—';
  return (
    <div style={{overflowX:'auto',borderRadius:8,border:'1px solid var(--bdr)'}}>
      <table style={{width:'100%',borderCollapse:'collapse',fontSize:12}}>
        <thead><tr>{['#','Challan','Customer','Material','Qty (T)','Amount','Company'].map(h=>(
          <th key={h} style={{padding:'7px 10px',textAlign:h==='#'?'center':'left',fontSize:10,fontWeight:700,color:'var(--txt2)',textTransform:'uppercase',letterSpacing:'.04em',borderBottom:'1px solid var(--bdr)',background:'#FAFAF8',whiteSpace:'nowrap',position:'sticky',top:0}}>{h}</th>
        ))}</tr></thead>
        <tbody>
          {txns.length===0
            ?<tr><td colSpan={7} style={{textAlign:'center',padding:24,color:'var(--txt3)'}}>No transactions on this date</td></tr>
            :txns.map((t,i)=>(
              <tr key={i} style={{borderBottom:'1px solid #F3F4F6'}}>
                <td style={{padding:'5px 10px',textAlign:'center',color:'var(--txt2)',fontWeight:600}}>{i+1}</td>
                <td style={{padding:'5px 10px',fontFamily:'monospace',fontSize:11}}>{t.challanNumber||'—'}</td>
                <td style={{padding:'5px 10px',maxWidth:140,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{sn('customers',t.customerId)}</td>
                <td style={{padding:'5px 10px'}}>{sn('materials',t.materialId)}</td>
                <td style={{padding:'5px 10px',fontWeight:600,textAlign:'right'}}>{(parseFloat(t.quantity)||0).toFixed(3)}</td>
                <td style={{padding:'5px 10px',fontWeight:600,color:'var(--ok)',textAlign:'right'}}>{acC(window.gAmt(t))}</td>
                <td style={{padding:'5px 10px',fontSize:10.5,color:'var(--txt2)'}}>{sn('companies',t.companyId)}</td>
              </tr>
            ))
          }
        </tbody>
        {txns.length>0&&(
          <tfoot><tr style={{background:'var(--or-lt)'}}>
            <td colSpan={4} style={{padding:'6px 10px',fontWeight:700,fontSize:11}}>TOTAL — {txns.length} record{txns.length!==1?'s':''}</td>
            <td style={{padding:'6px 10px',fontWeight:700,textAlign:'right'}}>{totQ.toFixed(3)}</td>
            <td style={{padding:'6px 10px',fontWeight:700,color:'var(--or)',textAlign:'right'}}>{acC(totR)}</td>
            <td></td>
          </tr></tfoot>
        )}
      </table>
    </div>
  );
}

// ── Bar Chart Card ────────────────────────────────────────────────────────────
// Store.on() fires on EVERY store mutation (autosave, verification re-checks,
// unrelated module edits) — not just changes relevant to this chart. Each
// firing bumps `tick`, and acMonthly()/acDaily()/etc. always return a FRESH
// array, so without this guard the chart's `data` prop changes identity on
// every background tick even when the values are byte-identical, replaying
// the entrance animation forever. This keeps the previous array reference
// whenever the recomputed data is value-equal, so the animation effect
// (keyed on `data`) only fires on a genuine change.
function acStable(prevRef, next){
  const prev=prevRef.current;
  if(prev && prev.length===next.length && prev.every((row,i)=>{
    const r=next[i];
    return r && Object.keys(row).length===Object.keys(r).length && Object.keys(row).every(k=>row[k]===r[k]);
  })) return prev;
  prevRef.current=next; return next;
}

function ACBarCard({ companyId, onMonthClick }) {
  const [period,setPeriod]=acSt('fy');
  const [drill,setDrill]=acSt(null);
  const [crumbs,setCrumbs]=acSt([{label:'All Months'}]);
  const [tick,setTick]=acSt(0);
  acEf(()=>{ const unsub=Store.on(()=>setTick(t=>t+1)); return unsub; },[]);
  const {from,to}=acMemo(()=>acPeriodRange(period),[period]);
  const mDataRef=acRef(null);
  const mData=acStable(mDataRef, acMemo(()=>acMonthly(companyId,from,to),[companyId,from,to,tick]));

  function drillMonth(m){setDrill({lv:1,month:m.m,mLabel:m.monthLabel});setCrumbs([{label:'All Months'},{label:m.monthLabel}]);}
  function drillWeek(w){setDrill(d=>({...d,lv:2,week:w}));setCrumbs(c=>[...c.slice(0,2),{label:w.label}]);}
  function drillDay(d){setDrill(p=>({...p,lv:3,day:d.date}));setCrumbs(c=>[...c.slice(0,3),{label:(window.fmtDate||String)(d.date)}]);}

  function navCrumb(idx){
    if(idx===0){setDrill(null);setCrumbs([{label:'All Months'}]);}
    else if(idx===1){setDrill(d=>({...d,lv:1,week:null}));setCrumbs(c=>c.slice(0,2));}
    else if(idx===2){setDrill(d=>({...d,lv:2}));setCrumbs(c=>c.slice(0,3));}
  }

  return (
    <div className="card">
      <div className="card-hd" style={{flexWrap:'wrap',gap:8}}>
        <div style={{minWidth:0}}>
          <h3>Revenue Trend</h3>
          <div style={{fontSize:'var(--fs-sm)',color:'var(--txt3)',marginTop:2,lineHeight:1.4}}>
            {drill?'Use breadcrumb to go back · drill into weeks, days, and transactions':'Click any bar to drill down · click bar again to filter dashboard'}
          </div>
        </div>
        {!drill&&<ACPeriod value={period} onChange={p=>{setPeriod(p);setDrill(null);setCrumbs([{label:'All Months'}]);}}/>}
      </div>
      <div style={{padding:'12px 16px'}}>
        {crumbs.length>1&&<ACCrumb crumbs={crumbs} onNav={navCrumb}/>}
        {!drill&&(mData.length===0
          ?<ACEmpty msg="No revenue data for the selected period"/>
          :<ACBarCanvas data={mData} onBarClick={onMonthClick} onBarDrill={drillMonth}/>
        )}
        {drill?.lv===1&&<ACWeekView month={drill.month} companyId={companyId} onWeekClick={drillWeek}/>}
        {drill?.lv===2&&<ACDayView from={drill.week.from} to={drill.week.to} companyId={companyId} onDayClick={drillDay}/>}
        {drill?.lv===3&&<ACTxnView date={drill.day} companyId={companyId}/>}
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// CHART 2 — PREMIUM SVG LINE CHART
// ═══════════════════════════════════════════════════════════════════════════════

function ACLineChart({ data, visible, onPointClick }) {
  const wRef=acRef(null);
  const [sz,setSz]=acSt({w:600,h:220});
  const [hov,setHov]=acSt(-1);
  const [prog,setProg]=acSt(0);
  const [tip,setTip]=acSt({vis:false,x:0,y:0,d:null});
  const raf=acRef(null);

  // Same fix as ACBarCanvas: round + bail-out on no-op resizes so this
  // ResizeObserver only fires state updates on a genuine size change.
  acEf(()=>{
    if(!wRef.current) return;
    const ro=new ResizeObserver(e=>{
      const w=Math.round(e[0].contentRect.width);
      setSz(prev=>{
        const h=Math.min(220,Math.max(150,w*.3));
        if(prev.w===Math.max(260,w)&&prev.h===h) return prev;
        return {w:Math.max(260,w),h};
      });
    });
    ro.observe(wRef.current); return()=>ro.disconnect();
  },[]);

  // Restart key is `data` + `visKey` (legend toggle) only — never `sz` — so
  // resizing the window/sidebar never replays this line animation either.
  const visKey=Object.keys(visible).filter(k=>visible[k]).sort().join();
  acEf(()=>{
    if(!data.length) return;
    if(raf.current) cancelAnimationFrame(raf.current);
    let t0=null; const dur=1000;
    const tick=t=>{if(!t0)t0=t;const p=Math.min((t-t0)/dur,1);setProg(acEaseInOutCubic(p));if(p<1)raf.current=requestAnimationFrame(tick);};
    setProg(0); raf.current=requestAnimationFrame(tick);
    return()=>cancelAnimationFrame(raf.current);
  },[data,visKey]);

  const LINES=[
    {k:'revenue',    color:AC_COL.revenue.line,    label:'Revenue'},
    {k:'grossProfit',color:AC_COL.grossProfit.line, label:'Gross Profit'},
    {k:'netProfit',  color:AC_COL.netProfit.line,   label:'Net Profit'},
  ].filter(l=>visible[l.k]);

  const {pL,pT,pB,cW,cH,xp,yp,gVals}=acMemo(()=>{
    const pL=54,pR=14,pT=20,pB=34,{w,h}=sz;
    const cW=w-pL-pR, cH=h-pT-pB;
    const activeK=Object.keys(visible).filter(k=>visible[k]);
    const allV=data.flatMap(m=>activeK.map(k=>m[k]||0));
    const minV=Math.min(...allV,0), maxV=Math.max(...allV,1);
    const pad=(maxV-minV)*.13;
    const yMin=minV-pad, yMax=maxV+pad;
    const xp=i=>pL+(data.length>1?i/(data.length-1):0.5)*cW;
    const yp=v=>pT+cH-((v-yMin)/(yMax-yMin||1))*cH;
    const gVals=[0,.25,.5,.75,1].map(t=>yMin+t*(yMax-yMin));
    return {pL,pT,pB,cW,cH,xp,yp,gVals};
  },[sz,data,visKey]);

  function bezier(pts) {
    if(!pts.length) return '';
    if(pts.length===1) return `M${pts[0][0]},${pts[0][1]}`;
    let d=`M${pts[0][0]},${pts[0][1]}`;
    for(let i=1;i<pts.length;i++){
      const cp=(pts[i-1][0]+pts[i][0])/2;
      d+=` C${cp},${pts[i-1][1]} ${cp},${pts[i][1]} ${pts[i][0]},${pts[i][1]}`;
    }
    return d;
  }

  function animPts(pts) {
    const n=pts.length; if(!n) return [];
    const vis=prog*(n-1), fi=Math.floor(vis), frac=vis-fi;
    const res=pts.slice(0,fi+1).map(p=>[...p]);
    if(fi<n-1){const a=pts[fi],b=pts[fi+1];res.push([a[0]+(b[0]-a[0])*frac,a[1]+(b[1]-a[1])*frac]);}
    return res;
  }

  function onMove(e) {
    const r=wRef.current?.getBoundingClientRect(); if(!r||!data.length) return;
    const cx=e.touches?.[0]?e.touches[0].clientX-r.left:e.clientX-r.left;
    const idx=Math.max(0,Math.min(data.length-1,Math.round((cx-pL)/cW*(data.length-1))));
    setHov(idx);
    const m=data[idx];
    setTip({vis:true,x:xp(idx),y:pT,d:{
      title:m.m,
      rows:[
        {label:'Revenue',     value:acC(m.revenue),     dot:AC_COL.revenue.line,     vc:AC_COL.revenue.line},
        {label:'Gross Profit',value:acC(m.grossProfit),  dot:AC_COL.grossProfit.line, vc:m.grossProfit>=0?AC_COL.grossProfit.line:'var(--err)'},
        {label:'Net Profit',  value:acC(m.netProfit),    dot:AC_COL.netProfit.line,   vc:m.netProfit>=0?AC_COL.netProfit.line:'var(--err)'},
      ],
    }});
  }

  function onLeave(){setHov(-1);setTip(t=>({...t,vis:false}));}
  function onClick(){if(hov>=0&&data[hov]) onPointClick&&onPointClick(data[hov].m);}

  const {w,h}=sz;

  return (
    <div ref={wRef} style={{position:'relative',width:'100%'}}>
      <svg viewBox={`0 0 ${w} ${h}`}
        style={{width:'100%',height:h,display:'block',cursor:hov>=0?'pointer':'crosshair',overflow:'visible'}}
        onMouseMove={onMove} onMouseLeave={onLeave} onClick={onClick}
        onTouchStart={onMove} onTouchMove={onMove} onTouchEnd={onLeave}>
        <defs>
          {LINES.map(l=>(
            <linearGradient key={l.k} id={`acfill-${l.k}`} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={l.color} stopOpacity=".18"/>
              <stop offset="100%" stopColor={l.color} stopOpacity="0"/>
            </linearGradient>
          ))}
        </defs>

        {/* Grid lines + Y labels */}
        {gVals.map((v,i)=>(
          <g key={i}>
            <line x1={pL} x2={pL+cW} y1={yp(v)} y2={yp(v)}
              stroke={i===0?'rgba(0,0,0,.07)':'rgba(0,0,0,.026)'} strokeWidth={.75}
              strokeDasharray={i===0?'':'4,5'}/>
            <text x={pL-8} y={yp(v)+3.5} textAnchor="end" fontSize={10} fill="#A8A4A0">
              {acN(Math.round(v))}
            </text>
          </g>
        ))}

        {/* X labels */}
        {data.map((m,i)=>(
          <text key={i} x={xp(i)} y={pT+cH+22} textAnchor="middle" fontSize={10}
            fill={hov===i?AC_COL.revenue.line:'#A8A4A0'}
            fontWeight={hov===i?700:400}>
            {m.monthLabel}
          </text>
        ))}

        {/* Animated lines + fills */}
        {LINES.map(l=>{
          const pts=data.map((m,i)=>[xp(i),yp(m[l.k]||0)]);
          const aPts=animPts(pts); if(!aPts.length) return null;
          const lp=bezier(aPts);
          const b0=yp(Math.max(0,gVals[0]));
          const ap=aPts.length>=2?`${lp} L${aPts[aPts.length-1][0]},${b0} L${aPts[0][0]},${b0} Z`:'';
          return (
            <g key={l.k}>
              {ap&&<path d={ap} fill={`url(#acfill-${l.k})`}/>}
              <path d={lp} fill="none" stroke={l.color} strokeWidth={2.5} strokeLinecap="round" strokeLinejoin="round" style={{filter:`drop-shadow(0 0 5px ${l.color}55)`}}/>
            </g>
          );
        })}

        {/* Crosshair */}
        {hov>=0&&(
          <line x1={xp(hov)} x2={xp(hov)} y1={pT} y2={pT+cH}
            stroke="rgba(0,0,0,.12)" strokeWidth={1.5} strokeDasharray="4,3"/>
        )}

        {/* Markers */}
        {LINES.map(l=>data.map((m,i)=>{
          const ap=Math.max(0,Math.min(1,prog*data.length-i));
          if(ap<.5) return null;
          const isH=hov===i;
          return (
            <g key={`${l.k}-${i}`}>
              {isH&&<circle cx={xp(i)} cy={yp(m[l.k]||0)} r={16} fill={l.color} opacity=".09"/>}
              <circle cx={xp(i)} cy={yp(m[l.k]||0)} r={isH?5.5:3} fill="#fff"
                stroke={l.color} strokeWidth={isH?2.5:2}
                style={{filter:isH?`drop-shadow(0 0 5px ${l.color}aa)`:undefined}}/>
            </g>
          );
        }))}

        {/* Invisible hover target */}
        <rect x={pL} y={pT} width={cW} height={cH} fill="transparent"/>
      </svg>
      <ACTip vis={tip.vis} x={tip.x} y={tip.y} data={tip.d} cRef={wRef}/>
    </div>
  );
}

// ── Line Chart Card ───────────────────────────────────────────────────────────
function ACLineCard({ companyId, onMonthClick }) {
  const [period,setPeriod]=acSt('fy');
  const [vis,setVis]=acSt({revenue:true,grossProfit:true,netProfit:true});
  const [tick,setTick]=acSt(0);
  acEf(()=>{ const unsub=Store.on(()=>setTick(t=>t+1)); return unsub; },[]);
  const {from,to}=acMemo(()=>acPeriodRange(period),[period]);
  const mDataRef=acRef(null);
  const mData=acStable(mDataRef, acMemo(()=>acMonthly(companyId,from,to),[companyId,from,to,tick]));

  const TOGGLES=[
    {k:'revenue',    label:'Revenue',      color:AC_COL.revenue.line},
    {k:'grossProfit',label:'Gross Profit', color:AC_COL.grossProfit.line},
    {k:'netProfit',  label:'Net Profit',   color:AC_COL.netProfit.line},
  ];

  return (
    <div className="card">
      <div className="card-hd" style={{flexWrap:'wrap',gap:8}}>
        <div style={{minWidth:0}}>
          <h3>Financial Performance</h3>
          <div style={{fontSize:'var(--fs-sm)',color:'var(--txt3)',marginTop:2,lineHeight:1.4}}>
            Click any data point to filter dashboard to that month
          </div>
        </div>
        <ACPeriod value={period} onChange={setPeriod}/>
      </div>

      {/* Legend toggles */}
      <div style={{display:'flex',gap:6,padding:'8px 16px 0',flexWrap:'wrap'}}>
        {TOGGLES.map(t=>(
          <button key={t.k} onClick={()=>setVis(v=>({...v,[t.k]:!v[t.k]}))} style={{
            display:'flex',alignItems:'center',gap:6,padding:'4px 12px',
            border:`1.5px solid ${vis[t.k]?t.color:'var(--bdr)'}`,borderRadius:20,
            cursor:'pointer',background:vis[t.k]?t.color+'1a':'#fff',
            color:vis[t.k]?t.color:'var(--txt3)',
            fontSize:11,fontWeight:600,fontFamily:'var(--font)',
            transition:'all .2s ease',whiteSpace:'nowrap',
          }}>
            <span style={{width:7,height:7,borderRadius:'50%',background:vis[t.k]?t.color:'#D1D5DB',display:'inline-block',flexShrink:0,transition:'background .2s'}}/>
            {t.label}
          </button>
        ))}
      </div>

      <div style={{padding:'10px 16px 14px'}}>
        {mData.length===0
          ?<ACEmpty msg="No financial data for selected period"/>
          :<ACLineChart data={mData} visible={vis} onPointClick={onMonthClick}/>
        }
      </div>
    </div>
  );
}

// ── Public export ─────────────────────────────────────────────────────────────
function AnalyticsChartsSection({ companyId, onMonthClick }) {
  return (
    <div>
      <div style={{fontSize:10.5,fontWeight:700,color:'var(--txt2)',textTransform:'uppercase',letterSpacing:'0.06em',marginBottom:6}}>
        Premium Analytics
      </div>
      <div className="ch-grid" style={{marginBottom:12}}>
        <ACBarCard  companyId={companyId} onMonthClick={onMonthClick}/>
        <ACLineCard companyId={companyId} onMonthClick={onMonthClick}/>
      </div>
    </div>
  );
}

window.AnalyticsChartsSection = AnalyticsChartsSection;
