/* Eno.Health Console — Knowledge base: Wearables & Wearable Inputs.
   A Wearable Input (e.g. VO₂ Max) is a signal Eno can ingest. Each input is
   linked to the supported Wearables (devices / aggregators) that provide it,
   and becomes available to map onto Exposome factors. */
const W = window.ENO;

const STREAM_MODES = {
  continuous: { label: "Continuous", tone: "ok" },
  batched: { label: "Batched", tone: "info" },
  manual: { label: "Manual", tone: "neutral" },
};
const streamMode = (k) => STREAM_MODES[k] || { label: k || "—", tone: "neutral" };
const hueFor = (k) => { let n = 0; for (const c of String(k)) n = (n * 31 + c.charCodeAt(0)) % 360; return n; };

function WearablesView() {
  const toast = useToast();
  const [inputs, setInputs] = useState(() => W.SIGNAL_TYPES.map((s) => ({ ...s, sources: [...(s.sources || [])] })));
  const [sources] = useState(() => W.STREAM_SOURCES.map((s) => ({ ...s })));
  const [q, setQ] = useState("");
  const [editing, setEditing] = useState(null);       // input editor { isNew, key, label, unit, range, sources }
  const [selWearable, setSelWearable] = useState(null);
  const [removing, setRemoving] = useState(null);     // input pending deletion (has active mappings)

  const usageOf = (key) => (W.EXPOSOME_FACTORS || []).filter((f) => (f.signals || []).includes(key)).length;
  const wearableUse = (srcKey) => inputs.filter((i) => i.sources.includes(srcKey)).length;
  const rows = inputs.filter((i) => !q || (i.label + " " + i.key + " " + i.unit).toLowerCase().includes(q.toLowerCase()));

  const saveInput = (data) => {
    if (data.isNew) { setInputs((xs) => [...xs, { key: data.key, label: data.label, category: data.category, unit: data.unit, range: data.range, sources: data.sources }]); toast("Wearable input “" + data.label + "” added", "ok"); }
    else { setInputs((xs) => xs.map((i) => i.key === data.key ? { ...i, label: data.label, category: data.category, unit: data.unit, range: data.range, sources: data.sources } : i)); toast("Wearable input “" + data.label + "” updated", "ok"); }
    setEditing(null);
  };
  const removeInput = (key) => { const i = inputs.find((x) => x.key === key); setInputs((xs) => xs.filter((x) => x.key !== key)); toast("Wearable input “" + (i ? i.label : key) + "” deleted"); };

  const head = ["Wearable input", "Signal type", "Identifier", "Unit", "Detected range", "Supported wearables", "Exposome use", ""];
  return h("div", { className: "view" },
    h(PageHead, { title: "Wearables",
      sub: "Wearable inputs Eno ingests and the supported devices that provide them — the signals available to map onto the Exposome" }),

    h("div", { className: "ref-banner" }, h(Icon, { name: "smartphone", size: 16, className: "dim" }),
      h("div", null, "A ", h("strong", null, "Wearable input"), " (e.g. ", h("strong", null, "VO₂ Max"), ") is a signal Eno can ingest. Each input is linked to the ",
        h("strong", null, "supported wearables"), " that provide it, and becomes available to map onto ", h("strong", null, "exposome factors"), ".")),

    h("div", { className: "stat-grid stat-grid-3" },
      h(Card, { pad: false }, h(Stat, { label: "Wearable inputs", value: inputs.length, icon: "activity" })),
      h(Card, { pad: false }, h(Stat, { label: "Supported wearables", value: sources.length, icon: "smartphone" })),
      h(Card, { pad: false }, h(Stat, { label: "Mapped to exposome", value: inputs.filter((i) => usageOf(i.key) > 0).length, tone: "ok", icon: "globe" }))),

    h(Card, { pad: false, title: "Supported wearables", subtitle: "Devices and aggregators Eno can ingest from — click one to see its supported signals" },
      h("div", { className: "wear-grid" }, sources.map((s) => {
        const n = wearableUse(s.key);
        const mode = streamMode(s.streamMode);
        return h("div", { className: "wear-card", key: s.key, onClick: () => setSelWearable(s) },
          h("span", { className: "wear-glyph", style: { background: "oklch(0.62 0.13 " + hueFor(s.key) + ")", color: "#fff", borderColor: "transparent" } }, s.glyph || (s.name || "?")[0]),
          h("div", { className: "wear-main" },
            h("span", { className: "wear-name" }, s.name),
            h("span", { className: "wear-kind" }, s.kind, " · ", n, " input", n === 1 ? "" : "s"),
            h("span", { className: "wear-mode" }, h(Pill, { tone: mode.tone, soft: true }, mode.label, " streaming")),
            s.refreshRate ? h("span", { className: "wear-refresh" }, h(Icon, { name: "refresh", size: 12 }), "Refresh: ", s.refreshRate) : null));
      }))),

    h(Card, { pad: false, title: "Wearable inputs", subtitle: "Signals available to map onto exposome factors and biomarkers",
      actions: h(Button, { variant: "primary", size: "sm", icon: "plus", onClick: () => setEditing({ isNew: true, key: "", label: "", category: "", unit: "", range: "", sources: [] }) }, "New input") },
      h("div", { className: "toolbar" },
        h("div", { className: "search" }, h(Icon, { name: "search", size: 16 }),
          h("input", { className: "search-input", placeholder: "Search input, identifier or unit…", value: q, onChange: (e) => setQ(e.target.value) })),
        h("span", { className: "toolbar-count" }, rows.length, " of ", inputs.length)),
      h("div", { className: "table-wrap flush" },
        h("table", { className: "table" },
          h("thead", null, h("tr", null, head.map((c, i) => h("th", { key: i, className: i === 6 ? "ta-c" : (i === 7 ? "ta-r" : "") }, c)))),
          h("tbody", null,
            rows.map((i) => {
              const use = usageOf(i.key);
              const openEdit = () => setEditing({ isNew: false, key: i.key, label: i.label, category: i.category || "", unit: i.unit, range: i.range, sources: [...i.sources] });
              return h("tr", { key: i.key, className: "row-click", onClick: openEdit },
                h("td", null, h("span", { style: { fontWeight: 600 } }, i.label)),
                h("td", null, i.category ? h("span", { className: "tag" }, i.category) : h("span", { className: "dim" }, "—")),
                h("td", null, h("span", { className: "mono dim" }, i.key)),
                h("td", null, h("span", { className: "tag" }, i.unit)),
                h("td", { className: "mono dim" }, i.range),
                h("td", null, i.sources.length
                  ? h("span", { className: "tags" }, i.sources.slice(0, 3).map((sk) => h("span", { className: "tag", key: sk }, W.sourceName(sk))),
                      i.sources.length > 3 ? h("span", { className: "tag" }, "+" + (i.sources.length - 3)) : null)
                  : h("span", { className: "dim" }, "—")),
                h("td", { className: "ta-c" }, use ? h("span", { className: "mono" }, use) : h("span", { className: "dim" }, "0")),
                h("td", { className: "ta-r" }, h("div", { className: "row-actions", onClick: (e) => e.stopPropagation() },
                  h("button", { className: "mini-btn", title: "Edit input", onClick: openEdit }, h(Icon, { name: "edit", size: 15 })),
                  h("button", { className: "mini-btn danger", title: "Delete input", onClick: () => { if (usageOf(i.key) > 0) setRemoving(i); else removeInput(i.key); } }, h(Icon, { name: "trash", size: 15 })))));
            }),
            !rows.length && h("tr", null, h("td", { colSpan: 8 }, h(Empty, { icon: "activity", title: "No wearable inputs", sub: "Adjust your search." }))))))),

    h(WearableInputModal, { data: editing, existing: inputs, sources, onClose: () => setEditing(null), onSave: saveInput }),
    h(WearableDrawer, { wearable: selWearable, inputs, onClose: () => setSelWearable(null) }),
    h(DeleteInputModal, { input: removing, onClose: () => setRemoving(null), onConfirm: () => { removeInput(removing.key); setRemoving(null); } }));
}

function DeleteInputModal({ input, onClose, onConfirm }) {
  if (!input) return null;
  const affected = (W.EXPOSOME_FACTORS || []).filter((f) => (f.signals || []).includes(input.key));
  const n = affected.length;
  return h(Modal, { open: true, onClose, title: "Delete wearable input", width: 540,
    footer: h(React.Fragment, null,
      h(Button, { variant: "ghost", onClick: onClose }, "Cancel"),
      h(Button, { variant: "danger", icon: "trash", onClick: onConfirm }, "Unmap & delete")) },
    h("div", { className: "mform" },
      h("div", { className: "warn-banner" }, h(Icon, { name: "alert", size: 16 }),
        h("div", null, "Deleting the wearable input ", h("strong", null, "“" + input.label + "”"), " will unmap it from ",
          h("strong", null, n, " exposome factor", n === 1 ? "" : "s"), ". Those exposome mappings will be removed — this cannot be undone.")),
      h("div", { className: "refsec" },
        h("p", { className: "refsec-title" }, "Affected exposome factors (" + n + ")"),
        h("div", { className: "tags" }, affected.map((f) => h("span", { className: "tag", key: f.id }, f.label)))),
      h("p", { className: "mform-note" }, h(Icon, { name: "info", size: 14 }),
        "The exposome factors themselves are kept — only their mapping to this wearable input is removed.")));
}

function WearableDrawer({ wearable, inputs, onClose }) {
  if (!wearable) return h(Drawer, { open: false, onClose });
  const signals = inputs.filter((i) => i.sources.includes(wearable.key));
  const mode = streamMode(wearable.streamMode);
  const head = ["Signal name", "Signal type", "Identifier", "Unit", "Detected range"];
  return h(Drawer, { open: true, onClose, width: 660, title: wearable.name,
    sub: h(React.Fragment, null,
      h(Pill, { tone: "neutral", soft: true }, wearable.kind),
      h(Pill, { tone: mode.tone, soft: true }, mode.label, " streaming")) },
    h("div", { className: "refbox" },
      h("div", { className: "meta-grid" },
        h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Type"), h("span", { className: "meta-v" }, wearable.kind)),
        h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Data streaming mode"), h("span", { className: "meta-v" }, mode.label)),
        h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Data refresh rate"), h("span", { className: "meta-v" }, wearable.refreshRate || "—"))),

      h("div", { className: "refsec" },
        h("div", { className: "refsec-head" },
          h("p", { className: "refsec-title" }, "Supported signals"),
          h("span", { className: "toolbar-count" }, signals.length, " signal", signals.length === 1 ? "" : "s")),
        h("p", { className: "field-hint", style: { margin: "-2px 0 8px" } }, "Read-only — manage which signals a wearable provides from each input’s editor."),
        signals.length
          ? h("div", { className: "table-wrap", style: { border: "1px solid var(--line)", borderRadius: "var(--r-sm)" } },
              h("table", { className: "table" },
                h("thead", null, h("tr", null, head.map((c) => h("th", { key: c }, c)))),
                h("tbody", null, signals.map((s) => h("tr", { key: s.key },
                  h("td", null, h("span", { style: { fontWeight: 600 } }, s.label)),
                  h("td", null, s.category ? h("span", { className: "tag" }, s.category) : h("span", { className: "dim" }, "—")),
                  h("td", null, h("span", { className: "mono dim" }, s.key)),
                  h("td", null, h("span", { className: "tag" }, s.unit)),
                  h("td", { className: "mono dim" }, s.range))))))
          : h(Empty, { icon: "activity", title: "No signals", sub: "No wearable inputs are linked to this device yet." }))));
}

function WearableInputModal({ data, existing, sources, onClose, onSave }) {
  const [label, setLabel] = useState("");
  const [category, setCategory] = useState("");
  const [unit, setUnit] = useState("");
  const [range, setRange] = useState("");
  const [srcs, setSrcs] = useState([]);
  useEffect(() => { if (data) { setLabel(data.label || ""); setCategory(data.category || ""); setUnit(data.unit || ""); setRange(data.range || ""); setSrcs([...(data.sources || [])]); } }, [data]);
  if (!data) return null;
  const dup = existing.some((i) => i.key !== data.key && i.label.trim().toLowerCase() === label.trim().toLowerCase());
  const slug = label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "").slice(0, 20) || "input";
  const valid = label.trim() && !dup;
  const submit = () => {
    if (!valid) return;
    onSave({ isNew: data.isNew, key: data.isNew ? slug : data.key, label: label.trim(), category: category.trim(), unit: unit.trim() || "—", range: range.trim() || "—", sources: [...srcs] });
  };
  return h(Modal, { open: true, onClose, title: data.isNew ? "New wearable input" : "Edit wearable input", width: 560,
    footer: h(React.Fragment, null,
      h(Button, { variant: "ghost", onClick: onClose }, "Cancel"),
      h(Button, { variant: "primary", icon: data.isNew ? "plus" : "check", disabled: !valid, onClick: submit }, data.isNew ? "Create input" : "Save changes")) },
    h("div", { className: "mform" },
      h(Field, { label: "Input name", hint: dup ? "An input with this name already exists." : "e.g. VO₂ Max, Resting heart rate" },
        h("input", { className: "input", autoFocus: true, value: label, placeholder: "e.g. VO₂ Max", onChange: (e) => setLabel(e.target.value) })),
      h("div", { className: "form-grid", style: { display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: "14px" } },
        h(Field, { label: "Signal type" }, h("input", { className: "input", value: category, placeholder: "e.g. Cardiac", onChange: (e) => setCategory(e.target.value) })),
        h(Field, { label: "Unit" }, h("input", { className: "input mono", value: unit, placeholder: "e.g. mL/kg/min", onChange: (e) => setUnit(e.target.value) })),
        h(Field, { label: "Detected range" }, h("input", { className: "input mono", value: range, placeholder: "e.g. 20–60", onChange: (e) => setRange(e.target.value) }))),
      !data.isNew && h(Field, { label: "Identifier" }, h("input", { className: "input mono", value: data.key, disabled: true })),
      data.isNew && label.trim() && h("p", { className: "mform-note" }, h(Icon, { name: "hash", size: 14 }), "Identifier: ", h("span", { className: "mono" }, slug)),
      h("div", { className: "refsec" },
        h("div", { className: "refsec-head" },
          h("p", { className: "refsec-title" }, "Supported wearables"),
          h("span", { className: "toolbar-count" }, srcs.length, " linked")),
        h("p", { className: "field-hint", style: { margin: "-2px 0 7px" } }, "Devices and aggregators that provide this input."),
        h(window.LinkCombo, { all: sources.map((s) => ({ id: s.key, label: s.name, meta: s.kind, search: s.name + " " + s.kind })),
          selected: srcs, placeholder: "Search wearable to link…", emptyText: "No wearables linked yet.", noMatchText: "No matching wearable",
          onAdd: (id) => setSrcs((xs) => xs.includes(id) ? xs : [...xs, id]),
          onRemove: (id) => setSrcs((xs) => xs.filter((x) => x !== id)) }))));
}

Object.assign(window, { WearablesView });
