/* People, one person's record, and the access the house hands out.
   Commit model: the household screens are INSTANT (answering a request applies
   at once with an undo toast; denying one needs a reason, so it opens a staged
   dialog); /people/:id is STAGED — preferences are a form, and saving them moves
   the comfort target on every other screen. */
(() => {
  const {
    Button, Badge, Avatar, Alert, AlertTitle, AlertDescription, Switch, Checkbox, Slider,
    Input, Select, Field, FieldLabel, FieldDescription, FieldError, Tabs, TabsList, TabsTab,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
  } = window.OriginDesignSystem_7c998b;

  function PeopleScreen({ data, now, viewer, onNavigate, onRequest, onMemberTransition, onInvite, onExport }) {
    const members = data.members;
    const home = membersHome(members);
    const awake = membersAwake(members, now);
    const waiting = requestsWaiting(data.requests);
    const target = comfortTarget(members, now);
    const setter = comfortSetter(members, now);

    return (
      <>
        <PageHead path="/people" title="People"
          lede={activeMembers(members).length + " people in the household, " + home.length + " home and " + awake.length + " up. The warmest preference among those " + awake.length + " is what the house is holding — " + formatTemp(target.c) + "°."}
          actions={
            <>
              <Button variant="outline" size="sm" onClick={() => onNavigate("/access")}><Icon name="key-round" />Access</Button>
              <ExportMenu scope={"All " + members.length + " people"} onExport={(format) => onExport("People", "The household", members.length, format)} />
              <Button size="sm" onClick={onInvite}><Icon name="plus" />New person</Button>
            </>
          } />

        <Figures items={[
          { label: "Home", value: home.length, unit: "of " + activeMembers(members).length, qualifier: awake.length + " up, " + membersAsleep(members, now).length + " asleep" },
          { label: "Waiting on you", value: waiting.length, qualifier: waiting.length ? waiting.map((r) => memberName(members, r.memberId).split(" ")[0]).join(" and ") + " asked for something" : "nobody is waiting on an answer" },
          { label: "Setting the temperature", value: setter ? formatTemp(setter.prefs.comfortC) : formatTemp(target.c), unit: "°", qualifier: setter ? setter.name + " — " + target.because : target.because },
          { label: "Not active", value: members.filter((m) => m.status !== "active").length, qualifier: members.filter((m) => m.status !== "active").map((m) => statusValue(MEMBER_STATUS, m.status).label.toLowerCase()).join(", ") || "everybody is active" },
        ]} />

        {waiting.length ? (
          <Band>
            <Card flush title={"Waiting on you (" + waiting.length + ")"} note="Somebody asked for something the rules do not let them do on their own.">
              <div style={{ paddingInline: "var(--card-pad)" }}>
                <Rows>
                  {waiting.map((request) => {
                    const member = memberById(data.members, request.memberId);
                    const subject = request.kind === "unlock" ? (entryById(data.entries, request.subject) || {}).label
                      : request.kind === "lights" ? deviceName(data.devices, request.subject)
                        : zoneName(request.subject);
                    return (
                      <Row key={request.id}
                        title={<PersonCell name={member.name} avatar={member.avatar} size={30}
                          meta={labelOf(HOUSEHOLD_ROLES, member.role) + " · asked " + formatWhen(request.askedAt, now) + " · " + subject}
                          onOpen={() => onNavigate("/people/" + member.id)} />}
                        note={"“" + request.body + "”"}>
                        <Transitions transitions={requestTransitions(request, viewer)} onRun={(t) => onRequest(request, t)} />
                      </Row>
                    );
                  })}
                </Rows>
              </div>
            </Card>
          </Band>
        ) : null}

        <div className="hh-roomgrid">
          {members.map((member) => {
            const counts = MEMBER_FLAGS.countsForComfort(member, now);
            const theirs = requestsForMember(data.requests, member.id);
            const routines = routinesForMember(routineRows(data, now), member.id);
            return (
              <Card key={member.id} fill>
                <div className="hh-room__top">
                  <PersonCell name={member.name} avatar={member.avatar} size={44}
                    meta={labelOf(HOUSEHOLD_ROLES, member.role) + " · since " + formatSince(member.since)}
                    onOpen={() => onNavigate("/people/" + member.id)} />
                  <StatusBadge values={MEMBER_STATUS} value={member.status} />
                </div>
                <Rows>
                  <Row title={member.home ? "Home" : "Out"}
                    note={member.status !== "active" ? (member.status === "invited" ? "Invited " + formatWhen(member.since, now) + " and not accepted yet" : member.suspendedNote || "No access")
                      : member.home ? (member.where ? "In the " + roomName(member.where).toLowerCase() + ", " : "") + (MEMBER_FLAGS.isAwake(member, now) ? "up until " + member.prefs.bedtime : "asleep since " + member.prefs.bedtime)
                        : "Last seen " + formatWhen(member.lastSeenAt, now)}
                    value={member.status === "active" ? <Badge size="sm" variant={member.home ? "success" : "secondary"}>{member.home ? "In" : "Out"}</Badge> : null} />
                  <Row title="Wants it at" value={formatTemp(member.prefs.comfortC) + "°"}
                    note={counts ? "Counts towards the target now" : member.status !== "active" ? "Ignored — no access" : member.home ? "Asleep, so ignored" : "Out, so ignored"} />
                  <Row title="Day" value={member.prefs.wake + "–" + member.prefs.bedtime}
                    note={routines.length ? routines.map((r) => r.name.toLowerCase()).join(", ") + " read this" : "No routine reads their times"} />
                  {theirs.length ? (
                    <Row title={"Requests (" + theirs.length + ")"}
                      note={theirs.map((r) => statusValue(REQUEST_STATUS, r.status).label.toLowerCase() + " — " + (r.reason || r.body.slice(0, 40) + "…")).join(" · ")} />
                  ) : null}
                </Rows>
                <div className="hh-room__foot">
                  <span className="hh-footnote">{member.email || "No email on the record"}</span>
                  <span className="hh-actions__row">
                    <Transitions transitions={memberTransitions(member, viewer).filter((t) => t.primary)} onRun={(t) => onMemberTransition(member, t)} />
                    <Button variant="outline" size="sm" onClick={() => onNavigate("/people/" + member.id)}>Open</Button>
                  </span>
                </div>
              </Card>
            );
          })}
        </div>
      </>
    );
  }

  /* STAGED: preferences are a form. Saving them moves the comfort target, the
     bedtime routines and the climate screen, which is stated on the bar. */
  function PersonScreen({ data, now, viewer, memberId, onNavigate, onSavePreferences, onMemberTransition, onRequest }) {
    const member = memberById(data.members, memberId);
    const initial = {
      comfortC: member.prefs.comfortC, bedtime: member.prefs.bedtime, wake: member.prefs.wake,
      lightsOnArrival: member.prefs.lightsOnArrival, notifyAlarm: member.prefs.notifyAlarm, notifyDeliveries: member.prefs.notifyDeliveries,
    };
    const [values, setValues] = React.useState(initial);
    const [submitted, setSubmitted] = React.useState(false);
    React.useEffect(() => { setValues(initial); setSubmitted(false); }, [member]);
    const set = (id, value) => setValues((v) => Object.assign({}, v, { [id]: value }));
    const dirty = Object.keys(initial).some((key) => values[key] !== initial[key]);

    const errors = {};
    if (!/^\d\d:\d\d$/.test(values.bedtime)) errors.bedtime = "Use HH:MM.";
    if (!/^\d\d:\d\d$/.test(values.wake)) errors.wake = "Use HH:MM.";
    if (!(values.comfortC >= 15 && values.comfortC <= 24)) errors.comfortC = "Between 15 and 24 degrees.";
    const valid = Object.keys(errors).length === 0;

    /* The preview executes the real evaluator on the real fixture, with this
       form's values swapped in — so it cannot disagree with what Save will do. */
    const previewMembers = data.members.map((m) => (m.id === member.id ? Object.assign({}, m, { prefs: Object.assign({}, m.prefs, values) }) : m));
    const previewTarget = comfortTarget(previewMembers, now);
    const currentTarget = comfortTarget(data.members, now);
    const routines = routinesForMember(routineRows(data, now), member.id);
    const grants = data.grants.filter((g) => g.holder === member.name);
    const theirRequests = requestsForMember(data.requests, member.id);
    const events = eventsForMember(data.events, member.id);

    return (
      <>
        <PageHead path="/people/:id" params={{ id: member.id }} crumb={member.name} title={member.name}
          lede={labelOf(HOUSEHOLD_ROLES, member.role) + " · " + (member.status === "active" ? (member.home ? "home now" : "out now") : statusValue(MEMBER_STATUS, member.status).label.toLowerCase()) + " · in the household since " + formatSince(member.since)}
          actions={<Transitions transitions={memberTransitions(member, viewer)} onRun={(t) => onMemberTransition(member, t)} />} />

        <Band>
          <Card>
            <div style={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: "var(--space-5)" }}>
              <Avatar size={72} src={member.avatar} alt={"Photograph of " + member.name} fallback={initialsOf(member.name)} />
              <div style={{ flex: "1 1 18rem", minWidth: 0 }}>
                <span className="hh-actions__row">
                  <StatusBadge values={MEMBER_STATUS} value={member.status} />
                  <Badge size="sm" variant={member.home ? "success" : "secondary"}>{member.home ? "Home" : "Out"}</Badge>
                  {MEMBER_FLAGS.countsForComfort(member, now) ? <Badge size="sm" variant="info">Counts towards the temperature</Badge> : null}
                </span>
                <p className="hh-card__note" style={{ marginTop: "var(--space-2)" }}>
                  {member.status === "invited" ? "Invited " + formatWhen(member.since, now) + " and has not accepted, so nothing they prefer is counted yet."
                    : member.status === "suspended" ? member.suspendedNote
                      : member.home ? (member.where ? "In the " + roomName(member.where).toLowerCase() + ". " : "") + (MEMBER_FLAGS.isAwake(member, now) ? "Up until " + member.prefs.bedtime + ", so their comfort preference counts." : "Asleep since " + member.prefs.bedtime + ", so their preference is ignored and the house holds " + formatTemp(HOUSE_POLICY.nightSetbackC) + "°.")
                        : "Out since " + formatWhen(member.lastSeenAt, now) + ", so their preference is ignored."}
                </p>
              </div>
              <div className="hh-dial">
                <span className="hh-dial__reading">{formatTemp(values.comfortC)}<span>°</span></span>
                <span className="hh-dial__target">what {member.name.split(" ")[0]} wants</span>
              </div>
            </div>
          </Card>
        </Band>

        <div className="hh-cols hh-cols--wide">
          <div>
            <Card title="Preferences" note="Every control here is a form field. Nothing changes in the house until you save.">
              <div className="hh-fields">
                <Field>
                  <FieldLabel>Comfortable at</FieldLabel>
                  <div style={{ display: "flex", alignItems: "center", gap: "var(--space-4)" }}>
                    <span style={{ flex: 1, minWidth: "8rem" }}>
                      <Slider min={15} max={24} step={0.5} value={values.comfortC} aria-label="Comfortable temperature" onValueChange={(v) => set("comfortC", Array.isArray(v) ? v[0] : v)} />
                    </span>
                    <span className="hh-row__value" style={{ minWidth: "3.5rem" }}>{formatTemp(values.comfortC)}°</span>
                  </div>
                  {submitted && errors.comfortC ? <FieldError>{errors.comfortC}</FieldError> : <FieldDescription>The house holds the warmest preference among the people home and awake.</FieldDescription>}
                </Field>
                <Field>
                  <FieldLabel htmlFor="hh-p-bed">Goes to bed</FieldLabel>
                  <Input id="hh-p-bed" value={values.bedtime} invalid={submitted && !!errors.bedtime} onChange={(e) => set("bedtime", e.target.value)} />
                  {submitted && errors.bedtime ? <FieldError>{errors.bedtime}</FieldError> : <FieldDescription>{routines.length ? routines.map((r) => r.name).join(" and ") + " reads this." : "Also decides when their preference stops counting."}</FieldDescription>}
                </Field>
                <Field>
                  <FieldLabel htmlFor="hh-p-wake">Gets up</FieldLabel>
                  <Input id="hh-p-wake" value={values.wake} invalid={submitted && !!errors.wake} onChange={(e) => set("wake", e.target.value)} />
                  {submitted && errors.wake ? <FieldError>{errors.wake}</FieldError> : null}
                </Field>
                <Field className="hh-fields__wide">
                  <FieldLabel>What the house does for them</FieldLabel>
                  <div style={{ display: "flex", flexDirection: "column", gap: "var(--space-2-5)" }}>
                    <span className="hh-person">
                      <Checkbox checked={values.lightsOnArrival} aria-label="Lights on when they arrive" onCheckedChange={(v) => set("lightsOnArrival", v)} />
                      <span className="hh-pair__value">Turn the hall light on when they arrive</span>
                    </span>
                    <span className="hh-person">
                      <Checkbox checked={values.notifyAlarm} aria-label="Tell them about the alarm" onCheckedChange={(v) => set("notifyAlarm", v)} />
                      <span className="hh-pair__value">Tell them when the alarm is triggered</span>
                    </span>
                    <span className="hh-person">
                      <Checkbox checked={values.notifyDeliveries} aria-label="Tell them about deliveries" onCheckedChange={(v) => set("notifyDeliveries", v)} />
                      <span className="hh-pair__value">Tell them when a delivery arrives</span>
                    </span>
                  </div>
                  <FieldDescription>Checkboxes, because they are submitted with this form rather than applying on their own.</FieldDescription>
                </Field>
              </div>
            </Card>

            <Card tint title="What saving would do" note="The real evaluator, run on the real house with these values swapped in.">
              <Rows>
                <Row title="The house would hold" value={formatTemp(previewTarget.c) + "°"}
                  note={previewTarget.because + (previewTarget.c !== currentTarget.c ? " · it holds " + formatTemp(currentTarget.c) + "° now" : " · unchanged")} />
                {routines.map((routine) => (
                  <Row key={routine.id} title={routine.name} value={values.bedtime}
                    note={routine.when !== values.bedtime ? "Runs at " + routine.when + " today · it stores the person, not the time" : "Unchanged"} />
                ))}
              </Rows>
            </Card>
          </div>

          <div>
            <Card title="Record">
              <Pairs items={[
                { label: "Role", value: labelOf(HOUSEHOLD_ROLES, member.role) },
                { label: "Standing", value: <StatusBadge values={MEMBER_STATUS} value={member.status} /> },
                { label: "Email", value: member.email || "None on the record" },
                { label: "Phone", value: member.phone || "None on the record" },
                { label: "Last seen", value: <Clock at={member.lastSeenAt} now={now} /> },
                { label: "May unlock doors", value: ROLE_CAN_UNLOCK[member.role] ? "Yes" : "No — they ask instead" },
                { label: "May arm the alarm", value: ROLE_CAN_ARM[member.role] ? "Yes" : "No" },
                { label: "May hand out access", value: ROLE_CAN_GRANT_ACCESS[member.role] ? "Yes" : "No" },
              ]} />
            </Card>

            {theirRequests.length ? (
              <Card title={"Their requests (" + theirRequests.length + ")"} note="What they have asked the house for.">
                <Rows>
                  {theirRequests.map((request) => (
                    <Row key={request.id} title={"“" + request.body + "”"}
                      note={statusValue(REQUEST_STATUS, request.status).label + " · asked " + formatWhen(request.askedAt, now) + (request.answeredAt ? ", answered " + formatWhen(request.answeredAt, now) + " by " + memberName(data.members, request.answeredById) : "") + (request.reason ? " — " + request.reason : "")}>
                      <Transitions transitions={requestTransitions(request, viewer)} onRun={(t) => onRequest(request, t)} />
                    </Row>
                  ))}
                </Rows>
              </Card>
            ) : null}

            {grants.length ? (
              <Card title={"Ways in they hold (" + grants.length + ")"} note="Their own code or key, managed on Access."
                actions={<Button variant="outline" size="sm" onClick={() => onNavigate("/access")}>Access</Button>}>
                <Rows>
                  {grants.map((grant) => (
                    <Row key={grant.id} title={grant.label}
                      note={labelOf(ACCESS_KINDS, grant.kind) + " · " + grant.entryIds.map((id) => (entryById(data.entries, id) || {}).label).join(", ") + " · used " + formatCount(grant.uses) + " times"}>
                      <StatusBadge values={ACCESS_STATUS} value={grant.status} />
                    </Row>
                  ))}
                </Rows>
              </Card>
            ) : null}

            <Card title={"In the log (" + events.length + ")"} note="What they did, and what the house recorded about them.">
              {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 them has reached the log yet.</p>}
            </Card>
          </div>
        </div>

        <FormBar dirty={dirty} label="Save preferences"
          note={previewTarget.c !== currentTarget.c
            ? "Saving moves the house from " + formatTemp(currentTarget.c) + "° to " + formatTemp(previewTarget.c) + "°, and every zone on Climate with it."
            : "Preferences are read live by Climate, Today and the bedtime routines."}
          onCancel={() => { setValues(initial); setSubmitted(false); }}
          onSave={() => { setSubmitted(true); if (valid) onSavePreferences(member, values); }} />
      </>
    );
  }

  function AccessScreen({ data, now, viewer, onNavigate, onAccessTransition, onNewGrant, onExport }) {
    const [tab, setTab] = React.useState("live");
    const { sort, onSort } = useSort("used", "desc");

    const live = grantsLive(data.grants);
    const expiring = grantsExpiringSoon(data.grants, now);
    const unused = grantsNeverUsed(data.grants);
    const base = tab === "live" ? live : tab === "ended" ? data.grants.filter((g) => g.status === "revoked" || g.status === "suspended") : data.grants;
    const rows = sortRows(base, sort, {
      used: (g) => g.lastUsedAt,
      uses: (g) => g.uses,
      label: (g) => g.label,
      ends: (g) => g.endsAt,
    });

    return (
      <>
        <PageHead path="/access" title="Access"
          lede={live.length + " ways into the house that are not a person: keypad codes, handed-out keys and windows of access opened for a job."}
          actions={
            <>
              <ExportMenu scope={"All " + data.grants.length + " grants"} onExport={(format) => onExport("Access", "Every way in", data.grants.length, format)} />
              <Button size="sm" onClick={onNewGrant}><Icon name="plus" />New access</Button>
            </>
          } />

        <Figures items={[
          { label: "Live", value: live.length, qualifier: live.filter((g) => g.kind === "code").length + " codes, " + live.filter((g) => g.kind === "key").length + " keys, " + live.filter((g) => g.kind === "window").length + " timed" },
          { label: "Expiring", value: expiring.length, qualifier: expiring.length ? expiring.map((g) => g.label.toLowerCase() + " " + formatCountdown(g.endsAt, now)).join(", ") : "nothing expires within " + HOUSE_POLICY.accessExpirySoonDays + " days" },
          { label: "Never used", value: unused.length, qualifier: unused.length ? unused.map((g) => g.label.toLowerCase()).join(", ") + " — handed over and never used" : "every live grant has been used" },
          { label: "Ended", value: data.grants.filter((g) => g.status === "revoked" || g.status === "suspended").length, qualifier: "kept rather than deleted, so the log still reads" },
        ]} />

        {unused.length ? (
          <Band>
            <Card tint>
              <Alert variant="info">
                <AlertTitle>{unused.length === 1 ? "One way in has never been used" : unused.length + " ways in have never been used"}</AlertTitle>
                <AlertDescription>
                  {unused.map((g) => g.label + " — " + g.holder + ", " + labelOf(ACCESS_KINDS, g.kind).toLowerCase() + " to " + g.entryIds.map((id) => (entryById(data.entries, id) || {}).label.toLowerCase()).join(" and ") + ", handed over " + formatWhen(g.startsAt, now)).join(" · ")}.
                  A key nobody uses is a key nobody is accounting for.
                </AlertDescription>
              </Alert>
            </Card>
          </Band>
        ) : null}

        <Band>
          <Card flush>
            <div style={{ padding: "0 var(--card-pad) var(--space-4)" }}>
              <Tabs value={tab} onValueChange={setTab}>
                <TabsList>
                  <TabsTab value="live" size="sm">Live ({live.length})</TabsTab>
                  <TabsTab value="ended" size="sm">Suspended and revoked ({data.grants.filter((g) => g.status === "revoked" || g.status === "suspended").length})</TabsTab>
                  <TabsTab value="all" size="sm">All ({data.grants.length})</TabsTab>
                </TabsList>
              </Tabs>
            </div>
            <TableWrap>
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead><SortHead label="Grant" sortKey="label" sort={sort} onSort={onSort} /></TableHead>
                    <TableHead>Who holds it</TableHead>
                    <TableHead>Kind</TableHead>
                    <TableHead>Opens</TableHead>
                    <TableHead>Status</TableHead>
                    <TableHead><SortHead label="Last used" sortKey="used" sort={sort} onSort={onSort} /></TableHead>
                    <TableHead><SortHead label="Ends" sortKey="ends" sort={sort} onSort={onSort} /></TableHead>
                    <TableHead className="hh-num"><SortHead label="Uses" sortKey="uses" sort={sort} onSort={onSort} /></TableHead>
                    <TableHead>Action</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {rows.map((grant) => (
                    <TableRow key={grant.id} className={grant.status === "revoked" ? "hh-row--recessed" : undefined}>
                      <TableCell>
                        <span className="hh-person__text">
                          <span className="hh-person__name">{grant.label}</span>
                          <span className="hh-person__meta">{grant.note}</span>
                        </span>
                      </TableCell>
                      <TableCell>
                        {grant.providerId
                          ? <button type="button" className="hh-linkbtn" onClick={() => onNavigate("/providers/" + grant.providerId)}>{grant.holder}</button>
                          : grant.holder}
                      </TableCell>
                      <TableCell>{labelOf(ACCESS_KINDS, grant.kind)}</TableCell>
                      <TableCell>{grant.entryIds.map((id) => (entryById(data.entries, id) || {}).label).join(", ")}</TableCell>
                      <TableCell>
                        <span className="hh-actions__row">
                          <StatusBadge values={ACCESS_STATUS} value={grant.status} />
                          {ACCESS_FLAGS.expiringSoon(grant, now) ? <Badge size="sm" variant="warning">{formatCountdown(grant.endsAt, now)}</Badge> : null}
                          {ACCESS_FLAGS.neverUsed(grant) ? <Badge size="sm" variant="secondary">Never used</Badge> : null}
                        </span>
                      </TableCell>
                      <TableCell><Clock at={grant.lastUsedAt} now={now} /></TableCell>
                      <TableCell>{grant.endsAt ? <Clock at={grant.endsAt} now={now} problem={grant.status === "active"} /> : <span className="hh-muted">No end</span>}</TableCell>
                      <TableCell className="hh-num">{formatCount(grant.uses)}</TableCell>
                      <TableCell><RowActions transitions={accessTransitions(grant, viewer)} onRun={(t) => onAccessTransition(grant, t)} label={"Actions for " + grant.label} /></TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </TableWrap>
            <TableFoot shown={rows.length} total={data.grants.length} noun="grants" />
          </Card>
        </Band>
      </>
    );
  }

  Object.assign(window, { PeopleScreen, PersonScreen, AccessScreen });
})();
