// components.jsx — INDIECASH shared atoms
// All components consume theme + accent via props (no global context — keeps
// JSX scoping simple across multiple Babel files).

// ─── Icon ────────────────────────────────────────────────────────
// Wrapper around Lucide. Re-runs createIcons after mount so newly-rendered
// <i data-lucide=…> stubs get swapped for SVG. size in px, stroke in px.
function Icon({ name, size = 20, color = 'currentColor', stroke = 1.75, style }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (window.lucide && ref.current) window.lucide.createIcons({
      attrs: { 'stroke-width': stroke, width: size, height: size },
      // limit scope so we don't keep re-rendering icons elsewhere
      nameAttr: 'data-lucide',
    });
  }, [name, size, stroke]);
  return (
    <i ref={ref} data-lucide={name}
       style={{
         display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
         width: size, height: size, color,
         flexShrink: 0, ...style,
       }} />
  );
}

// ─── INDIECASH logo badge ────────────────────────────────────────
// Circular emerald badge with stylized $ glyph — matches INDIE Group
// circular-badge vocabulary. size=mark only; wordmark is a separate component.
function CashBadge({ size = 64, accent }) {
  const a = accent || PALETTES.emerald;
  return (
    <svg width={size} height={size} viewBox="0 0 100 100" aria-label="INDIECASH">
      <defs>
        <linearGradient id="cb-grad" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stopColor={a.grad[0]} />
          <stop offset="100%" stopColor={a.grad[1]} />
        </linearGradient>
      </defs>
      <circle cx="50" cy="50" r="48" fill="url(#cb-grad)" />
      <circle cx="50" cy="50" r="42" fill="none" stroke="#fff" strokeOpacity="0.18" strokeWidth="1" />
      {/* stylized $ — vertical bar + S curve, all white */}
      <g fill="none" stroke="#fff" strokeWidth="6" strokeLinecap="round" strokeLinejoin="round">
        <path d="M50 24 V76" />
        <path d="M65 36 Q50 28 40 36 Q30 44 50 50 Q70 56 60 64 Q50 72 35 64" />
      </g>
    </svg>
  );
}

function CashWordmark({ size = 22, color = 'currentColor' }) {
  // ALL-CAPS condensed display: matches INDIE Group wordmark voice.
  return (
    <span style={{
      fontFamily: '"Bebas Neue", "Anton", "Plus Jakarta Sans", system-ui',
      fontSize: size, fontWeight: 700, letterSpacing: '0.04em',
      lineHeight: 1, color,
    }}>
      INDIE<span style={{ opacity: 0.5 }}>·</span>CASH
    </span>
  );
}

// ─── Card ────────────────────────────────────────────────────────
function Card({ theme, children, style, onClick, padding = 16, radius = 18, hoverable = false }) {
  return (
    <div
      onClick={onClick}
      style={{
        background: theme.surface,
        border: `0.5px solid ${theme.border}`,
        borderRadius: radius,
        padding,
        boxShadow: theme.shadow1,
        cursor: onClick ? 'pointer' : 'default',
        transition: 'background 120ms, transform 120ms',
        ...style,
      }}>
      {children}
    </div>
  );
}

// ─── Avatar ──────────────────────────────────────────────────────
// Solid disc with initials. Stable hue per member (passed in).
function Avatar({ member, size = 36, ring, style }) {
  if (!member) return null;
  if (!member.name && !member.short && !member.full_name) member = Object.assign({}, member, {name:'?'});
  if (!member) return null;
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      background: member.color,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      color: '#fff', fontWeight: 700,
      fontSize: Math.max(10, size * 0.38),
      letterSpacing: '0.02em',
      flexShrink: 0,
      boxShadow: ring ? `0 0 0 2px ${ring}` : 'none',
      ...style,
    }}>
      {memberInitials(member.name || member.full_name || member.short || '?')}
    </div>
  );
}

// Stacked avatars (-8px overlap) for shared-goal contributors etc.
function AvatarStack({ members, size = 24, max = 4, ringColor = '#fff' }) {
  const shown = members.slice(0, max);
  const extra = members.length - shown.length;
  return (
    <div style={{ display: 'flex' }}>
      {shown.map((m, i) => (
        <div key={m.id} style={{ marginLeft: i === 0 ? 0 : -size * 0.32 }}>
          <Avatar member={m} size={size} ring={ringColor} />
        </div>
      ))}
      {extra > 0 && (
        <div style={{
          width: size, height: size, borderRadius: '50%',
          marginLeft: -size * 0.32,
          background: '#94a3b8',
          color: '#fff', fontWeight: 700, fontSize: size * 0.36,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          boxShadow: `0 0 0 2px ${ringColor}`,
        }}>+{extra}</div>
      )}
    </div>
  );
}

// ─── Category disc ───────────────────────────────────────────────
// Soft tinted disc + colored stroke icon. Used in transaction list.
function CatDisc({ cat, size = 38 }) {
  if (!cat) return null;
  return (
    <div style={{
      width: size, height: size, borderRadius: size * 0.32,
      background: cat.color + '1a', // 10% alpha
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      flexShrink: 0,
    }}>
      <Icon name={cat.icon} size={size * 0.52} color={cat.color} stroke={1.75} />
    </div>
  );
}

// ─── Chip / Pill ─────────────────────────────────────────────────
function Chip({ children, active, onClick, theme, style }) {
  return (
    <button
      onClick={onClick}
      style={{
        appearance: 'none', border: 0,
        padding: '8px 14px',
        background: active ? theme.fg1 : 'transparent',
        color: active ? theme.surface : theme.fg2,
        border: `1px solid ${active ? theme.fg1 : theme.border}`,
        borderRadius: 999,
        fontSize: 13, fontWeight: 600,
        cursor: 'pointer',
        whiteSpace: 'nowrap',
        transition: 'all 120ms',
        ...style,
      }}>
      {children}
    </button>
  );
}

// ─── Badge ───────────────────────────────────────────────────────
function Badge({ children, color = 'neutral', theme, style }) {
  const tones = {
    neutral: { bg: theme.surfaceAlt, fg: theme.fg2 },
    success: { bg: theme.successSoft, fg: theme.success },
    danger:  { bg: theme.dangerSoft,  fg: theme.danger },
    warning: { bg: theme.warningSoft, fg: theme.warning },
    accent:  { bg: theme.accent.tint, fg: theme.accent[600] },
  };
  const t = tones[color] || tones.neutral;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 4,
      padding: '3px 8px', borderRadius: 6,
      background: t.bg, color: t.fg,
      fontSize: 11, fontWeight: 600,
      letterSpacing: '0.02em',
      whiteSpace: 'nowrap',
      ...style,
    }}>{children}</span>
  );
}

// ─── Progress bar ────────────────────────────────────────────────
// Color shifts by pct — green < 70 < amber < 90 < red.
function ProgressBar({ value, max, theme, height = 6, color, showValues }) {
  const pct = Math.min(100, (value / max) * 100);
  const auto = pct >= 100 ? theme.danger
    : pct >= 90 ? theme.danger
    : pct >= 75 ? theme.warning
    : theme.accent[500];
  const fill = color || auto;
  return (
    <div style={{ width: '100%' }}>
      <div style={{
        width: '100%', height, borderRadius: height,
        background: theme.surfaceAlt, overflow: 'hidden',
      }}>
        <div style={{
          width: pct + '%', height: '100%',
          background: fill,
          borderRadius: height,
          transition: 'width 300ms cubic-bezier(0.22,0.61,0.36,1)',
        }} />
      </div>
    </div>
  );
}

// ─── Section header ─────────────────────────────────────────────
function SectionHeader({ title, action, onAction, theme, style }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      padding: '0 20px', marginBottom: 12, ...style,
    }}>
      <h2 style={{
        margin: 0, fontSize: 16, fontWeight: 700, color: theme.fg1,
        letterSpacing: '-0.01em',
      }}>{title}</h2>
      {action && (
        <button onClick={onAction} style={{
          appearance: 'none', background: 'none', border: 0,
          color: theme.accent[600], fontSize: 13, fontWeight: 600,
          cursor: 'pointer', padding: 4, margin: -4,
        }}>{action}</button>
      )}
    </div>
  );
}

// ─── 7-day area chart (SVG) ─────────────────────────────────────
function AreaChart7d({ data, labels, theme, accent, height = 100, width = 350 }) {
  const max = Math.max(...data, 1);
  const n = data.length;
  const padX = 8, padY = 10;
  const innerW = width - padX * 2;
  const innerH = height - padY * 2;
  const x = (i) => padX + (innerW * i) / (n - 1);
  const y = (v) => padY + innerH - (v / max) * innerH;

  // smooth path via catmull-rom → cubic
  let path = `M ${x(0)} ${y(data[0])}`;
  for (let i = 0; i < n - 1; i++) {
    const x1 = x(i), y1 = y(data[i]);
    const x2 = x(i + 1), y2 = y(data[i + 1]);
    const cx = (x1 + x2) / 2;
    path += ` C ${cx} ${y1}, ${cx} ${y2}, ${x2} ${y2}`;
  }
  const area = path + ` L ${x(n - 1)} ${padY + innerH} L ${x(0)} ${padY + innerH} Z`;

  // index of today (last point) — highlight dot
  const last = n - 1;
  const gid = `g-${accent[500].replace('#','')}`;

  return (
    <div>
      <svg width="100%" viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none"
           style={{ display: 'block' }}>
        <defs>
          <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"  stopColor={accent[500]} stopOpacity="0.28" />
            <stop offset="100%" stopColor={accent[500]} stopOpacity="0" />
          </linearGradient>
        </defs>
        <path d={area} fill={`url(#${gid})`} />
        <path d={path} fill="none" stroke={accent[500]} strokeWidth="2"
              strokeLinecap="round" strokeLinejoin="round" />
        {/* hairline baseline */}
        <line x1={padX} y1={padY + innerH + 0.5} x2={width - padX} y2={padY + innerH + 0.5}
              stroke={theme.border} strokeWidth="1" />
        {/* today dot */}
        <circle cx={x(last)} cy={y(data[last])} r="4" fill={accent[500]}
                stroke={theme.surface} strokeWidth="2" />
      </svg>
      <div style={{
        display: 'flex', justifyContent: 'space-between',
        padding: '6px 8px 0', fontSize: 11, color: theme.fg3, fontWeight: 500,
      }}>
        {labels.map((l, i) => (
          <span key={i} style={{
            color: i === last ? theme.fg1 : theme.fg3,
            fontWeight: i === last ? 700 : 500,
          }}>{l}</span>
        ))}
      </div>
    </div>
  );
}

// ─── Quick-action button (Pemasukan / Pengeluaran on dashboard) ─
function QuickAction({ kind, label, accent, theme, onClick }) {
  // income → accent gradient | expense → outlined neutral
  const isIncome = kind === 'income';
  return (
    <button onClick={onClick} style={{
      appearance: 'none', border: 0, cursor: 'pointer',
      flex: 1, height: 56, borderRadius: 14,
      display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      background: isIncome ? accent[500] : theme.surface,
      color: isIncome ? '#fff' : theme.fg1,
      border: isIncome ? 'none' : `1px solid ${theme.border}`,
      boxShadow: isIncome ? `0 4px 14px ${accent[500]}40` : theme.shadow1,
      fontSize: 14, fontWeight: 700,
      letterSpacing: '-0.01em',
      transition: 'transform 120ms',
    }}>
      <span style={{
        width: 26, height: 26, borderRadius: 8,
        background: isIncome ? 'rgba(255,255,255,0.22)' : accent[500] + '1a',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: isIncome ? '#fff' : accent[600],
      }}>
        <Icon name={isIncome ? 'arrow-down-left' : 'arrow-up-right'} size={16} stroke={2.2} />
      </span>
      {label}
    </button>
  );
}

// ─── Bottom-sheet shell (used by Transaction form, etc.) ────────
function BottomSheet({ open, onClose, theme, children, height = '85%', maxHeight = 720 }) {
  const [mounted, setMounted] = React.useState(open);
  const [visible, setVisible] = React.useState(open);

  React.useEffect(() => {
    if (open) {
      setMounted(true);
      // double-rAF so the transform transition runs from initial state
      requestAnimationFrame(() => requestAnimationFrame(() => setVisible(true)));
    } else {
      setVisible(false);
      const t = setTimeout(() => setMounted(false), 240);
      return () => clearTimeout(t);
    }
  }, [open]);

  if (!mounted) return null;
  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 30,
      pointerEvents: 'auto',
    }}>
      {/* scrim */}
      <div onClick={onClose} style={{
        position: 'absolute', inset: 0,
        background: theme.overlay,
        opacity: visible ? 1 : 0,
        transition: 'opacity 240ms ease-out',
      }} />
      {/* sheet */}
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0,
        height, maxHeight,
        background: theme.surface,
        borderTopLeftRadius: 24, borderTopRightRadius: 24,
        boxShadow: theme.shadow3,
        transform: visible ? 'translateY(0)' : 'translateY(100%)',
        transition: 'transform 280ms cubic-bezier(0.22,0.61,0.36,1)',
        display: 'flex', flexDirection: 'column',
        overflow: 'hidden',
      }}>
        {/* grabber */}
        <div style={{
          padding: '10px 0 4px',
          display: 'flex', justifyContent: 'center', flexShrink: 0,
        }}>
          <div style={{
            width: 38, height: 4, borderRadius: 2,
            background: theme.borderStrong,
          }} />
        </div>
        {children}
      </div>
    </div>
  );
}

// ─── Empty state ────────────────────────────────────────────────
function EmptyState({ icon = 'inbox', title, hint, theme }) {
  return (
    <div style={{
      padding: '40px 20px', textAlign: 'center',
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10,
    }}>
      <div style={{
        width: 52, height: 52, borderRadius: 16,
        background: theme.surfaceAlt,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <Icon name={icon} size={26} color={theme.fg3} />
      </div>
      <div style={{ fontSize: 15, fontWeight: 600, color: theme.fg1 }}>{title}</div>
      {hint && <div style={{ fontSize: 13, color: theme.fg3, maxWidth: 240 }}>{hint}</div>}
    </div>
  );
}

Object.assign(window, {
  Icon, CashBadge, CashWordmark, Card,
  Avatar, AvatarStack, CatDisc, Chip, Badge,
  ProgressBar, SectionHeader,
  AreaChart7d, QuickAction, BottomSheet, EmptyState,
});
