/* Settings and your profile — the staged half of the product.
   Commit model: every screen in this file is STAGED. Nothing applies until Save,
   Save and Cancel are disabled until the form is dirty, checkboxes are submitted
   with the form rather than applying themselves, and errors render on validation
   rather than on first paint (§3).

   Five settings screens in one namespace, reached from the account menu, with the
   within-destination level as a tab strip derived from the route table — never a
   second set of destinations.

   Each screen has a real effect, stated on the screen: the setbacks are what
   every zone falls back to, the tariff windows price every figure on Energy and
   decide when the battery charges, the response order is who Sentinel rings, and
   a notification switched off stops arriving. */
(() => {
  const {
    Button, Badge, Input, Select, Checkbox, Switch, Slider, Avatar,
    Field, FieldLabel, FieldDescription, FieldError, Alert, AlertTitle, AlertDescription,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
  } = window.OriginDesignSystem_7c998b;

  function SettingsTabs({ path, onNavigate }) {
    return (
      <div className="hh-tabs" role="tablist" aria-label="Settings">
        {HEARTH_SETTINGS_NAV.map((route) => (
          <button type="button" key={route.path} className="hh-tab" role="tab"
            aria-current={route.path === path ? "page" : undefined} aria-selected={route.path === path}
            onClick={() => onNavigate(route.path)}>{route.label}</button>
        ))}
      </div>
    );
  }

  function SettingsPage({ path, title, lede, onNavigate, dirty, note, label, onCancel, onSave, children }) {
    return (
      <>
        <PageHead path={path} title={title} lede={lede} />
        <SettingsTabs path={path} onNavigate={onNavigate} />
        {children}
        <FormBar dirty={dirty} note={note} label={label} onCancel={onCancel} onSave={onSave} />
      </>
    );
  }

  function useStaged(initial) {
    const [values, setValues] = React.useState(initial);
    const [submitted, setSubmitted] = React.useState(false);
    const set = (id, value) => setValues((v) => Object.assign({}, v, { [id]: value }));
    return {
      values: values, set: set, setValues: setValues, submitted: submitted, setSubmitted: setSubmitted,
      dirty: JSON.stringify(values) !== JSON.stringify(initial),
      reset: () => { setValues(initial); setSubmitted(false); },
    };
  }

  /* ── Residence ───────────────────────────────────────────────────────── */
  function ResidenceSettingsScreen({ data, now, residence, policy, onNavigate, onSave }) {
    const form = useStaged({
      name: residence.name, short: residence.short, address: residence.address, timezone: residence.timezone,
      away: policy.awaySetbackC, night: policy.nightSetbackC, frost: policy.frostC, boost: String(policy.boostMinutes),
      lowBattery: String(policy.lowBatteryPercent),
    });
    const { values, set, submitted } = form;
    const errors = {};
    if (!values.name.trim()) errors.name = "The house's name appears on every export.";
    if (!values.short.trim()) errors.short = "The short name is what the sidebar and the toasts use.";
    if (!values.address.trim()) errors.address = "Give the address the response company holds.";
    if (!(values.away >= 10 && values.away <= 20)) errors.away = "Between 10 and 20 degrees.";
    if (!(values.night >= 12 && values.night <= 22)) errors.night = "Between 12 and 22 degrees.";
    if (!/^\d+$/.test(values.boost) || Number(values.boost) < 15 || Number(values.boost) > 240) errors.boost = "Between 15 and 240 minutes.";
    if (!/^\d+$/.test(values.lowBattery) || Number(values.lowBattery) < 5 || Number(values.lowBattery) > 50) errors.lowBattery = "Between 5 and 50 per cent.";
    const valid = Object.keys(errors).length === 0;
    const zones = zoneRows(data, now);

    return (
      <SettingsPage path="/settings" title="Residence" onNavigate={onNavigate}
        lede={"What the house is called, and the numbers every zone and every device falls back on."}
        dirty={form.dirty} label="Save residence"
        note={"The setbacks are read live by all " + zones.length + " zones — saving them moves Climate and Today at once."}
        onCancel={form.reset} onSave={() => { form.setSubmitted(true); if (valid) onSave(values); }}>
        <Band>
          <Card title="The house">
            <div className="hh-fields">
              <Field>
                <FieldLabel htmlFor="hh-rs-name">Name</FieldLabel>
                <Input id="hh-rs-name" value={values.name} invalid={submitted && !!errors.name} onChange={(e) => set("name", e.target.value)} />
                {submitted && errors.name ? <FieldError>{errors.name}</FieldError> : <FieldDescription>Written into the header of every export.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-rs-short">Short name</FieldLabel>
                <Input id="hh-rs-short" value={values.short} invalid={submitted && !!errors.short} onChange={(e) => set("short", e.target.value)} />
                {submitted && errors.short ? <FieldError>{errors.short}</FieldError> : <FieldDescription>Used in the sidebar and in every message the house sends.</FieldDescription>}
              </Field>
              <Field className="hh-fields__wide">
                <FieldLabel htmlFor="hh-rs-address">Address</FieldLabel>
                <Input id="hh-rs-address" value={values.address} invalid={submitted && !!errors.address} onChange={(e) => set("address", e.target.value)} />
                {submitted && errors.address ? <FieldError>{errors.address}</FieldError> : <FieldDescription>What {providerName(data.providers, "prv_05")} gives the police.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel>Timezone</FieldLabel>
                <Select aria-label="Timezone" value={values.timezone} onValueChange={(v) => set("timezone", v)}
                  items={[{ value: "Europe/London", label: "Europe/London" }, { value: "Europe/Dublin", label: "Europe/Dublin" }, { value: "UTC", label: "UTC" }]} />
                <FieldDescription>Every clock in Hearth, including the tariff windows and quiet hours.</FieldDescription>
              </Field>
            </div>
          </Card>
        </Band>

        <Band>
          <Card title="What the house falls back to" note="These are not a zone's setting — they are what a zone uses when nobody's preference applies.">
            <div className="hh-fields">
              <Field>
                <FieldLabel>Away setback</FieldLabel>
                <div style={{ display: "flex", alignItems: "center", gap: "var(--space-4)" }}>
                  <span style={{ flex: 1, minWidth: "8rem" }}><Slider min={10} max={20} step={0.5} value={values.away} aria-label="Away setback" onValueChange={(v) => set("away", Array.isArray(v) ? v[0] : v)} /></span>
                  <span className="hh-row__value" style={{ minWidth: "3.5rem" }}>{formatTemp(values.away)}°</span>
                </div>
                {submitted && errors.away ? <FieldError>{errors.away}</FieldError> : <FieldDescription>Held when nobody is home. It is {formatTemp(policy.awaySetbackC)}° today.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel>Night setback</FieldLabel>
                <div style={{ display: "flex", alignItems: "center", gap: "var(--space-4)" }}>
                  <span style={{ flex: 1, minWidth: "8rem" }}><Slider min={12} max={22} step={0.5} value={values.night} aria-label="Night setback" onValueChange={(v) => set("night", Array.isArray(v) ? v[0] : v)} /></span>
                  <span className="hh-row__value" style={{ minWidth: "3.5rem" }}>{formatTemp(values.night)}°</span>
                </div>
                {submitted && errors.night ? <FieldError>{errors.night}</FieldError> : <FieldDescription>Held once everybody home is asleep.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel>Frost protection</FieldLabel>
                <div style={{ display: "flex", alignItems: "center", gap: "var(--space-4)" }}>
                  <span style={{ flex: 1, minWidth: "8rem" }}><Slider min={4} max={12} step={0.5} value={values.frost} aria-label="Frost protection" onValueChange={(v) => set("frost", Array.isArray(v) ? v[0] : v)} /></span>
                  <span className="hh-row__value" style={{ minWidth: "3.5rem" }}>{formatTemp(values.frost)}°</span>
                </div>
                <FieldDescription>What the annexe holds all winter.</FieldDescription>
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-rs-boost">Boost lasts</FieldLabel>
                <Input id="hh-rs-boost" value={values.boost} invalid={submitted && !!errors.boost} onChange={(e) => set("boost", e.target.value)} />
                {submitted && errors.boost ? <FieldError>{errors.boost}</FieldError> : <FieldDescription>Minutes, at 1.5° over the target.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-rs-batt">Low battery under</FieldLabel>
                <Input id="hh-rs-batt" value={values.lowBattery} invalid={submitted && !!errors.lowBattery} onChange={(e) => set("lowBattery", e.target.value)} />
                {submitted && errors.lowBattery ? <FieldError>{errors.lowBattery}</FieldError> : <FieldDescription>Per cent. {devicesLowBattery(data.devices).length} devices are under {policy.lowBatteryPercent}% today.</FieldDescription>}
              </Field>
            </div>
          </Card>
        </Band>

        <Band>
          <Card tint title="The house as it stands" note="Read from the fixture, not from this form.">
            <Pairs items={[
              { label: "Built", value: residence.built },
              { label: "Bedrooms", value: residence.bedrooms },
              { label: "Floor area", value: residence.floorAreaSqm + " m²" },
              { label: "EPC", value: residence.epc },
              { label: "Rooms in Hearth", value: ROOMS.length },
              { label: "Devices", value: devicesInHouse(data.devices).length },
              { label: "Zones", value: zones.length },
              { label: "Ways in", value: data.entries.length },
            ]} />
          </Card>
        </Band>
      </SettingsPage>
    );
  }

  /* ── Tariff and meters ───────────────────────────────────────────────── */
  function TariffSettingsScreen({ data, now, tariff, policy, onNavigate, onSave }) {
    const form = useStaged({
      supplier: tariff.supplier, name: tariff.name,
      offFrom: tariff.windows[0].from, offTo: tariff.windows[0].to, offRate: String(tariff.windows[0].pencePerKwh),
      peakFrom: tariff.windows[1].from, peakTo: tariff.windows[1].to, peakRate: String(tariff.windows[1].pencePerKwh),
      standardRate: String(tariff.windows[2].pencePerKwh),
      exportRate: String(tariff.exportPencePerKwh), standing: String(policy.standingChargePence),
    });
    const { values, set, submitted } = form;
    const errors = {};
    ["offFrom", "offTo", "peakFrom", "peakTo"].forEach((key) => { if (!/^\d\d:\d\d$/.test(values[key])) errors[key] = "Use HH:MM."; });
    ["offRate", "peakRate", "standardRate", "exportRate", "standing"].forEach((key) => { if (!/^\d+(\.\d+)?$/.test(values[key])) errors[key] = "A number of pence."; });
    if (!errors.offRate && !errors.standardRate && Number(values.offRate) >= Number(values.standardRate)) errors.offRate = "The off-peak rate has to be cheaper than the standard one, or nothing about the battery makes sense.";
    const valid = Object.keys(errors).length === 0;
    const days = data.energy;

    return (
      <SettingsPage path="/settings/tariff" title="Tariff and meters" onNavigate={onNavigate}
        lede="The rates every cost in Hearth is worked out from, and the windows the battery and the routines read."
        dirty={form.dirty} label="Save tariff"
        note={"Saving re-prices all " + days.length + " days on Energy and moves the battery's advice with them."}
        onCancel={form.reset} onSave={() => { form.setSubmitted(true); if (valid) onSave(values); }}>
        <Band>
          <Card title="Supplier">
            <div className="hh-fields">
              <Field>
                <FieldLabel htmlFor="hh-ts-supplier">Supplier</FieldLabel>
                <Input id="hh-ts-supplier" value={values.supplier} onChange={(e) => set("supplier", e.target.value)} />
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-name">Tariff name</FieldLabel>
                <Input id="hh-ts-name" value={values.name} onChange={(e) => set("name", e.target.value)} />
                <FieldDescription>Shown at the top of Energy.</FieldDescription>
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-standing">Standing charge</FieldLabel>
                <Input id="hh-ts-standing" value={values.standing} invalid={submitted && !!errors.standing} onChange={(e) => set("standing", e.target.value)} />
                {submitted && errors.standing ? <FieldError>{errors.standing}</FieldError> : <FieldDescription>Pence a day, added to every daily cost.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-export">Export rate</FieldLabel>
                <Input id="hh-ts-export" value={values.exportRate} invalid={submitted && !!errors.exportRate} onChange={(e) => set("exportRate", e.target.value)} />
                {submitted && errors.exportRate ? <FieldError>{errors.exportRate}</FieldError> : <FieldDescription>Pence a kWh for what the roof sends back.</FieldDescription>}
              </Field>
            </div>
          </Card>
        </Band>

        <Band>
          <Card title="Windows" note="Three rates. The strip below is drawn from these fields as you change them, so it cannot disagree with what you save.">
            <div className="hh-fields">
              <Field>
                <FieldLabel htmlFor="hh-ts-off-from">Off-peak from</FieldLabel>
                <Input id="hh-ts-off-from" value={values.offFrom} invalid={submitted && !!errors.offFrom} onChange={(e) => set("offFrom", e.target.value)} />
                {submitted && errors.offFrom ? <FieldError>{errors.offFrom}</FieldError> : null}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-off-to">Off-peak until</FieldLabel>
                <Input id="hh-ts-off-to" value={values.offTo} invalid={submitted && !!errors.offTo} onChange={(e) => set("offTo", e.target.value)} />
                {submitted && errors.offTo ? <FieldError>{errors.offTo}</FieldError> : null}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-off-rate">Off-peak rate</FieldLabel>
                <Input id="hh-ts-off-rate" value={values.offRate} invalid={submitted && !!errors.offRate} onChange={(e) => set("offRate", e.target.value)} />
                {submitted && errors.offRate ? <FieldError>{errors.offRate}</FieldError> : <FieldDescription>Pence a kWh.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-peak-from">Peak from</FieldLabel>
                <Input id="hh-ts-peak-from" value={values.peakFrom} invalid={submitted && !!errors.peakFrom} onChange={(e) => set("peakFrom", e.target.value)} />
                {submitted && errors.peakFrom ? <FieldError>{errors.peakFrom}</FieldError> : null}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-peak-to">Peak until</FieldLabel>
                <Input id="hh-ts-peak-to" value={values.peakTo} invalid={submitted && !!errors.peakTo} onChange={(e) => set("peakTo", e.target.value)} />
                {submitted && errors.peakTo ? <FieldError>{errors.peakTo}</FieldError> : null}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-peak-rate">Peak rate</FieldLabel>
                <Input id="hh-ts-peak-rate" value={values.peakRate} invalid={submitted && !!errors.peakRate} onChange={(e) => set("peakRate", e.target.value)} />
                {submitted && errors.peakRate ? <FieldError>{errors.peakRate}</FieldError> : <FieldDescription>Pence a kWh.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ts-std-rate">Standard rate</FieldLabel>
                <Input id="hh-ts-std-rate" value={values.standardRate} invalid={submitted && !!errors.standardRate} onChange={(e) => set("standardRate", e.target.value)} />
                {submitted && errors.standardRate ? <FieldError>{errors.standardRate}</FieldError> : <FieldDescription>Everything outside the two windows.</FieldDescription>}
              </Field>
            </div>
            <div style={{ marginTop: "var(--space-5)" }}>
              <DayStrip now={now} label="The windows as entered"
                toneOf={(hour) => {
                  const inWindow = (from, to) => {
                    if (!/^\d\d:\d\d$/.test(from) || !/^\d\d:\d\d$/.test(to)) return false;
                    const f = parseClock(from) / 60, t = parseClock(to) / 60;
                    return f <= t ? hour >= f && hour < t : hour >= f || hour < t;
                  };
                  if (inWindow(values.offFrom, values.offTo)) return "off";
                  if (inWindow(values.peakFrom, values.peakTo)) return "peak";
                  return undefined;
                }} />
              <div style={{ marginTop: "var(--space-4)" }}>
                <Legend items={[
                  { label: "Off-peak, " + values.offRate + "p", kind: "off" },
                  { label: "Peak, " + values.peakRate + "p", kind: "peak" },
                  { label: "Standard, " + values.standardRate + "p", kind: "soft" },
                ]} />
              </div>
            </div>
          </Card>
        </Band>

        <Band>
          <Card tint title="Meters" note="Read from the device list; a meter is a device like any other.">
            <Rows>
              {data.devices.filter((d) => d.kind === "meter").map((meter) => (
                <Row key={meter.id} title={<button type="button" className="hh-linkbtn" onClick={() => onNavigate("/devices/" + meter.id)}>{meter.name}</button>}
                  note={roomName(meter.room) + " · last read " + formatWhen(meter.lastSeenAt, now)}
                  value={<StatusBadge values={DEVICE_STATUS} value={meter.status} />} />
              ))}
            </Rows>
          </Card>
        </Band>
      </SettingsPage>
    );
  }

  /* ── Alarm and response ──────────────────────────────────────────────── */
  function SecuritySettingsScreen({ data, now, policy, response, onNavigate, onSave }) {
    const form = useStaged({
      darkAfter: policy.darkAfter,
      responseNumber: RESIDENCE.responseNumber,
      order: response.order.slice(),
      autoArm: response.autoArm,
      autoArmAt: response.autoArmAt,
      notifyPolice: response.notifyPolice,
    });
    const { values, set, submitted } = form;
    const errors = {};
    if (!/^\d\d:\d\d$/.test(values.darkAfter)) errors.darkAfter = "Use HH:MM.";
    if (!/^\d\d:\d\d$/.test(values.autoArmAt)) errors.autoArmAt = "Use HH:MM.";
    if (!values.order.length) errors.order = "Somebody has to be called.";
    const valid = Object.keys(errors).length === 0;
    const callable = activeMembers(data.members).filter((m) => m.role === "owner" || m.role === "adult" || m.role === "staff");
    const insecure = entriesInsecure(data, now);

    const move = (id, delta) => {
      const list = values.order.slice();
      const index = list.indexOf(id);
      const to = index + delta;
      if (index < 0 || to < 0 || to >= list.length) return;
      list.splice(to, 0, list.splice(index, 1)[0]);
      set("order", list);
    };

    return (
      <SettingsPage path="/settings/security" title="Alarm and response" onNavigate={onNavigate}
        lede={"Who is rung when the alarm goes, and the hour after which an unlocked door counts against the house. " + insecure.length + " ways in are insecure right now on those rules."}
        dirty={form.dirty} label="Save alarm settings"
        note={"The dark hour is what makes an unlocked door insecure — saving it moves the count on Today, Security and the sidebar."}
        onCancel={form.reset} onSave={() => { form.setSubmitted(true); if (valid) onSave(values); }}>
        <Band>
          <Card title="Rules">
            <div className="hh-fields">
              <Field>
                <FieldLabel htmlFor="hh-ss-dark">Dark after</FieldLabel>
                <Input id="hh-ss-dark" value={values.darkAfter} invalid={submitted && !!errors.darkAfter} onChange={(e) => set("darkAfter", e.target.value)} />
                {submitted && errors.darkAfter ? <FieldError>{errors.darkAfter}</FieldError> : <FieldDescription>After this, an unlocked door or an open window counts as insecure even with the alarm off.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ss-number">Response number</FieldLabel>
                <Input id="hh-ss-number" value={values.responseNumber} onChange={(e) => set("responseNumber", e.target.value)} />
                <FieldDescription>{providerName(data.providers, "prv_05")}'s control room.</FieldDescription>
              </Field>
              <Field>
                <FieldLabel>Arm automatically</FieldLabel>
                <span className="hh-person">
                  <Checkbox checked={values.autoArm} aria-label="Arm the house automatically" onCheckedChange={(v) => set("autoArm", v)} />
                  <span className="hh-pair__value">Arm — home once everybody is asleep</span>
                </span>
                <FieldDescription>Submitted with this form, like every control on this screen.</FieldDescription>
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ss-at">If nobody has armed it by</FieldLabel>
                <Input id="hh-ss-at" value={values.autoArmAt} invalid={submitted && !!errors.autoArmAt} onChange={(e) => set("autoArmAt", e.target.value)} />
                {submitted && errors.autoArmAt ? <FieldError>{errors.autoArmAt}</FieldError> : <FieldDescription>Only applies when nothing is open — anything open would fault the alarm instead.</FieldDescription>}
              </Field>
            </div>
          </Card>
        </Band>

        <Band>
          <Card title="Who Sentinel rings, in order" note="First to answer takes the call. Only adults and staff with live access can be on the list.">
            <Rows>
              {values.order.map((id, index) => {
                const member = memberById(data.members, id);
                return (
                  <Row key={id} title={<PersonCell name={member.name} avatar={member.avatar} size={28} meta={labelOf(HOUSEHOLD_ROLES, member.role) + " · " + (member.phone || "no number on the record")} />}
                    note={index === 0 ? "Called first" : index === values.order.length - 1 ? "Called last, then the police" : "Called " + (index + 1) + (index === 1 ? "nd" : index === 2 ? "rd" : "th")}>
                    <span className="hh-actions__row">
                      <Button variant="ghost" size="icon-sm" aria-label={"Move " + member.name + " earlier"} disabled={index === 0} onClick={() => move(id, -1)}><Icon name="arrow-up" size={14} /></Button>
                      <Button variant="ghost" size="icon-sm" aria-label={"Move " + member.name + " later"} disabled={index === values.order.length - 1} onClick={() => move(id, 1)}><Icon name="arrow-down" size={14} /></Button>
                      <Button variant="ghost" size="sm" disabled={values.order.length === 1} onClick={() => set("order", values.order.filter((x) => x !== id))}>Remove</Button>
                    </span>
                  </Row>
                );
              })}
              {callable.filter((m) => values.order.indexOf(m.id) < 0).map((member) => (
                <Row key={member.id} recessed title={<PersonCell name={member.name} avatar={member.avatar} size={28} meta={labelOf(HOUSEHOLD_ROLES, member.role) + " · not on the list"} />}
                  note={member.phone ? "Reachable on " + member.phone : "No number on the record, so they cannot be called"}>
                  <Button variant="outline" size="sm" disabled={!member.phone} onClick={() => set("order", values.order.concat([member.id]))}>Add</Button>
                </Row>
              ))}
            </Rows>
            {submitted && errors.order ? <p className="hh-footnote hh-danger" style={{ marginTop: "var(--space-3)" }}>{errors.order}</p> : null}
            <div style={{ marginTop: "var(--space-4)", paddingTop: "var(--space-3)", borderTop: "1px solid var(--border)" }}>
              <span className="hh-person">
                <Checkbox checked={values.notifyPolice} aria-label="Escalate to the police" onCheckedChange={(v) => set("notifyPolice", v)} />
                <span className="hh-pair__value">Escalate to the police if nobody answers</span>
              </span>
            </div>
          </Card>
        </Band>
      </SettingsPage>
    );
  }

  /* ── Notifications ───────────────────────────────────────────────────── */
  function NotificationSettingsScreen({ data, prefs, residence, onNavigate, onSave }) {
    const form = useStaged({ rows: prefs.rows.map((r) => Object.assign({}, r)), from: prefs.quietFrom, to: prefs.quietTo });
    const { values, set, submitted } = form;
    const setRow = (id, channel, on) => form.setValues((v) => Object.assign({}, v, { rows: v.rows.map((r) => (r.id === id ? Object.assign({}, r, { [channel]: on }) : r)) }));
    const errors = {};
    if (!/^\d\d:\d\d$/.test(values.from)) errors.from = "Use HH:MM.";
    if (!/^\d\d:\d\d$/.test(values.to)) errors.to = "Use HH:MM.";
    const valid = Object.keys(errors).length === 0;
    const silent = values.rows.filter((r) => !r.push && !r.sms && !r.email);

    return (
      <SettingsPage path="/settings/notifications" title="Notifications" onNavigate={onNavigate}
        lede="What the house tells you about, and when it leaves you alone. Each person also chooses whether they hear about the alarm and about deliveries on their own screen."
        dirty={form.dirty} label="Save notifications"
        note={silent.length ? silent.length + " of " + values.rows.length + " events would tell nobody anything." : "Every event reaches somebody by at least one channel."}
        onCancel={form.reset} onSave={() => { form.setSubmitted(true); if (valid) onSave(values); }}>
        <Band>
          <Card title="Events" note="Checkboxes, because they are submitted with this form rather than applying on their own.">
            <TableWrap>
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Event</TableHead>
                    <TableHead>Push</TableHead>
                    <TableHead>Text</TableHead>
                    <TableHead>Email</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {values.rows.map((row) => (
                    <TableRow key={row.id}>
                      <TableCell>
                        <span className="hh-actions__row">
                          {row.label}
                          {!row.push && !row.sms && !row.email ? <Badge size="sm" variant="secondary">Silent</Badge> : null}
                        </span>
                      </TableCell>
                      {["push", "sms", "email"].map((channel) => (
                        <TableCell key={channel}>
                          <Checkbox checked={!!row[channel]} aria-label={row.label + " — " + channel}
                            onCheckedChange={(on) => setRow(row.id, channel, on)} />
                        </TableCell>
                      ))}
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </TableWrap>
          </Card>
        </Band>
        <Band>
          <Card title="Quiet hours" note={"Nothing is pushed or texted between these times, " + residence.timezone + ". The alarm ignores quiet hours."}>
            <div className="hh-fields">
              <Field>
                <FieldLabel htmlFor="hh-ns-from">From</FieldLabel>
                <Input id="hh-ns-from" value={values.from} invalid={submitted && !!errors.from} onChange={(e) => set("from", e.target.value)} />
                {submitted && errors.from ? <FieldError>{errors.from}</FieldError> : <FieldDescription>24-hour clock.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ns-to">Until</FieldLabel>
                <Input id="hh-ns-to" value={values.to} invalid={submitted && !!errors.to} onChange={(e) => set("to", e.target.value)} />
                {submitted && errors.to ? <FieldError>{errors.to}</FieldError> : null}
              </Field>
            </div>
          </Card>
        </Band>
        <Band>
          <Card tint title="Who hears what" note="Read from each person's own preferences, which are edited on their screen.">
            <Rows>
              {activeMembers(data.members).map((member) => (
                <Row key={member.id} title={<PersonCell name={member.name} avatar={member.avatar} size={28} meta={labelOf(HOUSEHOLD_ROLES, member.role)} onOpen={() => onNavigate("/people/" + member.id)} />}
                  note={[member.prefs.notifyAlarm ? "the alarm" : null, member.prefs.notifyDeliveries ? "deliveries" : null].filter(Boolean).join(" and ") || "nothing — they have both switched off"} />
              ))}
            </Rows>
          </Card>
        </Band>
      </SettingsPage>
    );
  }

  /* ── Data and privacy ────────────────────────────────────────────────── */
  function DataSettingsScreen({ data, now, dataPrefs, onNavigate, onSave, onExportAll }) {
    const form = useStaged({
      keepLogDays: String(dataPrefs.keepLogDays),
      keepClipsDays: String(dataPrefs.keepClipsDays),
      shareWithProviders: dataPrefs.shareWithProviders,
      presenceHistory: dataPrefs.presenceHistory,
    });
    const { values, set, submitted } = form;
    const errors = {};
    if (!/^\d+$/.test(values.keepLogDays) || Number(values.keepLogDays) < 7 || Number(values.keepLogDays) > 730) errors.keepLogDays = "Between 7 and 730 days.";
    if (!/^\d+$/.test(values.keepClipsDays) || Number(values.keepClipsDays) < 1 || Number(values.keepClipsDays) > 90) errors.keepClipsDays = "Between 1 and 90 days.";
    const valid = Object.keys(errors).length === 0;
    const cameras = data.devices.filter((d) => d.kind === "camera");

    return (
      <SettingsPage path="/settings/data" title="Data and privacy" onNavigate={onNavigate}
        lede="A house that watches itself keeps records about the people in it. This is how long it keeps them, and who else sees them."
        dirty={form.dirty} label="Save data settings"
        note={"The log holds " + data.events.length + " entries today; anything older than the window is deleted rather than hidden."}
        onCancel={form.reset} onSave={() => { form.setSubmitted(true); if (valid) onSave(values); }}>
        <Band>
          <Card title="How long the house remembers">
            <div className="hh-fields">
              <Field>
                <FieldLabel htmlFor="hh-ds-log">Keep the log for</FieldLabel>
                <Input id="hh-ds-log" value={values.keepLogDays} invalid={submitted && !!errors.keepLogDays} onChange={(e) => set("keepLogDays", e.target.value)} />
                {submitted && errors.keepLogDays ? <FieldError>{errors.keepLogDays}</FieldError> : <FieldDescription>Days. Everything on the Log screen, including who came and went.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-ds-clips">Keep camera clips for</FieldLabel>
                <Input id="hh-ds-clips" value={values.keepClipsDays} invalid={submitted && !!errors.keepClipsDays} onChange={(e) => set("keepClipsDays", e.target.value)} />
                {submitted && errors.keepClipsDays ? <FieldError>{errors.keepClipsDays}</FieldError> : <FieldDescription>Days, across {cameras.length} cameras.</FieldDescription>}
              </Field>
              <Field className="hh-fields__wide">
                <FieldLabel>What is shared</FieldLabel>
                <div style={{ display: "flex", flexDirection: "column", gap: "var(--space-2-5)" }}>
                  <span className="hh-person">
                    <Checkbox checked={values.shareWithProviders} aria-label="Share device history with providers" onCheckedChange={(v) => set("shareWithProviders", v)} />
                    <span className="hh-pair__value">Let a provider see the device history for the job they are on</span>
                  </span>
                  <span className="hh-person">
                    <Checkbox checked={values.presenceHistory} aria-label="Keep presence history" onCheckedChange={(v) => set("presenceHistory", v)} />
                    <span className="hh-pair__value">Keep a record of who was home and when</span>
                  </span>
                </div>
                <FieldDescription>
                  With presence history off, the Log still records what the house did — it stops recording which person did it.
                </FieldDescription>
              </Field>
            </div>
          </Card>
        </Band>
        <Band>
          <Card tint title="Take it with you" note="Everything Hearth holds about this house, in one file.">
            <Rows>
              <Row title="The whole record" value={<Button variant="outline" size="sm" onClick={() => onExportAll("CSV")}>Export</Button>}
                note={data.devices.length + " devices, " + data.events.length + " log entries, " + data.bills.length + " bills, " + data.jobs.length + " jobs, " + data.members.length + " people"} />
              <Row title="Providers who hold a way in" value={data.providers.filter((p) => p.keyHolder).length}
                note={data.providers.filter((p) => p.keyHolder).map((p) => p.name).join(", ") + " — revoke on Access"} />
            </Rows>
          </Card>
        </Band>
      </SettingsPage>
    );
  }

  /* ── Your profile ────────────────────────────────────────────────────── */
  const PHOTO_CHOICES = ["../../assets/img/avatar-f-7.jpg", "../../assets/img/avatar-f-9.jpg", "../../assets/img/avatar-f-11.jpg", "../../assets/img/avatar-f-13.jpg"];
  const MAX_PHOTO_KB = 2048;

  function PhotoUpload({ value, name, fileName, onPick, onClear, error, onError }) {
    const [over, setOver] = React.useState(false);
    const inputRef = React.useRef(null);
    const take = (file) => {
      if (!file) return;
      if (file.type.indexOf("image/") !== 0) return onError("That is a " + (file.type || "file with no type") + ". Use a JPEG or a PNG.");
      if (file.size / 1024 > MAX_PHOTO_KB) return onError("That photograph is " + Math.round((file.size / 1024 / 1024) * 10) / 10 + " MB. The limit is 2 MB.");
      const reader = new FileReader();
      reader.onload = () => { onError(null); onPick(String(reader.result), file.name); };
      reader.onerror = () => onError("That file could not be read. Try saving it again.");
      reader.readAsDataURL(file);
    };
    return (
      <div className="hh-upload">
        <span className="hh-preview">
          <Avatar size={72} src={value || undefined} alt={value ? "Your photograph" : ""} fallback={initialsOf(name)} />
          <span className="hh-footnote">{value ? (fileName || "On the household record") : "No photograph"}</span>
        </span>
        <label className="hh-drop" data-over={over ? "true" : "false"} data-invalid={error ? "true" : "false"}
          onDragOver={(e) => { e.preventDefault(); setOver(true); }}
          onDragLeave={() => setOver(false)}
          onDrop={(e) => { e.preventDefault(); setOver(false); take(e.dataTransfer.files && e.dataTransfer.files[0]); }}>
          <Icon name="image-up" size={20} />
          <span className="hh-drop__title">Drop a photograph here, or choose a file</span>
          <span className="hh-footnote">JPEG or PNG, up to 2 MB. It is what the household sees beside your name.</span>
          <input ref={inputRef} type="file" accept="image/jpeg,image/png" aria-label="Upload a photograph"
            onChange={(e) => { take(e.target.files && e.target.files[0]); e.target.value = ""; }} />
          <span className="hh-actions__row" style={{ marginTop: "var(--space-1)" }}>
            <Button size="sm" variant="outline" onClick={(e) => { e.preventDefault(); inputRef.current && inputRef.current.click(); }}>Choose a file</Button>
            {value ? <Button size="sm" variant="ghost" onClick={(e) => { e.preventDefault(); onError(null); onClear(); }}>Remove photograph</Button> : null}
          </span>
        </label>
      </div>
    );
  }

  function ProfileScreen({ data, now, viewer, onNavigate, onSave }) {
    const initial = { name: viewer.name, email: viewer.email || "", phone: viewer.phone || "", avatar: viewer.avatar || null };
    const [values, setValues] = React.useState(initial);
    const [fileName, setFileName] = React.useState(null);
    const [photoError, setPhotoError] = React.useState(null);
    const [submitted, setSubmitted] = React.useState(false);
    React.useEffect(() => { setValues(initial); setFileName(null); setPhotoError(null); setSubmitted(false); }, [viewer]);
    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 (!values.name.trim() || values.name.trim().split(/\s+/).length < 2) errors.name = "Give the name the household sees.";
    if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(values.email.trim())) errors.email = "Enter a valid email address.";
    if (values.phone && !/^[\d ]{9,}$/.test(values.phone.trim())) errors.phone = "Enter a contact number, or leave it blank.";
    const valid = Object.keys(errors).length === 0;
    const inResponse = true;

    return (
      <>
        <PageHead path="/account/profile" title="Your profile"
          lede="Your name, your photograph and how the house reaches you. Your comfort preference, your bedtime and what the house does when you arrive are on your own person screen."
          actions={<Button variant="outline" size="sm" onClick={() => onNavigate("/people/" + viewer.id)}>Your preferences</Button>} />

        <Band>
          <Card title="Photograph" note="Shown beside your name in the log, in the response order and on every screen that names you.">
            <PhotoUpload value={values.avatar} name={values.name} fileName={fileName} error={photoError} onError={setPhotoError}
              onPick={(dataUrl, name) => { set("avatar", dataUrl); setFileName(name); }}
              onClear={() => { set("avatar", null); setFileName(null); }} />
            {photoError ? <p className="hh-footnote hh-danger" style={{ marginTop: "var(--space-3)" }}>{photoError}</p> : null}
            <div className="hh-photos" style={{ marginTop: "var(--space-4)" }}>
              <span className="hh-footnote" style={{ width: "100%" }}>Or use one already on the household record:</span>
              {PHOTO_CHOICES.map((src) => (
                <button type="button" key={src} className="hh-photo" aria-pressed={values.avatar === src}
                  aria-label={values.avatar === src ? "Current photograph, selected" : "Use this photograph"}
                  onClick={() => { setPhotoError(null); set("avatar", src); setFileName(null); }}>
                  <Avatar size={46} src={src} alt="" fallback={initialsOf(values.name)} />
                </button>
              ))}
            </div>
          </Card>
        </Band>

        <Band>
          <Card title="How the house reaches you">
            <div className="hh-fields">
              <Field>
                <FieldLabel htmlFor="hh-me-name">Name</FieldLabel>
                <Input id="hh-me-name" value={values.name} invalid={submitted && !!errors.name} onChange={(e) => set("name", e.target.value)} />
                {submitted && errors.name ? <FieldError>{errors.name}</FieldError> : <FieldDescription>Saving rewrites your name everywhere the log already names you.</FieldDescription>}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-me-email">Email</FieldLabel>
                <Input id="hh-me-email" type="email" autoComplete="email" value={values.email} invalid={submitted && !!errors.email} onChange={(e) => set("email", e.target.value)} />
                {submitted && errors.email ? <FieldError>{errors.email}</FieldError> : null}
              </Field>
              <Field>
                <FieldLabel htmlFor="hh-me-phone">Phone</FieldLabel>
                <Input id="hh-me-phone" value={values.phone} invalid={submitted && !!errors.phone} onChange={(e) => set("phone", e.target.value)} />
                {submitted && errors.phone ? <FieldError>{errors.phone}</FieldError> : <FieldDescription>{inResponse ? "Sentinel Response calls this number first when the alarm goes." : "Not on the response list."}</FieldDescription>}
              </Field>
            </div>
          </Card>
        </Band>

        <Band>
          <Card tint title="Set by the household" note="Your role decides what you can do; only the owner changes it, on People.">
            <Pairs items={[
              { label: "Role", value: labelOf(HOUSEHOLD_ROLES, viewer.role) },
              { label: "Standing", value: <StatusBadge values={MEMBER_STATUS} value={viewer.status} /> },
              { label: "May unlock doors", value: ROLE_CAN_UNLOCK[viewer.role] ? "Yes" : "No" },
              { label: "May arm the alarm", value: ROLE_CAN_ARM[viewer.role] ? "Yes" : "No" },
              { label: "May hand out access", value: ROLE_CAN_GRANT_ACCESS[viewer.role] ? "Yes" : "No" },
              { label: "Your preferences", value: <button type="button" className="hh-linkbtn" onClick={() => onNavigate("/people/" + viewer.id)}>On your person screen</button> },
            ]} />
          </Card>
        </Band>

        <FormBar dirty={dirty} label="Save profile"
          note={values.avatar && values.avatar.indexOf("data:") === 0 ? "The photograph you dropped is a preview until you save." : "Saving rewrites your name and photograph everywhere the house names you."}
          onCancel={() => { setValues(initial); setFileName(null); setPhotoError(null); setSubmitted(false); }}
          onSave={() => { setSubmitted(true); if (valid) onSave(values); }} />
      </>
    );
  }

  Object.assign(window, {
    SettingsTabs, SettingsPage, useStaged, PhotoUpload, PHOTO_CHOICES,
    ResidenceSettingsScreen, TariffSettingsScreen, SecuritySettingsScreen, NotificationSettingsScreen, DataSettingsScreen, ProfileScreen,
  });
})();
