// ══════════════════════════════════════════════════════════
// Shared small components and icons
// ══════════════════════════════════════════════════════════

const { useState, useEffect, useMemo, useRef } = React;

// ──────────────────────────────────────────────────────────
// EDUTIP — hover / tap tooltip for client education.
// Wraps a "?" glyph; popover flips to stay in viewport.
// Usage: <EduTip term="KYC">Know Your Client — regulators require...</EduTip>
// ──────────────────────────────────────────────────────────
function EduTip({ term, children, size = 'sm' }){
  const [open, setOpen] = useState(false);
  const [placement, setPlacement] = useState({ side:'top', align:'center' });
  const triggerRef = useRef(null);
  const popRef = useRef(null);

  // Close on outside click (mobile) or escape
  useEffect(() => {
    if(!open) return;
    let closeTimer = null;
    function onDown(e){
      if(!triggerRef.current?.contains(e.target) && !popRef.current?.contains(e.target)){
        setOpen(false);
      }
    }
    function onEsc(e){ if(e.key === 'Escape') setOpen(false); }
    document.addEventListener('mousedown', onDown);
    document.addEventListener('touchstart', onDown);
    document.addEventListener('keydown', onEsc);
    return () => {
      document.removeEventListener('mousedown', onDown);
      document.removeEventListener('touchstart', onDown);
      document.removeEventListener('keydown', onEsc);
      if(closeTimer) clearTimeout(closeTimer);
    };
  }, [open]);

  const handleMouseEnter = () => {
    setOpen(true);
  };

  const handleMouseLeave = () => {
    // Delay close by 500ms to allow moving between trigger and popup
    setTimeout(() => {
      setOpen(false);
    }, 500);
  };

  return (
    <span className={`edutip edutip-${size}`}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}>
      <button type="button"
        ref={triggerRef}
        className="edutip-trigger"
        aria-label={`What does ${term} mean?`}
        aria-expanded={open}
        onClick={(e) => { e.stopPropagation(); setOpen(v => !v); }}
        onFocus={() => setOpen(true)}
        onBlur={() => setOpen(false)}>
        ?
      </button>
      {open && (
        <span
          ref={popRef}
          className={`edutip-pop edutip-${placement.side} edutip-align-${placement.align}`}
          role="tooltip"
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}>
          <span className="edutip-term">{term}</span>
          <span className="edutip-body">{children}</span>
        </span>
      )}
    </span>
  );
}

// Simple inline SVG icons
const Icon = {
  mail: (<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>),
  shield: (<svg viewBox="0 0 24 24"><path d="M12 1 3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z"/></svg>),
  phone: (<svg viewBox="0 0 24 24"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></svg>),
  cal: (<svg viewBox="0 0 24 24"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"/></svg>),
  doc: (<svg viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>),
  play: (<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>),
  check: (<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>),
  arrow: (<svg viewBox="0 0 24 24"><path d="M8 5l1.4 1.4L13.95 11H4v2h9.95L9.4 17.6 8 19l7-7z"/></svg>),
  settings: (<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.488.488 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94 0 .31.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>)
};

// Progress ring with animated fill
function ProgressRing({ score, max, size=112 }){
  const r = (size - 16) / 2;
  const c = 2 * Math.PI * r;
  const pct = Math.min(1, score / max);
  const offset = c - (c * pct);
  return (
    <div className="progress-ring" style={{width:size,height:size}}>
      <svg viewBox={`0 0 ${size} ${size}`}>
        <circle cx={size/2} cy={size/2} r={r} className="progress-ring-track" />
        <circle cx={size/2} cy={size/2} r={r} className="progress-ring-fill"
          strokeDasharray={c} strokeDashoffset={offset} />
      </svg>
      <div className="progress-ring-center">
        <div className="progress-ring-num">{score}<span style={{color:'rgba(255,255,255,.4)',fontSize:16,fontWeight:800}}>/{max}</span></div>
        <div className="progress-ring-label">Engagement</div>
      </div>
    </div>
  );
}

function ProgressBar({ score, max }){
  const pct = Math.min(100, Math.round((score/max)*100));
  return (
    <div className="progress-bar-wrap">
      <div className="progress-bar-top">
        <div className="progress-bar-num">{score}<span>/ {max} points</span></div>
        <div className="progress-bar-label">{pct}% complete</div>
      </div>
      <div className="progress-bar"><div className="progress-bar-fill" style={{width: pct+'%'}}/></div>
    </div>
  );
}

function ProgressNumeric({ score, max }){
  const pct = Math.round((score/max)*100);
  return (
    <div style={{display:'flex',alignItems:'baseline',gap:14,flexWrap:'wrap'}}>
      <div>
        <div style={{fontFamily:'Manrope',fontWeight:900,fontSize:48,color:'var(--accent)',letterSpacing:'-.03em',lineHeight:1}}>{pct}<span style={{fontSize:24}}>%</span></div>
        <div style={{fontSize:11.5,color:'var(--text-dim)',fontWeight:700,textTransform:'uppercase',letterSpacing:1.3,marginTop:6}}>Service-plan completion</div>
      </div>
      <div style={{fontFamily:'Manrope',fontSize:15,color:'var(--text-muted)',fontWeight:600}}>
        {score} of {max} points earned this year
      </div>
    </div>
  );
}

// Map category key to the CSS modifier class
function catClass(k){
  return k ? `cat-${k}` : '';
}

// Derive status for an activity given the completed set
function deriveStatus(activity, completedSet, forceOverdueSet){
  if(completedSet.has(activity.id)) return 'complete';
  if(forceOverdueSet && forceOverdueSet.has(activity.id)) return 'overdue';
  if(activity.overdue) return 'overdue';
  if(activity.status === 'active') return 'active';
  return 'upcoming';
}

function statusLabel(s){
  return ({complete:'Completed',active:'In progress',overdue:'Overdue',upcoming:'Upcoming'})[s] || 'Upcoming';
}

// Activity card (default 'cards' view)
function ActivityCard({ activity, completed, reminderOn, onToggle, onToggleRemind, onOpenResource, forceOverdue, overdueDates }){
  const [expanded, setExpanded] = useState(false);
  const status = deriveStatus(activity, completed, forceOverdue);
  const isComplete = status === 'complete';
  const isOverdue = status === 'overdue';
  const overdueText = overdueDates && overdueDates[activity.id];
  return (
    <div className={`activity-card ${isComplete?'completed':''} ${isOverdue?'overdue':''} ${expanded?'expanded':''}`}>
      <div className="activity-top">
        <button className={`activity-check ${isComplete?'checked':''} ${isOverdue?'overdue':''}`}
          onClick={(e) => onToggle(activity.id, e)}
          aria-label={isComplete?'Mark incomplete':'Mark complete'}></button>
        <div className="activity-body">
          <div className="activity-meta-row">
            <span className={`activity-cat ${catClass(activity.categoryKey)}`}>{activity.category}</span>
            <span className={`activity-status ${status}`}>
              <span className="activity-status-dot"></span>
              {statusLabel(status)}
            </span>
          </div>
          <h3 className="activity-title">{activity.title}</h3>
          <p className="activity-desc">{activity.desc}</p>
        </div>
        <div className="activity-right">
          <div className="activity-date-label">{isComplete?'Completed':isOverdue?'Overdue':'Due'}</div>
          <div className="activity-date">{isOverdue && overdueText ? overdueText.split(' · ')[0].replace(/^Was due /,'') : activity.due.replace(/^Completed |^Due |^Scheduled |^Suggested /,'')}</div>
          {isOverdue && overdueText && overdueText.includes(' · ') && (
            <div className="activity-date-late">{overdueText.split(' · ')[1]}</div>
          )}
        </div>
      </div>
      <div className="activity-actions">
        {!isComplete && (
          <button className="activity-btn" onClick={() => onOpenResource(activity)}>
            {activity.expanded?.resourceLabel || 'Open'}
            <svg viewBox="0 0 24 24"><path d="M8 5l1.4 1.4L13.95 11H4v2h9.95L9.4 17.6 8 19l7-7z"/></svg>
          </button>
        )}
        <button className="activity-btn secondary" onClick={() => setExpanded(v=>!v)}>
          <span>{expanded?'Close details':'View details'}</span>
          <span className="activity-expand-chev">▾</span>
        </button>
        {!isComplete && (
          <label className={`activity-remind ${reminderOn?'activity-remind-on':''}`}>
            <input type="checkbox" checked={!!reminderOn} onChange={() => onToggleRemind(activity.id)} />
            {reminderOn?'Reminders on':'Remind me'}
          </label>
        )}
      </div>
      {expanded && activity.expanded && (
        <div className="activity-expanded">
          <h5>{activity.expanded.title}</h5>
          <ol>
            {activity.expanded.steps.map((s,i) => <li key={i}>{s}</li>)}
          </ol>
          {activity.expanded.resourceLabel && (
            <a className="exp-resource" onClick={() => onOpenResource(activity)}>
              {activity.expanded.resourceLabel} →
            </a>
          )}
        </div>
      )}
    </div>
  );
}

// Timeline view item
function ActivityTimelineItem({ activity, completed, reminderOn, onToggle, onToggleRemind, onOpenResource, forceOverdue, overdueDates }){
  const status = deriveStatus(activity, completed, forceOverdue);
  return (
    <div className={`activity-tl-item ${status}`}>
      <div className="activity-tl-dot"></div>
      <ActivityCard activity={activity} completed={completed} reminderOn={reminderOn} forceOverdue={forceOverdue} overdueDates={overdueDates}
        onToggle={onToggle} onToggleRemind={onToggleRemind} onOpenResource={onOpenResource}/>
    </div>
  );
}

// Compact row
function ActivityCompact({ activity, completed, onToggle, forceOverdue, overdueDates }){
  const status = deriveStatus(activity, completed, forceOverdue);
  const isComplete = status === 'complete';
  const isOverdue = status === 'overdue';
  const overdueText = overdueDates && overdueDates[activity.id];
  return (
    <div className={`activity-compact ${isComplete?'completed':''} ${isOverdue?'overdue':''}`} onClick={(e) => onToggle(activity.id, e)}>
      <div className={`ac-status-bar ${status}`}></div>
      <span className={`activity-cat ${catClass(activity.categoryKey)}`} style={{padding:'3px 8px',fontSize:10}}>{activity.category}</span>
      <div className="ac-title">
        {activity.title}
        {isOverdue && <span className="ac-overdue-badge">Overdue</span>}
      </div>
      <div className="ac-date">{isOverdue && overdueText ? overdueText : activity.due.replace(/^Completed |^Due |^Scheduled |^Suggested /,'')}</div>
      <button className={`activity-check ${isComplete?'checked':''} ${isOverdue?'overdue':''}`}
        onClick={(e) => { e.stopPropagation(); onToggle(activity.id, e); }}
        style={{width:22,height:22}}></button>
    </div>
  );
}

// Tile view — compact grid card, click to expand into full card
function ActivityTile({ activity, completed, reminderOn, onToggle, onToggleRemind, onOpenResource, forceOverdue, overdueDates, expanded, onToggleExpand }){
  const status = deriveStatus(activity, completed, forceOverdue);
  const isComplete = status === 'complete';
  const isOverdue = status === 'overdue';
  const overdueText = overdueDates && overdueDates[activity.id];
  const dateText = isOverdue && overdueText
    ? overdueText.split(' · ')[0].replace(/^Was due /,'')
    : activity.due.replace(/^Completed |^Due |^Scheduled |^Suggested /,'');
  return (
    <div className={`activity-tile ${isComplete?'completed':''} ${isOverdue?'overdue':''} ${expanded?'expanded':''}`}>
      <div className="activity-tile-head" onClick={()=>onToggleExpand(activity.id)}>
        <button className={`activity-check ${isComplete?'checked':''} ${isOverdue?'overdue':''}`}
          onClick={(e)=>{ e.stopPropagation(); onToggle(activity.id, e); }}
          aria-label={isComplete?'Mark incomplete':'Mark complete'}></button>
        <div className="activity-tile-body">
          <div className="activity-tile-meta">
            <span className={`activity-cat ${catClass(activity.categoryKey)}`}>{activity.category}</span>
            <span className={`activity-status ${status}`}>
              <span className="activity-status-dot"></span>
              {statusLabel(status)}
            </span>
          </div>
          <h3 className="activity-tile-title">
            {activity.title}
            {activity.expanded?.tooltip && (
              <EduTip term="Learn more" size="sm">
                <span dangerouslySetInnerHTML={{__html: activity.expanded.tooltip}} />
              </EduTip>
            )}
          </h3>
          <div className="activity-tile-date">
            <span className="activity-tile-date-label">{isComplete?'Completed':isOverdue?'Overdue':'Due'}</span>
            <span className="activity-tile-date-val">{dateText}</span>
          </div>
        </div>
      </div>
      {expanded && (
        <div className="activity-tile-expanded" onClick={(e)=>e.stopPropagation()}>
          <p>{activity.desc}</p>
          <div className="activity-actions">
            {!isComplete && (
              <button className="activity-btn" onClick={()=>onOpenResource(activity)}>
                {activity.expanded?.resourceLabel || 'Open'}
                <svg viewBox="0 0 24 24"><path d="M8 5l1.4 1.4L13.95 11H4v2h9.95L9.4 17.6 8 19l7-7z"/></svg>
              </button>
            )}
            {isComplete && (
              <button className="activity-btn secondary" onClick={()=>onContact('request-update-' + activity.id)}>
                Request update
              </button>
            )}
            {!isComplete && (
              <label className={`activity-remind ${reminderOn?'activity-remind-on':''}`}>
                <input type="checkbox" checked={!!reminderOn} onChange={()=>onToggleRemind(activity.id)} />
                {reminderOn?'Reminders on':'Remind me'}
              </label>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// ──────────────────────────────────────────────────────────
// CONFETTI — lightweight canvas burst at a point on screen
// palette = brand colors. Small burst ~24 particles, big ~80.
// ──────────────────────────────────────────────────────────
const CONFETTI_PALETTE = ['#EFBC75','#C1E1A7','#148D8D','#F5EFE3','#EFBC75','#A8C293'];

function fireConfetti(x, y, { count = 24, spread = Math.PI * 0.9, power = 10, big = false } = {}){
  let canvas = document.getElementById('__confetti_canvas');
  if(!canvas){
    canvas = document.createElement('canvas');
    canvas.id = '__confetti_canvas';
    canvas.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:9999';
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    document.body.appendChild(canvas);
    window.addEventListener('resize', () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    });
  }
  const ctx = canvas.getContext('2d');
  const n = big ? 80 : count;
  const particles = [];
  for(let i = 0; i < n; i++){
    const angle = -Math.PI/2 + (Math.random() - 0.5) * spread;
    const speed = power * (0.6 + Math.random() * 0.8);
    particles.push({
      x, y,
      vx: Math.cos(angle) * speed + (Math.random() - 0.5) * 2,
      vy: Math.sin(angle) * speed - 2,
      rot: Math.random() * Math.PI * 2,
      vrot: (Math.random() - 0.5) * 0.35,
      w: big ? 8 + Math.random() * 6 : 5 + Math.random() * 4,
      h: big ? 4 + Math.random() * 3 : 2.5 + Math.random() * 2,
      color: CONFETTI_PALETTE[(Math.random() * CONFETTI_PALETTE.length) | 0],
      life: big ? 1.4 : 1.0,
      shape: Math.random() < 0.35 ? 'circle' : 'rect',
    });
  }
  if(!canvas.__running){
    canvas.__running = true;
    canvas.__particles = [];
    let last = performance.now();
    function frame(now){
      const dt = Math.min(0.05, (now - last) / 1000);
      last = now;
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      for(const p of canvas.__particles){
        p.vy += 22 * dt;
        p.vx *= 0.995;
        p.x += p.vx * 60 * dt;
        p.y += p.vy * 60 * dt;
        p.rot += p.vrot;
        p.life -= dt * 0.9;
        if(p.life <= 0) continue;
        ctx.save();
        ctx.translate(p.x, p.y);
        ctx.rotate(p.rot);
        ctx.globalAlpha = Math.max(0, Math.min(1, p.life * 1.2));
        ctx.fillStyle = p.color;
        if(p.shape === 'circle'){
          ctx.beginPath();
          ctx.arc(0, 0, p.w * 0.5, 0, Math.PI * 2);
          ctx.fill();
        } else {
          ctx.fillRect(-p.w/2, -p.h/2, p.w, p.h);
        }
        ctx.restore();
      }
      canvas.__particles = canvas.__particles.filter(p => p.life > 0 && p.y < canvas.height + 40);
      if(canvas.__particles.length > 0){
        requestAnimationFrame(frame);
      } else {
        canvas.__running = false;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
      }
    }
    requestAnimationFrame(frame);
  }
  canvas.__particles.push(...particles);
}

// Toast
function Toast({ message, visible }){
  return (
    <div className={`toast ${visible?'visible':''}`}>
      <svg viewBox="0 0 24 24" style={{width:16,height:16,fill:'currentColor'}}><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>
      {message}
    </div>
  );
}

Object.assign(window, {
  Icon, ProgressRing, ProgressBar, ProgressNumeric,
  catClass, deriveStatus, statusLabel,
  ActivityCard, ActivityTimelineItem, ActivityCompact, ActivityTile, Toast,
  fireConfetti, EduTip
});
