/* === Reusable components: Tag pill, Donut chart, Bar chart === */

function Tag({ tag, onClick, selected }) {
  if (!tag) return null;
  const color = TAG_COLORS[tag.color] || TAG_COLORS.slate;
  return (
    <span
      className={`tag ${selected ? 'selected' : ''}`}
      style={{
        color: color,
        background: color + '14',
        borderColor: color + '33',
      }}
      onClick={onClick}
    >
      {tag.name}
    </span>
  );
}

// Donut pie chart with multi-segment support
function Donut({ data, size = 180, thickness = 22, centerLabel, centerValue }) {
  const r = (size - thickness) / 2;
  const c = 2 * Math.PI * r;
  const total = data.reduce((s, d) => s + d.value, 0) || 1;
  let offset = 0;
  return (
    <div className="donut-wrap" style={{ width: size, height: size }}>
      <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
        <circle
          cx={size/2} cy={size/2} r={r}
          fill="none" stroke="var(--paper-3)" strokeWidth={thickness}
        />
        {data.map((d, i) => {
          const len = (d.value / total) * c;
          const dasharray = `${len} ${c - len}`;
          const dashoffset = -offset;
          offset += len;
          return (
            <circle
              key={i}
              cx={size/2} cy={size/2} r={r}
              fill="none"
              stroke={d.color}
              strokeWidth={thickness}
              strokeDasharray={dasharray}
              strokeDashoffset={dashoffset}
            />
          );
        })}
      </svg>
      <div className="donut-center">
        <div className="label">{centerLabel}</div>
        <div className="val">{centerValue}</div>
      </div>
    </div>
  );
}

// Stacked bar chart for month-over-month
function BarChart({ data, height = 180, currentIndex }) {
  const max = Math.max(...data.map(d => d.value)) * 1.1;
  const labelSpace = 22; // px reserved for x label + gap
  const trackHeight = height - labelSpace;
  return (
    <div className="bars" style={{ height }}>
      {data.map((d, i) => (
        <div key={i} className={`bar-col ${i === currentIndex ? 'current' : ''}`}>
          <div className="bar-stack" style={{ height: `${(d.value / max) * trackHeight}px` }}>
            <div className="bar-fill" style={{ height: '100%' }} />
          </div>
          <div className="x">{d.label}</div>
        </div>
      ))}
    </div>
  );
}

// Sparkline (small line chart)
function Sparkline({ data, width = 200, height = 40, color = 'var(--ink)' }) {
  const max = Math.max(...data) * 1.05;
  const min = Math.min(...data) * 0.95;
  const range = max - min || 1;
  const step = width / (data.length - 1);
  const points = data.map((v, i) => `${i * step},${height - ((v - min) / range) * height}`).join(' ');
  const areaPoints = `0,${height} ${points} ${width},${height}`;
  return (
    <svg width={width} height={height} style={{ display: 'block' }}>
      <polygon points={areaPoints} fill={color} opacity="0.1" />
      <polyline points={points} fill="none" stroke={color} strokeWidth="1.5" strokeLinejoin="round" />
      {data.map((v, i) => (
        <circle
          key={i}
          cx={i * step}
          cy={height - ((v - min) / range) * height}
          r={i === data.length - 1 ? 2.5 : 0}
          fill={color}
        />
      ))}
    </svg>
  );
}

// Cents-first amount input. Type digits to fill cents → tens → dollars; space appends a zero.
// `value` is a string of dollars (e.g. "12.34" or ""), matching existing modal state shape.
function AmountInput({ value, onChange, autoFocus, placeholder = '0.00', className = 'input amount' }) {
  // Mirror the dollar-string `value` prop into an internal digit string (cents).
  const digitsFromValue = (v) => {
    if (v === '' || v === null || v === undefined) return '';
    const n = parseFloat(v);
    if (isNaN(n)) return '';
    return Math.round(n * 100).toString();
  };
  const [digits, setDigits] = React.useState(() => digitsFromValue(value));

  // Resync if parent clears or replaces the value (e.g. modal reset).
  React.useEffect(() => {
    const fromProp = digitsFromValue(value);
    const fromState = digits;
    const sameNumber = (parseInt(fromProp || '0', 10) === parseInt(fromState || '0', 10));
    if (!sameNumber) setDigits(fromProp);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value]);

  const display = (() => {
    if (!digits) return '';
    const padded = digits.padStart(3, '0');
    const dollars = padded.slice(0, -2).replace(/^0+/, '') || '0';
    const cents = padded.slice(-2);
    return `${dollars}.${cents}`;
  })();

  const commit = (next) => {
    const trimmed = next.replace(/^0+/, '');
    setDigits(trimmed);
    onChange(trimmed ? (parseInt(trimmed, 10) / 100).toString() : '');
  };

  const onKeyDown = (e) => {
    if (e.metaKey || e.ctrlKey || e.altKey) return;
    if (e.key >= '0' && e.key <= '9') {
      e.preventDefault();
      commit((digits || '') + e.key);
    } else if (e.key === ' ') {
      e.preventDefault();
      if (digits) commit(digits + '0');
    } else if (e.key === 'Backspace') {
      e.preventDefault();
      commit((digits || '').slice(0, -1));
    } else if (e.key === 'Enter' || e.key === 'Tab' || e.key === 'Escape' ||
               e.key.startsWith('Arrow') || e.key === 'Home' || e.key === 'End') {
      // allow navigation/submit
    } else if (e.key.length === 1) {
      e.preventDefault();
    }
  };

  return (
    <input
      className={className}
      type="text"
      inputMode="numeric"
      autoFocus={autoFocus}
      value={display}
      onChange={() => {}}
      onKeyDown={onKeyDown}
      placeholder={placeholder}
    />
  );
}

// Read an image file, center-crop it to fit `targetW`x`targetH` (cover), return a JPEG data URL.
// Used for goal photos at 5:4 / 1350x1080 so they fill the goal banner without distortion.
function resizeImageFile(file, targetW = 1350, targetH = 1080, quality = 0.82) {
  return new Promise((resolve, reject) => {
    if (!file) return reject(new Error('no file'));
    if (!file.type.startsWith('image/')) return reject(new Error('not an image'));
    const reader = new FileReader();
    reader.onload = () => {
      const img = new Image();
      img.onload = () => {
        const srcRatio = img.width / img.height;
        const dstRatio = targetW / targetH;
        let sx, sy, sw, sh;
        if (srcRatio > dstRatio) {
          sh = img.height;
          sw = sh * dstRatio;
          sx = (img.width - sw) / 2;
          sy = 0;
        } else {
          sw = img.width;
          sh = sw / dstRatio;
          sx = 0;
          sy = (img.height - sh) / 2;
        }
        const scale = Math.min(1, targetW / sw);
        const w = Math.max(1, Math.round(targetW * scale));
        const h = Math.max(1, Math.round(targetH * scale));
        const canvas = document.createElement('canvas');
        canvas.width = w; canvas.height = h;
        canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, 0, 0, w, h);
        resolve(canvas.toDataURL('image/jpeg', quality));
      };
      img.onerror = () => reject(new Error('image decode failed'));
      img.src = reader.result;
    };
    reader.onerror = () => reject(new Error('file read failed'));
    reader.readAsDataURL(file);
  });
}

/* useSheet — drives the sheet/modal slide animation. Returns a `requestClose`
   function (call this instead of the bare `onClose` prop so the sheet plays
   its exit animation before unmounting) and a className flag. The 260ms must
   stay in sync with the .modal-backdrop.closing animations in app.css. */
const SHEET_EXIT_MS = 260;
function useSheet(onClose, exitMs) {
  const [closing, setClosing] = React.useState(false);
  const timerRef = React.useRef(null);
  React.useEffect(() => () => { if (timerRef.current) clearTimeout(timerRef.current); }, []);
  const delay = typeof exitMs === 'number' ? exitMs : SHEET_EXIT_MS;
  const requestClose = React.useCallback(() => {
    if (closing) return;
    setClosing(true);
    timerRef.current = setTimeout(onClose, delay);
  }, [onClose, closing, delay]);
  return [requestClose, closing ? 'closing' : ''];
}

/* Sheet — wrapper for inline-rendered modals that don't have their own
   component. Mirrors the useSheet animation lifecycle. Children may be a
   function `(requestClose) => ReactNode` so click handlers (Cancel, submit)
   can trigger the slide-down animation before the parent unmounts. */
function Sheet({ onClose, modalClassName = 'modal', modalStyle, children }) {
  const [requestClose, sheetClass] = useSheet(onClose);
  return (
    <div className={`modal-backdrop ${sheetClass}`} onClick={requestClose}>
      <div className={modalClassName} onClick={e => e.stopPropagation()} style={modalStyle}>
        {typeof children === 'function' ? children(requestClose) : children}
      </div>
    </div>
  );
}

Object.assign(window, { Tag, Donut, BarChart, Sparkline, AmountInput, resizeImageFile, useSheet, Sheet });
