/* Devices — the kit and its radio: pair a new one, test the ones that are here,
   and fix the ones that want a person. Not a register of devices to browse; the
   list at the bottom is the last thing on the screen, not the first.

   Commit model: INSTANT on control, STAGED on pairing. A switch applies
   immediately, a consequential move raises an undo toast, removing a device
   confirms first (App.jsx), and taking a device INTO the house is a three-stage
   dialog — which is what pairing is: find it, place it, prove it answers.

   Everything the screen says about the radio is derived from one link record per
   device: weak, behind on firmware and stale are LINK_FLAGS, so a device becomes
   a problem the moment the reading changes and nothing is written on it.

   The tabs narrow the list only and the figures do not move with them (§5.3);
   the room filter is part of the same list container for the same reason. */
(() => {
  const {
    Button, Badge, Tabs, TabsList, TabsTab,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
  } = window.OriginDesignSystem_7c998b;

  const KIND_FILTER = [{ value: "all", label: "Every kind" }].concat(DEVICE_KINDS);
  const ROOM_FILTER = [{ value: "all", label: "Every room" }].concat(ROOMS);

  function DevicesScreen({ data, now, viewer, onNavigate, onToggleDevice, onDeviceTransition, onPairDevice, onRunHouseTest, onTestDevice, onUpdateFirmware, onExport }) {
    const [tab, setTab] = React.useState("all");
    const [query, setQuery] = React.useState("");
    const [kind, setKind] = React.useState("all");
    const [room, setRoom] = React.useState("all");
    const { sort, onSort } = useSort("name", "asc");

    const inHouse = devicesInHouse(data.devices);
    const needy = devicesNeedingYou(data.devices);
    const weak = devicesWeakSignal(data);
    const behind = devicesBehind(data);
    const retired = data.devices.filter((d) => d.status === "retired");
    const verdict = linkVerdict(data, now);
    const stale = LINK_FLAGS.stale(data.hub, now);
    const mayPair = ROLE_CAN_PAIR[viewer.role];

    const dirty = !!query || kind !== "all" || room !== "all";
    const clear = () => { setQuery(""); setKind("all"); setRoom("all"); };
    const base = tab === "all" ? inHouse : tab === "needs" ? needy : retired;
    const filtered = base.filter((device) => {
      if (kind !== "all" && device.kind !== kind) return false;
      if (room !== "all" && device.room !== room) return false;
      if (query && (device.name + " " + roomName(device.room) + " " + labelOf(DEVICE_KINDS, device.kind)).toLowerCase().indexOf(query.toLowerCase()) < 0) return false;
      return true;
    });
    const rows = sortRows(filtered, sort, {
      name: (d) => d.name,
      room: (d) => roomName(d.room),
      signal: (d) => { const link = linkOf(data.links, d.id); return link && link.dbm != null ? link.dbm : -999; },
      battery: (d) => (d.battery == null ? -1 : d.battery),
      seen: (d) => d.lastSeenAt,
    });

    return (
      <>
        <PageHead path="/devices" title="Devices"
          lede={inHouse.length + " devices on the house network, and one hub in the utility room. Everything on this screen — a weak signal, a firmware behind, a test gone stale — is read off the link rather than written on the device."}
          actions={
            <>
              <Button variant="outline" size="sm" onClick={onRunHouseTest}><Icon name="activity" />Test every device</Button>
              <ExportMenu scope={"All " + inHouse.length + " devices and their link readings"}
                onExport={(format) => onExport("Devices", "Every device in the house", inHouse.length, format)} />
              <Button size="sm" disabled={!mayPair} onClick={() => onPairDevice(null)}><Icon name="plus" />Pair a device</Button>
            </>
          } />

        <Band>
          <Card>
            <div style={{ display: "flex", flexWrap: "wrap", alignItems: "flex-start", justifyContent: "space-between", gap: "var(--space-5)" }}>
              <div style={{ minWidth: 0, flex: "1 1 22rem" }}>
                <span className="hh-actions__row" style={{ marginBottom: "var(--space-2)" }}>
                  <Badge size="sm" variant="secondary">Tested {formatWhen(data.hub.testedAt, now)}</Badge>
                  {stale ? <span className="hh-footnote">older than {HOUSE_POLICY.testStaleDays} days</span> : null}
                </span>
                <VerdictLine verdict={verdict} />
                <p className="hh-card__note">{verdict.detail}</p>
                <span className="hh-actions__row" style={{ marginTop: "var(--space-4)" }}>
                  <Button size="sm" onClick={onRunHouseTest}>Run the test again</Button>
                  {behind.length ? <Button variant="outline" size="sm" onClick={() => onUpdateFirmware(behind)}>Update {behind.length} devices</Button> : null}
                </span>
              </div>
              <div style={{ flex: "0 1 16rem", minWidth: 0 }}>
                <span className="hh-fig__label">The hub</span>
                <p className="hh-footnote" style={{ marginTop: "var(--space-2)" }}>
                  {data.hub.name}, {roomName(data.hub.room).toLowerCase()} · channel {data.hub.channel}<br />
                  Firmware {data.hub.firmware}{data.hub.firmware === data.hub.latest ? " — up to date" : " · " + data.hub.latest + " available"}<br />
                  Up {formatCount(data.hub.uptimeHours)} hours, average {averageLatency(data)}ms to answer.
                </p>
              </div>
            </div>
          </Card>
        </Band>

        <Figures items={[
          { label: "In the house", value: inHouse.length, qualifier: devicesOn(data.devices).length + " on now, drawing " + formatWatts(liveWatts(data.devices)) },
          { label: "Wanting a person", value: needy.length, qualifier: needy.map((d) => statusValue(DEVICE_STATUS, d.status).label.toLowerCase()).join(", ") || "nothing offline or unset" },
          { label: "Weak signal", value: weak.length, qualifier: weak.length ? weak.map((d) => d.name.toLowerCase()).join(", ") + " — under " + HOUSE_POLICY.weakSignalDbm + " dBm" : "everything above " + HOUSE_POLICY.weakSignalDbm + " dBm" },
          { label: "On battery", value: data.devices.filter((d) => d.battery != null).length, qualifier: devicesLowBattery(data.devices).length + " under " + HOUSE_POLICY.lowBatteryPercent + "%" + (devicesLowBattery(data.devices).length ? " — " + devicesLowBattery(data.devices).map((d) => d.name.toLowerCase()).join(", ") : "") + ", " + data.devices.filter((d) => d.battery === 0).length + " flat" },
        ]} />

        {data.discovered.length ? (
          <Band>
            <Card title={"Heard on the network (" + data.discovered.length + ")"}
              note="Devices the hub can hear that the house has not taken in. Pairing one starts from what it announced itself as, rather than from a blank form.">
              <Rows>
                {data.discovered.map((found) => (
                  <Row key={found.id} title={found.announced}
                    note={"Found " + formatWhen(found.foundAt, now) + " · suggests the " + roomName(found.suggestRoom).toLowerCase()}
                    value={
                      <span className="hh-actions__row">
                        <Signal dbm={found.dbm} />
                        <Button variant="outline" size="sm" disabled={!mayPair} onClick={() => onPairDevice(found)}>Pair</Button>
                      </span>
                    } />
                ))}
              </Rows>
            </Card>
          </Band>
        ) : null}

        {needy.length ? (
          <Band>
            <Card title={"Wanting a person (" + needy.length + ")"}
              note="One predicate — offline, never set up, or a flat battery. It is the same count the sidebar carries.">
              <Rows>
                {needy.map((device) => {
                  const link = linkOf(data.links, device.id);
                  return (
                    <Row key={device.id} title={device.name}
                      note={roomName(device.room) + " · " + (device.status === "offline" ? "last heard " + formatWhen(device.lastSeenAt, now) + ", the test got no answer" : device.status === "setup_pending" ? "added " + formatSince(device.addedAt) + " and never set up" : "battery flat")}
                      value={
                        <span className="hh-actions__row">
                          {link && link.result !== "untested" ? <StatusBadge values={TEST_STATUS} value={link.result} /> : null}
                          <Transitions transitions={deviceTransitions(device, viewer).filter((t) => t.primary)} onRun={(t) => onDeviceTransition(device, t)} />
                          {device.status === "offline" ? <Button variant="ghost" size="sm" onClick={() => onTestDevice(device)}>Test</Button> : null}
                        </span>
                      } />
                  );
                })}
              </Rows>
            </Card>
          </Band>
        ) : null}

        <Band>
          <Card flush title="Everything on the network" note="The whole list, for when you want a particular thing rather than a problem.">
            <div style={{ padding: "0 var(--card-pad) var(--space-4)" }}>
              <Tabs value={tab} onValueChange={setTab}>
                <TabsList>
                  <TabsTab value="all" size="sm">In the house ({inHouse.length})</TabsTab>
                  <TabsTab value="needs" size="sm">Wanting a person ({needy.length})</TabsTab>
                  <TabsTab value="retired" size="sm">Out of the house ({retired.length})</TabsTab>
                </TabsList>
              </Tabs>
            </div>
            <FilterBar query={query} onQuery={setQuery} placeholder="Search a device, a room or a kind…" dirty={dirty} onClear={clear}
              selects={[
                { id: "kind", label: "Kind", items: KIND_FILTER, value: kind, onChange: setKind },
                { id: "room", label: "Room", items: ROOM_FILTER, value: room, onChange: setRoom },
              ]} />
            {rows.length === 0 ? <NoResults noun="devices" onClear={clear} /> : (
              <>
                <TableWrap>
                  <Table>
                    <TableHeader>
                      <TableRow>
                        <TableHead><SortHead label="Device" sortKey="name" sort={sort} onSort={onSort} /></TableHead>
                        <TableHead><SortHead label="Room" sortKey="room" sort={sort} onSort={onSort} /></TableHead>
                        <TableHead><SortHead label="Signal" sortKey="signal" sort={sort} onSort={onSort} /></TableHead>
                        <TableHead>Last test</TableHead>
                        <TableHead><SortHead label="Battery" sortKey="battery" sort={sort} onSort={onSort} /></TableHead>
                        <TableHead><SortHead label="Last heard" sortKey="seen" sort={sort} onSort={onSort} /></TableHead>
                        <TableHead>Control</TableHead>
                      </TableRow>
                    </TableHeader>
                    <TableBody>
                      {rows.map((device) => {
                        const link = linkOf(data.links, device.id);
                        return (
                          <TableRow key={device.id} className={DEVICE_FLAGS.recessed(device) ? "hh-row--recessed" : undefined}>
                            <TableCell>
                              <span className="hh-person__text">
                                <span className="hh-person__name"><button type="button" className="hh-linkbtn" onClick={() => onNavigate("/devices/" + device.id)}>{device.name}</button></span>
                                <span className="hh-person__meta">{labelOf(DEVICE_KINDS, device.kind)}{link && LINK_FLAGS.behind(link) ? " · firmware " + link.firmware + " of " + link.latest : ""}</span>
                              </span>
                            </TableCell>
                            <TableCell>{roomName(device.room)}</TableCell>
                            <TableCell><Signal dbm={link ? link.dbm : null} /></TableCell>
                            <TableCell>
                              {link && link.result !== "pass"
                                ? <span className="hh-actions__row"><StatusBadge values={TEST_STATUS} value={link.result} />{device.status === "online" || device.status === "offline" ? <Button variant="ghost" size="sm" onClick={() => onTestDevice(device)}>Test</Button> : null}</span>
                                : <span className="hh-footnote">{link && link.latencyMs != null ? link.latencyMs + "ms" : "—"}</span>}
                            </TableCell>
                            <TableCell><Battery level={device.battery} /></TableCell>
                            <TableCell><Clock at={device.lastSeenAt} now={now} problem={device.status === "offline"} /></TableCell>
                            <TableCell><DeviceControl device={device} viewer={viewer} onToggle={onToggleDevice} onRun={onDeviceTransition} /></TableCell>
                          </TableRow>
                        );
                      })}
                    </TableBody>
                  </Table>
                </TableWrap>
                <TableFoot shown={rows.length} total={base.length} noun="devices" />
              </>
            )}
          </Card>
        </Band>
      </>
    );
  }

  function DeviceScreen({ data, now, viewer, deviceId, onNavigate, onToggleDevice, onDeviceTransition, onTestDevice, onUpdateFirmware, onExport }) {
    const device = deviceById(data.devices, deviceId);
    const link = linkOf(data.links, device.id);
    const routines = routinesForDevice(routineRows(data, now), device.id);
    const jobs = jobsForDevice(jobRows(data, now), device.id);
    const events = eventsForDevice(data.events, device.id);
    const entry = data.entries.find((e) => e.deviceId === device.id) || null;
    const share = liveWatts(data.devices) ? (device.watts / liveWatts(data.devices)) * 100 : 0;
    const consumable = jobs.find((j) => j.lifeHours != null);

    return (
      <>
        <PageHead path="/devices/:id" params={{ id: device.id }} crumb={device.name} title={device.name}
          lede={labelOf(DEVICE_KINDS, device.kind) + " in the " + roomName(device.room).toLowerCase() + ", in the house since " + formatSince(device.addedAt) + "."}
          actions={
            <>
              <Button variant="outline" size="sm" onClick={() => onTestDevice(device)} disabled={device.status !== "online" && device.status !== "offline"}><Icon name="activity" />Test the connection</Button>
              <ExportMenu scope={"This device and its " + events.length + " log entries"} onExport={(format) => onExport("Device", device.name, 1, format)} />
            </>
          } />

        <Band>
          <Card>
            <div style={{ display: "flex", flexWrap: "wrap", alignItems: "center", justifyContent: "space-between", gap: "var(--space-5)" }}>
              <div style={{ minWidth: 0 }}>
                <span className="hh-actions__row">
                  <StatusBadge values={DEVICE_STATUS} value={device.status} />
                  {DEVICE_FLAGS.lowBattery(device) ? <Badge size="sm" variant="warning">Battery {device.battery}%</Badge> : null}
                  {entry ? <Badge size="sm" variant="secondary">{entry.label}</Badge> : null}
                </span>
                <p className="hh-card__note" style={{ marginTop: "var(--space-2)" }}>
                  {DEVICE_FLAGS.recessed(device)
                    ? device.status === "updating" ? "It is updating itself. Nothing can be asked of it until it finishes." : "It has been switched out of the house, so nothing acts on it — and the routines that name it are broken until it comes back."
                    : DEVICE_FLAGS.drawing(device) ? "Drawing " + formatWatts(device.watts) + ", which is " + Math.round(share) + "% of everything the house is using right now."
                      : device.kind === "sensor" || device.kind === "meter" ? "Reporting to the house; nothing to switch." : "On standby."}
                </p>
              </div>
              <Transitions transitions={deviceTransitions(device, viewer)} onRun={(t) => onDeviceTransition(device, t)} />
            </div>
          </Card>
        </Band>

        <Figures items={[
          { label: "Drawing now", value: DEVICE_FLAGS.drawing(device) ? formatWatts(device.watts).split(" ")[0] : "0", unit: DEVICE_FLAGS.drawing(device) ? formatWatts(device.watts).split(" ")[1] : "W", qualifier: DEVICE_FLAGS.drawing(device) ? Math.round(share) + "% of the live draw" : "nothing while it is off" },
          { label: "Battery", value: device.battery == null ? "Mains" : device.battery + "%", qualifier: device.battery == null ? "no battery to change" : device.battery === 0 ? "flat — it stopped reporting " + formatWhen(device.lastSeenAt, now) : "changed when it drops under " + HOUSE_POLICY.lowBatteryPercent + "%" },
          { label: "Signal", value: link && link.dbm != null ? link.dbm : "—", unit: link && link.dbm != null ? "dBm" : null, qualifier: link && link.dbm != null ? labelOf(SIGNAL_BANDS, signalBand(link.dbm)) + ", answered in " + link.latencyMs + "ms" : "it did not answer the last test" },
          { label: "Routines naming it", value: routines.length, qualifier: routines.filter((r) => r.broken).length + " of them are broken, " + routinesRunning(routines).length + " running" },
        ]} />

        <div className="hh-cols">
          <div>
            <Card title="Record">
              <Pairs items={[
                { label: "Kind", value: labelOf(DEVICE_KINDS, device.kind) },
                { label: "Room", value: <button type="button" className="hh-linkbtn" onClick={() => onNavigate("/rooms")}>{roomName(device.room)}</button> },
                { label: "Reporting", value: <StatusBadge values={DEVICE_STATUS} value={device.status} /> },
                { label: "Last heard from", value: <Clock at={device.lastSeenAt} now={now} problem={device.status === "offline"} /> },
                { label: "Added", value: formatSince(device.addedAt) },
                { label: "Ways in it holds", value: entry ? entry.label + " — " + statusValue(ENTRY_STATUS, entry.status).label.toLowerCase() : "None" },
              ]} />
            </Card>

            <Card title="The connection" note="What the hub read when it last asked. Whether that answer is stale is worked out from the clock, not stored.">
              <Pairs items={[
                { label: "Signal", value: <Signal dbm={link ? link.dbm : null} /> },
                { label: "Last test", value: link ? <StatusBadge values={TEST_STATUS} value={link.result} /> : "Never tested" },
                { label: "Time to answer", value: link && link.latencyMs != null ? link.latencyMs + "ms, against " + averageLatency(data) + "ms across the house" : "No answer" },
                { label: "Tested", value: link && link.testedAt ? formatWhen(link.testedAt, now) + (LINK_FLAGS.stale(link, now) ? " — worth running again" : "") : "Never" },
                {
                  label: "Firmware",
                  value: link && link.firmware
                    ? (LINK_FLAGS.behind(link)
                      ? <span className="hh-actions__row"><span>{link.firmware} of {link.latest}</span><Button variant="outline" size="sm" onClick={() => onUpdateFirmware([device])}>Update</Button></span>
                      : link.firmware + " — up to date")
                    : "Not known until it is set up",
                },
              ]} />
            </Card>

            {consumable ? (
              <Card title="Consumable" note="Measured in the hours the machine has actually run, not in months.">
                <Rows>
                  <Row title={consumable.title}
                    note={formatCount(consumable.ranHours) + " hours run of " + formatCount(consumable.lifeHours) + " · " + (consumable.spent ? formatCount(consumable.ranHours - consumable.lifeHours) + " hours past the change" : Math.round(consumable.lifeLeft * 100) + "% of the interval left")}
                    value={<Button variant="outline" size="sm" onClick={() => onNavigate("/upkeep/" + consumable.id)}>Open job</Button>} />
                </Rows>
                <div style={{ marginTop: "var(--space-3)" }}>
                  <Bar percent={Math.max(0, (consumable.lifeLeft || 0) * 100)} tone={consumable.spent ? "danger" : consumable.lifeLeft < 0.2 ? "warn" : null} label="Filter life left" />
                </div>
              </Card>
            ) : null}
          </div>

          <div>
            <Card title={"Routines naming it (" + routines.length + ")"}
              note="A routine that names a device which is no longer in the house is broken, and says so without anything being written on it."
              actions={<Button variant="outline" size="sm" onClick={() => onNavigate("/routines")}>All routines</Button>}>
              {routines.length ? (
                <Rows>
                  {routines.map((routine) => (
                    <Row key={routine.id} recessed={routine.recessed} title={routine.name}
                      note={routine.broken ? "Broken — it names " + routine.missing.map((id) => deviceName(data.devices, id)).join(" and ") : labelOf(TRIGGER_KINDS, routine.trigger) + (routine.when ? " · " + routine.when : "") + " · last ran " + formatWhen(routine.lastRunAt, now)}>
                      <StatusBadge values={ROUTINE_STATUS} value={routine.status} />
                      {routine.broken ? <Badge size="sm" variant="warning">Broken</Badge> : null}
                    </Row>
                  ))}
                </Rows>
              ) : <p className="hh-footnote">No routine names this device.</p>}
            </Card>

            <Card title={"In the log (" + events.length + ")"} note="Everything the house recorded about this device.">
              {events.length ? (
                <Rows>
                  {events.map((event) => (
                    <Row key={event.id} title={event.line} note={event.note || labelOf(EVENT_KINDS, event.kind)}
                      value={<span className="hh-footnote" title={formatAbsolute(event.at)}>{formatWhen(event.at, now)}</span>} />
                  ))}
                </Rows>
              ) : <p className="hh-footnote">Nothing about this device has reached the log yet.</p>}
            </Card>
          </div>
        </div>
      </>
    );
  }

  Object.assign(window, { DevicesScreen, DeviceScreen });
})();
