/* ui.jsx — shared primitives: icons, buttons, editable fields, modal, flourishes. */

// ── Icon set (stroke-based, 24×24, currentColor) ────────────────────────────
const ICON_PATHS = {
  users: '<path d="M16 18v-1a4 4 0 0 0-4-4H7a4 4 0 0 0-4 4v1"/><circle cx="9.5" cy="7" r="3.2"/><path d="M17 13a4 4 0 0 1 4 4v1"/><path d="M16 4a3.2 3.2 0 0 1 0 6"/>',
  spark: '<path d="M12 2l1.6 6.4L20 10l-6.4 1.6L12 18l-1.6-6.4L4 10l6.4-1.6z"/><path d="M19 15l.7 2.3L22 18l-2.3.7L19 21l-.7-2.3L16 18l2.3-.7z"/>',
  scroll: '<path d="M6 4h11a2 2 0 0 1 2 2v11"/><path d="M6 4a2 2 0 0 0-2 2v1a2 2 0 0 0 2 2h2"/><path d="M8 9v9a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-1H10"/>',
  key: '<circle cx="7.5" cy="14.5" r="3.5"/><path d="M10 12l8.5-8.5"/><path d="M16 6l2.5 2.5"/><path d="M19 3l2 2"/>',
  map: '<path d="M9 4 3 6v14l6-2 6 2 6-2V4l-6 2-6-2z"/><path d="M9 4v14"/><path d="M15 6v14"/>',
  mask: '<path d="M4 5s2-1 8-1 8 1 8 1v6a8 8 0 0 1-16 0V5z"/><path d="M8.5 10c.8.8 2 .8 2.8 0"/><path d="M12.7 10c.8.8 2 .8 2.8 0"/><path d="M9 15c1.5 1.2 4.5 1.2 6 0"/>',
  sword: '<path d="M14.5 3.5 21 3l-.5 6.5-9 9-3 1-1-3 9-9z"/><path d="m6 14-3 3 4 4 3-3"/><path d="m13 11 3 3"/>',
  gem: '<path d="M6 3h12l3 6-9 12L3 9z"/><path d="M3 9h18"/><path d="M9 3 7.5 9 12 21l4.5-12L15 3"/>',
  plus: '<path d="M12 5v14M5 12h14"/>',
  trash: '<path d="M4 7h16"/><path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/><path d="M6 7l1 13a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-13"/>',
  play: '<path d="M7 4.5v15l13-7.5z"/>',
  pen: '<path d="M14 4 20 10 9 21H3v-6z"/><path d="m13 5 6 6"/>',
  book: '<path d="M4 5a2 2 0 0 1 2-2h13v16H6a2 2 0 0 0-2 2z"/><path d="M19 19a2 2 0 0 0-2-2H6"/>',
  sparkles: '<path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z"/><path d="M5 16l.8 2.2L8 19l-2.2.8L5 22l-.8-2.2L2 19l2.2-.8z"/>',
  eye: '<path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/>',
  eyeoff: '<path d="M9.9 5.2A9.5 9.5 0 0 1 12 5c6 0 10 7 10 7a16 16 0 0 1-3.2 3.8"/><path d="M6 7.4A15.7 15.7 0 0 0 2 12s4 7 10 7a9.4 9.4 0 0 0 4-.9"/><path d="m3 3 18 18"/><path d="M9.5 9.6a3 3 0 0 0 4.2 4.2"/>',
  back: '<path d="M19 12H5"/><path d="m12 19-7-7 7-7"/>',
  cal: '<rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 9h18M8 3v4M16 3v4"/>',
  check: '<path d="M5 12.5 10 17 19 6"/>',
  x: '<path d="M6 6l12 12M18 6 6 18"/>',
  dots: '<circle cx="5" cy="12" r="1.4"/><circle cx="12" cy="12" r="1.4"/><circle cx="19" cy="12" r="1.4"/>',
  grip: '<circle cx="9" cy="6" r="1.3"/><circle cx="15" cy="6" r="1.3"/><circle cx="9" cy="12" r="1.3"/><circle cx="15" cy="12" r="1.3"/><circle cx="9" cy="18" r="1.3"/><circle cx="15" cy="18" r="1.3"/>',
  copy: '<rect x="8" y="8" width="13" height="13" rx="2"/><path d="M5 16a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2"/>',
  chevron: '<path d="m9 6 6 6-6 6"/>',
  flame: '<path d="M12 3s5 4 5 9a5 5 0 0 1-10 0c0-2 1-3 1-3s0 2 1.5 2S12 7 12 3z"/>',
  clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
  feather: '<path d="M20 4C10 4 4 11 4 20l4-4h7a5 5 0 0 0 5-5z"/><path d="M16 8 8 16"/>',
  settings: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>',
  import: '<path d="M12 4v12M8 12l4 4 4-4M4 19h16"/>',
  image: '<rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 15 5-5 4 4 3-3 6 6"/><circle cx="8.5" cy="9.5" r="1.5"/>',
};

function Icon({ name, size = 20, sw = 1.7, style, className }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
    stroke="currentColor" strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round"
    className={className} style={style} aria-hidden="true"
    dangerouslySetInnerHTML={{ __html: ICON_PATHS[name] || "" }} />
  );
}

// ── Decorative flourish — a thin filigree divider ───────────────────────────
function Flourish({ w = 120 }) {
  return (
    <svg className="flourish" width={w} height="14" viewBox="0 0 120 14" fill="none"
    stroke="currentColor" strokeWidth="1" aria-hidden="true">
      <path d="M2 7h36" /><path d="M118 7H82" />
      <path d="M44 7c0-3 3-3 3 0s-3 3-3 0" /><path d="M76 7c0 3-3 3-3 0s3-3 3 0" />
      <path d="M50 7h20" /><circle cx="60" cy="7" r="2.4" />
    </svg>
  );
}

// ── Buttons ──────────────────────────────────────────────────────────────────
function Button({ children, onClick, variant = "ghost", icon, size = "md", title, type = "button", style }) {
  return (
    <button type={type} className={`btn btn-${variant} btn-${size}`} onClick={onClick} title={title} style={style}>
      {icon && <Icon name={icon} size={size === "sm" ? 15 : 17} sw={1.9} />}
      {children && <span>{children}</span>}
    </button>
  );
}

function IconButton({ name, onClick, title, danger, size = 16, active }) {
  return (
    <button type="button" className={`icon-btn${danger ? " danger" : ""}${active ? " active" : ""}`} onClick={onClick} title={title} aria-label={title}>
      <Icon name={name} size={size} sw={1.8} />
    </button>
  );
}

// ── Editable fields (contentEditable-free; auto-growing textarea / input) ────
function EditableLine({ value, placeholder, onChange, className, big, ...rest }) {
  return (
    <input
      className={`ed-line${big ? " ed-line-big" : ""} ${className || ""}`}
      value={value || ""}
      placeholder={placeholder}
      onChange={(e) => onChange(e.target.value)}
      spellCheck={false}
      {...rest} />
  );
}

function EditableArea({ value, placeholder, onChange, className, minRows = 2, ...rest }) {
  const [focused, setFocused] = React.useState(false);
  const focusPending = React.useRef(false);

  const textareaRef = React.useCallback((el) => {
    if (el && focusPending.current) {
      focusPending.current = false;
      el.focus();
      const len = (el.value || "").length;
      el.selectionStart = el.selectionEnd = len;
      el.style.height = "auto";
      el.style.height = el.scrollHeight + "px";
    }
  }, []);

  const fit = React.useCallback((el) => {
    if (!el) return;
    el.style.height = "auto";
    el.style.height = el.scrollHeight + "px";
  }, []);

  const handleKeyDown = (e) => {
    if (e.key !== "Enter") return;
    const ta = e.currentTarget;
    const pos = ta.selectionStart;
    const text = ta.value;
    const lineStart = text.lastIndexOf("\n", pos - 1) + 1;
    const line = text.slice(lineStart, pos);
    if (line === "- ") {
      e.preventDefault();
      onChange(text.slice(0, lineStart) + text.slice(pos));
    } else if (line.startsWith("- ")) {
      e.preventDefault();
      const next = text.slice(0, pos) + "\n- " + text.slice(pos);
      onChange(next);
      requestAnimationFrame(() => {
        ta.selectionStart = ta.selectionEnd = pos + 3;
        fit(ta);
      });
    }
  };

  if (!focused && value && value.trim()) {
    const lines = value.split("\n");
    return (
      <div
        className={`ed-area-display ${className || ""}`}
        tabIndex={0}
        onClick={() => { focusPending.current = true; setFocused(true); }}
        onFocus={() => { focusPending.current = true; setFocused(true); }}>
        {lines.map((line, i) =>
          line.startsWith("- ") ? (
            <div key={i} className="bullet-line">
              <span className="bullet-dot">•</span>
              <span>{line.slice(2) || " "}</span>
            </div>
          ) : (
            <div key={i} className={`plain-line${!line ? " empty-line" : ""}`}>{line || " "}</div>
          )
        )}
      </div>
    );
  }

  return (
    <textarea
      ref={textareaRef}
      className={`ed-area ${className || ""}`}
      value={value || ""}
      placeholder={placeholder}
      rows={minRows}
      onChange={(e) => { onChange(e.target.value); fit(e.currentTarget); }}
      onKeyDown={handleKeyDown}
      onInput={(e) => fit(e.currentTarget)}
      onBlur={() => setFocused(false)}
      {...rest} />
  );
}

// ── Aspects editor — chips of evocative descriptors ─────────────────────────
function AspectsEditor({ value, onChange, placeholder }) {
  const list = Array.isArray(value) ? value : [];
  const [draft, setDraft] = React.useState("");
  const add = () => {
    const v = draft.trim();
    if (!v) return;
    onChange([...list, v]);
    setDraft("");
  };
  return (
    <div className="aspects">
      {list.map((a, i) =>
        <span className="aspect-chip" key={i}>
          {a}
          <button type="button"
            style={{ display:"flex", alignItems:"center", justifyContent:"center", width:18, height:18, minWidth:18, minHeight:18, flexShrink:0, background:"none", border:0, padding:0, opacity:0.55, borderRadius:"50%", cursor:"pointer", color:"inherit" }}
            onClick={() => onChange(list.filter((_, j) => j !== i))} aria-label="Remove aspect">
            <Icon name="x" size={11} sw={2.4} />
          </button>
        </span>
      )}
      <input
        className="aspect-input"
        value={draft}
        placeholder={list.length ? "add aspect…" : placeholder || "add aspect…"}
        onChange={(e) => setDraft(e.target.value)}
        onKeyDown={(e) => { if (e.key === "Enter" || e.key === ",") { e.preventDefault(); add(); } }}
        onBlur={add} />
    </div>
  );
}

// ── Modal shell ──────────────────────────────────────────────────────────────
function Modal({ title, onClose, children, width = 460 }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);
  return (
    <div className="modal-scrim" onMouseDown={onClose}>
      <div className="modal" style={{ maxWidth: width }} onMouseDown={(e) => e.stopPropagation()}>
        <div className="modal-hd">
          <h3>{title}</h3>
          <IconButton name="x" onClick={onClose} title="Close" size={18} />
        </div>
        <div className="modal-body">{children}</div>
      </div>
    </div>
  );
}

// ── ConfirmDelete: inline "Sure? Delete | Cancel" on the trash button ─────────
function ConfirmDelete({ onConfirm, label = "Delete" }) {
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open]);
  if (!open) {
    return (
      <button type="button" className="icon-btn danger" title={label}
        onClick={(e) => { e.stopPropagation(); setOpen(true); }}>
        <Icon name="trash" size={16} sw={1.8} />
      </button>
    );
  }
  return (
    <span className="confirm-delete" onMouseDown={(e) => e.stopPropagation()}>
      <span className="confirm-label">Delete?</span>
      <button type="button" className="confirm-yes"
        onClick={(e) => { e.stopPropagation(); setOpen(false); onConfirm(); }}>Yes</button>
      <button type="button" className="confirm-no"
        onClick={(e) => { e.stopPropagation(); setOpen(false); }}>No</button>
    </span>
  );
}

Object.assign(window, {
  Icon, Flourish, Button, IconButton,
  EditableLine, EditableArea, AspectsEditor, Modal, ConfirmDelete,
});
