// shared.jsx — icons, modal, small helpers. Exported to window.
const { useState, useEffect, useRef, useCallback } = React;

/* ---- Simple stroke icons ---- */
const ICONS = {
  plus:    "M12 5v14M5 12h14",
  minus:   "M5 12h14",
  check:   "M4 12.5l5 5L20 6",
  x:       "M6 6l12 12M18 6L6 18",
  search:  "M11 19a8 8 0 100-16 8 8 0 000 16zM21 21l-4.3-4.3",
  user:    "M12 12a4 4 0 100-8 4 4 0 000 8zM5 20c.8-3.5 3.6-5.5 7-5.5s6.2 2 7 5.5",
  users:   "M9 11a3.5 3.5 0 100-7 3.5 3.5 0 000 7zM2.5 19c.6-3 2.7-4.6 6.5-4.6S15 16 15.6 19M16 11.2A3.2 3.2 0 1016 5M18 14.4c2.6.4 4 1.9 4.4 4.6",
  grid:    "M4 6h16M4 12h16M4 18h16",
  trash:   "M5 7h14M9 7V5h6v2M7 7l1 13h8l1-13",
  copy:    "M9 9h10v10H9zM5 15V5h10",
  edit:    "M5 19h14M14 5l4 4-9 9H5v-4z",
  chevR:   "M9 6l6 6-6 6",
  chevL:   "M15 6l-6 6 6 6",
  chevD:   "M6 9l6 6 6-6",
  back:    "M11 6l-6 6 6 6M5 12h14",
  cal:     "M4 7h16v13H4zM4 7V5h16v2M8 3v4M16 3v4",
  dots:    "M6 12h.01M12 12h.01M18 12h.01",
  warn:    "M12 3l9 16H3zM12 10v4M12 17.5h.01",
  star:    "M12 4l2.4 5 5.6.7-4 3.9 1 5.5-5-2.7-5 2.7 1-5.5-4-3.9 5.6-.7z",
  zoomIn:  "M11 19a8 8 0 100-16 8 8 0 000 16zM21 21l-4.3-4.3M11 8v6M8 11h6",
  zoomOut: "M11 19a8 8 0 100-16 8 8 0 000 16zM21 21l-4.3-4.3M8 11h6",
  fit:     "M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5",
  rotate:  "M4 12a8 8 0 108-8M4 4v4h4",
  download:"M12 4v11M7 11l5 5 5-5M5 20h14",
  print:   "M7 8V4h10v4M7 17H5V9h14v8h-2M7 14h10v6H7z",
  list:    "M8 6h12M8 12h12M8 18h12M4 6h.01M4 12h.01M4 18h.01",
  table:   "M4 9h16M9 4v16M4 6.5A2.5 2.5 0 016.5 4h11A2.5 2.5 0 0120 6.5v11a2.5 2.5 0 01-2.5 2.5h-11A2.5 2.5 0 014 17.5z",
  settings:"M12 15a3 3 0 100-6 3 3 0 000 6zM4 12l-1.2.5.9 2.3 1.3-.3a7 7 0 001 1l-.2 1.4 2.3.9.7-1.2a7 7 0 001.4 0l.7 1.2 2.3-.9-.2-1.4a7 7 0 001-1l1.3.3.9-2.3L20 12l.8-.5-.9-2.3-1.3.3a7 7 0 00-1-1l.2-1.4-2.3-.9-.7 1.2a7 7 0 00-1.4 0l-.7-1.2-2.3.9.2 1.4a7 7 0 00-1 1L4.9 9 4 11.3z",
  sparkle: "M12 3l1.6 4.8L18 9.5l-4.4 1.7L12 16l-1.6-4.8L6 9.5l4.4-1.7zM18 14l.8 2.2L21 17l-2.2.8L18 20l-.8-2.2L15 17l2.2-.8zM5.5 14l.6 1.6L7.7 16l-1.6.6L5.5 18l-.6-1.6L3.3 16l1.6-.4z",
  link:    "M9 15l6-6M10 6l1-1a4 4 0 016 6l-1 1M14 18l-1 1a4 4 0 01-6-6l1-1",
  unlink:  "M9 15l6-6M8 8L6 6m0 12l2-2m8-8l2-2M16 16l2 2",
  heart:   "M12 20s-7-4.5-7-9.5A3.5 3.5 0 0112 7a3.5 3.5 0 017 3.5C19 15.5 12 20 12 20z",
  pin:     "M12 21s6-5.3 6-10a6 6 0 10-12 0c0 4.7 6 10 6 10zM12 13a2.5 2.5 0 100-5 2.5 2.5 0 000 5z",
  baby:    "M9 10h.01M15 10h.01M9.5 14c.7.6 1.6 1 2.5 1s1.8-.4 2.5-1M12 3a4 4 0 014 4M5 12a7 7 0 0114 0v1a7 7 0 01-14 0z",
  leaf:    "M5 19c0-7 5-12 14-13 0 9-5 14-12 14a6 6 0 01-2-1zM5 19c2-4 4-6 8-8",
  image:   "M5 5h14a1 1 0 011 1v12a1 1 0 01-1 1H5a1 1 0 01-1-1V6a1 1 0 011-1zM8.5 11a1.5 1.5 0 100-3 1.5 1.5 0 000 3M4 16l5-4 4 3 3-2 4 3",
};

function Icon({ name, size = 22, sw = 1.9, style, color }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
         stroke={color || "currentColor"} strokeWidth={sw}
         strokeLinecap="round" strokeLinejoin="round" style={{ flex: 'none', ...style }}>
      <path d={ICONS[name] || ICONS.x} />
    </svg>
  );
}

/* ---- Logo: et rundt bord set oppefra med pladser (prikker) rundt om ---- */
function LogoMark({ size = 30 }) {
  // 6 pladser jævnt fordelt om bordet.
  const seats = Array.from({ length: 6 }, (_, i) => {
    const a = (i / 6) * Math.PI * 2 - Math.PI / 2;
    return { cx: 12 + Math.cos(a) * 7.4, cy: 12 + Math.sin(a) * 7.4 };
  });
  return (
    <div style={{
      width: size, height: size, borderRadius: size * 0.34,
      background: 'linear-gradient(150deg, var(--accent) 35%, var(--honey))',
      display: 'grid', placeItems: 'center', boxShadow: '0 2px 6px rgba(184,104,67,.32)',
      flex: 'none',
    }}>
      <svg width={size * 0.7} height={size * 0.7} viewBox="0 0 24 24">
        {/* bordplade */}
        <circle cx="12" cy="12" r="5" fill="none" stroke="#fff" strokeWidth="2" />
        {/* pladser rundt om */}
        {seats.map((s, i) => <circle key={i} cx={s.cx} cy={s.cy} r="1.7" fill="#fff" />)}
      </svg>
    </div>
  );
}

function Logo({ size = 30 }) {
  return (
    <div className="row" style={{ gap: 10, alignItems: 'center' }}>
      <LogoMark size={size} />
      <span style={{ fontFamily: 'var(--font-disp)', fontWeight: 600, fontSize: size * 0.74, color: 'var(--ink)', letterSpacing: '-.02em' }}>Planiq</span>
    </div>
  );
}

/* ---- Avatar with initials ---- */
function initialsOf(name) {
  const clean = (name || '').replace(/\(.*?\)/g, '').trim();
  const parts = clean.split(/\s+/).filter(Boolean);
  if (!parts.length) return '?';
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}

function Avatar({ name, group, size = 38, child }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', flex: 'none',
      display: 'grid', placeItems: 'center',
      background: group ? `var(--grp-${group})` : 'var(--ink-faint)',
      color: '#fff', fontWeight: 800, fontSize: size * 0.36,
      fontFamily: 'var(--font-disp)',
      boxShadow: 'inset 0 0 0 2px rgba(255,255,255,.35)',
      position: 'relative',
    }}>
      {initialsOf(name)}
      {child && <span style={{
        position: 'absolute', bottom: -2, right: -2,
        background: '#fff', borderRadius: '50%', width: size * 0.44, height: size * 0.44,
        display: 'grid', placeItems: 'center', boxShadow: '0 1px 3px rgba(0,0,0,.2)',
      }}><Icon name="baby" size={size * 0.3} color="var(--accent-deep)" sw={2.2} /></span>}
    </div>
  );
}

/* ---- Modal ----
   Renderes via portal til <body>, så scrim/dialog ikke påvirkes af forældres
   transform/overflow (fx det zoom-/pan-transformerede bordlærred). */
function Modal({ onClose, children, maxWidth = 460 }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose && onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  const content = (
    <div className="scrim" onPointerDown={(e) => { if (e.target === e.currentTarget) onClose && onClose(); }}>
      <div className="modal" style={{ maxWidth }} onPointerDown={(e) => e.stopPropagation()}>
        {children}
      </div>
    </div>
  );
  return ReactDOM.createPortal(content, document.body);
}

/* ---- Confirm dialog ---- */
function Confirm({ title, body, confirmLabel = "Slet", danger, onConfirm, onClose }) {
  return (
    <Modal onClose={onClose} maxWidth={400}>
      <h2 style={{ marginBottom: 8 }}>{title}</h2>
      {body && <p className="muted" style={{ fontSize: 16, lineHeight: 1.5, marginBottom: 22 }}>{body}</p>}
      <div className="row gap-3" style={{ justifyContent: 'flex-end' }}>
        <button className="btn btn-ghost" onClick={onClose}>Annullér</button>
        <button className={"btn " + (danger ? "btn-primary" : "btn-sage")}
          style={danger ? { background: 'var(--warn)' } : {}}
          onClick={() => { onConfirm(); onClose(); }}>{confirmLabel}</button>
      </div>
    </Modal>
  );
}

/* ---- Segmented toggle ---- */
function Segmented({ value, options, onChange, size }) {
  return (
    <div className="row" style={{
      background: 'var(--surface-2)', borderRadius: 'var(--radius-pill)', padding: 4,
      gap: 2, border: '1.5px solid var(--line)',
    }}>
      {options.map(o => {
        const active = o.id === value;
        return (
          <button key={o.id} onClick={() => onChange(o.id)} style={{
            minHeight: size === 'sm' ? 34 : 40, padding: '0 16px', borderRadius: 'var(--radius-pill)',
            fontFamily: 'var(--font-disp)', fontWeight: 500, fontSize: size === 'sm' ? 14 : 15,
            background: active ? 'var(--surface)' : 'transparent',
            color: active ? 'var(--ink)' : 'var(--ink-soft)',
            boxShadow: active ? 'var(--shadow-sm)' : 'none',
            transition: 'all .16s var(--ease)',
            display: 'flex', alignItems: 'center', gap: 7,
          }}>{o.icon && <Icon name={o.icon} size={16} />}{o.label}</button>
        );
      })}
    </div>
  );
}

function fmtDate(iso) {
  if (!iso) return '';
  const d = new Date(iso + 'T00:00');
  const M = ['jan.','feb.','mar.','apr.','maj','jun.','jul.','aug.','sep.','okt.','nov.','dec.'];
  return d.getDate() + '. ' + M[d.getMonth()] + ' ' + d.getFullYear();
}
function relTime(ts) {
  const diff = Date.now() - ts, h = diff / 3.6e6;
  if (h < 1) return 'for nylig';
  if (h < 24) return 'redigeret i dag';
  const d = Math.floor(h / 24);
  if (d === 1) return 'redigeret i går';
  return `redigeret for ${d} dage siden`;
}

/* ---- Billed-upload: læs en fil, skalér ned og returnér en data-URL ----
   Holder coverbilledet lille nok til at ligge i Firestore-dokumentet (< 1 MB pr. dok). */
function readImageScaled(file, { maxSize = 800, quality = 0.78 } = {}) {
  return new Promise((resolve, reject) => {
    if (!file || !file.type.startsWith('image/')) { reject(new Error('Filen er ikke et billede.')); return; }
    const reader = new FileReader();
    reader.onerror = () => reject(new Error('Kunne ikke læse filen.'));
    reader.onload = () => {
      const img = new Image();
      img.onerror = () => reject(new Error('Billedet kunne ikke indlæses.'));
      img.onload = () => {
        const scale = Math.min(1, maxSize / Math.max(img.width, img.height));
        const w = Math.round(img.width * scale);
        const h = Math.round(img.height * scale);
        const canvas = document.createElement('canvas');
        canvas.width = w; canvas.height = h;
        canvas.getContext('2d').drawImage(img, 0, 0, w, h);
        resolve(canvas.toDataURL('image/jpeg', quality));
      };
      img.src = reader.result;
    };
    reader.readAsDataURL(file);
  });
}

/* ---- CoverPicker: knap der vælger/skifter coverbillede + fjern-knap ---- */
function CoverPicker({ value, onChange, height = 150 }) {
  const inputRef = useRef(null);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');

  async function pick(e) {
    const file = e.target.files && e.target.files[0];
    e.target.value = ''; // tillad valg af samme fil igen
    if (!file) return;
    setErr(''); setBusy(true);
    try {
      onChange(await readImageScaled(file));
    } catch (ex) {
      setErr(ex.message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div>
      <input ref={inputRef} type="file" accept="image/*" onChange={pick} style={{ display: 'none' }} />
      {value ? (
        <div style={{ position: 'relative', borderRadius: 16, overflow: 'hidden', height }}>
          <img src={value} alt="Coverbillede" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
          <div className="row gap-2" style={{ position: 'absolute', top: 10, right: 10 }}>
            <button className="btn btn-sm" style={{ background: 'rgba(255,255,255,.92)', border: 'none' }}
              onClick={() => inputRef.current.click()} disabled={busy}>
              <Icon name="edit" size={16} /> Skift
            </button>
            <button className="btn btn-sm btn-icon" style={{ background: 'rgba(255,255,255,.92)', border: 'none', color: 'var(--warn)' }}
              aria-label="Fjern billede" onClick={() => onChange(null)}>
              <Icon name="trash" size={16} />
            </button>
          </div>
        </div>
      ) : (
        <button onClick={() => inputRef.current.click()} disabled={busy} style={{
          width: '100%', height, borderRadius: 16, border: '2px dashed var(--line-2)',
          background: 'var(--surface-2)', display: 'flex', flexDirection: 'column', gap: 8,
          alignItems: 'center', justifyContent: 'center', color: 'var(--ink-soft)',
        }}>
          <Icon name="image" size={28} color="var(--accent)" />
          <span style={{ fontWeight: 800, fontSize: 15 }}>{busy ? 'Indlæser…' : 'Vælg coverbillede'}</span>
          <span className="faint" style={{ fontSize: 13 }}>JPG eller PNG</span>
        </button>
      )}
      {err && <p style={{ color: 'var(--warn)', fontSize: 13.5, marginTop: 8 }}>{err}</p>}
    </div>
  );
}

Object.assign(window, { Icon, Logo, LogoMark, Avatar, initialsOf, Modal, Confirm, Segmented, fmtDate, relTime, readImageScaled, CoverPicker });
