// HR settings — currently one policy: where staff are allowed to sign in from.
//
// The rule has two halves the admin fills in separately (a list of company IP
// addresses, a list of areas on the map) and a mode saying which of them is
// mandatory. Nothing takes effect until a mode other than "off" is picked, and
// even then `auditOnly` records refusals without turning anyone away.
//
// Every module script shares one global scope (Babel standalone rewrites the
// top-level const/let to var, which is why the repeated `const { useState }`
// across component files does not throw). A bare `Switch` here would therefore
// overwrite product-form.jsx's identically-named one, so this carries a prefix.
function GeoSwitch({ label, value, onChange, hint }) {
  return (
    <button type="button" className="switchrow" onClick={() => onChange(!value)} aria-pressed={value}>
      <span className={"sw" + (value ? " on" : "")}><i></i></span>
      <span className="switchrow-text">
        <b>{label}</b>
        {hint ? <small>{hint}</small> : null}
      </span>
    </button>
  );
}

// Leaflet is fetched on demand (window.loadLeaflet, defined in index.html) —
// it is ~85 KB that only this panel needs, so the app no longer pays for it on
// every load. If it does not arrive, the map is replaced by the plain
// coordinate inputs rather than the screen breaking — areas stay fully editable
// without it.
function AreaMap({ areas, draft, onPick }) {
  const { useEffect, useRef, useState } = React;
  const boxRef = useRef(null);
  const mapRef = useRef(null);
  const layerRef = useRef(null);
  // "loading" until the CDN answers, then "ready" or "failed". The map box is
  // only rendered in "ready", so the construction effect below always finds a
  // real element to measure.
  const [phase, setPhase] = useState(() => (window.L ? "ready" : "loading"));
  const ready = phase === "ready";

  useEffect(() => {
    if (ready) return;
    let live = true;
    const load = window.loadLeaflet;
    if (!load) { setPhase("failed"); return; }
    load().then(() => { if (live) setPhase("ready"); })
          .catch(() => { if (live) setPhase("failed"); });
    return () => { live = false; };
  }, [ready]);

  useEffect(() => {
    if (!ready || !window.L || !boxRef.current || mapRef.current) return;
    const start = draft && draft.lat != null ? [draft.lat, draft.lng]
      : areas.length ? [areas[0].lat, areas[0].lng]
      : [31.9539, 35.9106]; // Amman, so an empty map still opens somewhere useful
    const map = L.map(boxRef.current, { scrollWheelZoom: false }).setView(start, 15);
    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      maxZoom: 19,
      attribution: "© OpenStreetMap"
    }).addTo(map);
    map.on("click", (e) => onPick(e.latlng.lat, e.latlng.lng));
    layerRef.current = L.layerGroup().addTo(map);
    mapRef.current = map;
    // Leaflet measures the container at construction, while CSS is still
    // laying it out; without a re-measure the tiles paint into a zero-height
    // box. A ResizeObserver fires on attach, which covers that, and keeps
    // covering it when the box changes width for a reason Leaflet's own
    // trackResize never sees — a density switch, or the tab being re-shown.
    const ro = new ResizeObserver(() => map.invalidateSize());
    ro.observe(boxRef.current);
    return () => { ro.disconnect(); map.remove(); mapRef.current = null; };
  }, [ready]);

  // Redraw every circle whenever a saved area or the pending one changes.
  useEffect(() => {
    const map = mapRef.current, layer = layerRef.current;
    if (!map || !layer) return;
    layer.clearLayers();
    areas.forEach((a) => {
      L.circle([a.lat, a.lng], {
        radius: a.radius,
        color: a.active ? "#2f7d4f" : "#9aa39a",
        weight: 2,
        fillOpacity: a.active ? 0.12 : 0.05
      }).addTo(layer).bindTooltip(a.label);
    });
    if (draft && draft.lat != null) {
      L.circle([draft.lat, draft.lng], {
        radius: draft.radius || 200,
        color: "#c98a12",
        weight: 2,
        dashArray: "5 4",
        fillOpacity: 0.1
      }).addTo(layer);
      L.marker([draft.lat, draft.lng]).addTo(layer);
      map.panTo([draft.lat, draft.lng]);
    }
    // `ready` is a dep too: the map is built in the same commit that flips it,
    // so without it the first draw would wait for the next edit to an area.
  }, [ready, JSON.stringify(areas), JSON.stringify(draft)]);

  if (phase === "failed") {
    return <p className="formhint">{T("hrset.mapUnavailable")}</p>;
  }
  if (phase === "loading") {
    return <p className="formhint">{T("hrset.loading")}</p>;
  }
  return <div ref={boxRef} className="geo-map" />;
}

function HrSettings() {
  const { useState, useEffect } = React;
  const [cfg, setCfg] = useState(null);
  const [branches, setBranches] = useState([]);
  const [attempts, setAttempts] = useState([]);
  const [busy, setBusy] = useState(false);

  // Pending "add a network" row.
  const [netLabel, setNetLabel] = useState("");
  const [netCidr, setNetCidr] = useState("");
  const [netBranch, setNetBranch] = useState("");
  const [netErr, setNetErr] = useState("");

  // Pending "add an area" row. lat/lng arrive from a map click or the browser.
  const [areaLabel, setAreaLabel] = useState("");
  const [areaLat, setAreaLat] = useState(null);
  const [areaLng, setAreaLng] = useState(null);
  const [areaRadius, setAreaRadius] = useState(250);
  const [areaBranch, setAreaBranch] = useState("");
  const [areaErr, setAreaErr] = useState("");
  const [locating, setLocating] = useState(false);

  const reload = () => API.loginRestriction.get().then(setCfg);

  useEffect(() => {
    reload().catch((ex) => notify(T("hrset.loadFailed") + " — " + ex.message));
    API.inventory.branches.list().then(setBranches).catch(() => {});
    API.loginRestriction.attempts.list().then(setAttempts).catch(() => {});
  }, []);

  if (!cfg) return <section className="card"><p className="dim">{T("hrset.loading")}</p></section>;

  const MODES = [
    { id: "off", label: T("hrset.modeOff"), hint: T("hrset.modeOffHint") },
    { id: "ip", label: T("hrset.modeIp"), hint: T("hrset.modeIpHint") },
    { id: "location", label: T("hrset.modeLocation"), hint: T("hrset.modeLocationHint") },
    { id: "any", label: T("hrset.modeAny"), hint: T("hrset.modeAnyHint") },
    { id: "all", label: T("hrset.modeAll"), hint: T("hrset.modeAllHint") }
  ];
  const activeMode = MODES.find((m) => m.id === cfg.mode) || MODES[0];

  const save = (patch) => {
    setBusy(true);
    API.loginRestriction.update(patch)
      .then(setCfg)
      .catch((ex) => notify(T("hrset.saveFailed") + " — " + ex.message))
      .then(() => setBusy(false));
  };

  const addNetwork = () => {
    setNetErr("");
    if (!netLabel.trim()) return setNetErr(T("hrset.nameRequired"));
    if (!netCidr.trim()) return setNetErr(T("hrset.addressRequired"));
    API.loginRestriction.networks
      .create({ label: netLabel.trim(), cidr: netCidr.trim(), branch: netBranch || null })
      .then(() => { setNetLabel(""); setNetCidr(""); setNetBranch(""); return reload(); })
      .catch((ex) => setNetErr(readErr(ex, T("hrset.addressInvalid"))));
  };

  const addArea = () => {
    setAreaErr("");
    if (!areaLabel.trim()) return setAreaErr(T("hrset.nameRequired"));
    if (areaLat == null || areaLng == null) return setAreaErr(T("hrset.pointRequired"));
    API.loginRestriction.areas
      .create({ label: areaLabel.trim(), lat: areaLat, lng: areaLng, radius: areaRadius, branch: areaBranch || null })
      .then(() => { setAreaLabel(""); setAreaLat(null); setAreaLng(null); setAreaBranch(""); return reload(); })
      .catch((ex) => setAreaErr(readErr(ex, T("hrset.saveFailed"))));
  };

  // The server answers {details: {field: [msg]}}; show the first real message
  // rather than the JSON blob the generic error carries.
  const readErr = (ex, fallback) => {
    const details = ex && ex.data && ex.data.details;
    if (details) {
      const first = Object.keys(details)[0];
      const msg = details[first];
      if (msg) return Array.isArray(msg) ? msg[0] : String(msg);
    }
    return (ex && ex.data && ex.data.error) || fallback;
  };

  const useMyLocation = () => {
    if (!navigator.geolocation) return setAreaErr(T("hrset.noGeolocation"));
    setLocating(true);
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        setAreaLat(round6(pos.coords.latitude));
        setAreaLng(round6(pos.coords.longitude));
        setLocating(false);
      },
      () => { setLocating(false); setAreaErr(T("hrset.locationDenied")); },
      { enableHighAccuracy: true, timeout: 10000 }
    );
  };

  const round6 = (n) => Math.round(n * 1e6) / 1e6;

  const removeNetwork = (row) => {
    API.loginRestriction.networks.remove(row.id)
      .then(() => { notify(T("hrset.removed", { name: row.label })); return reload(); })
      .catch((ex) => notify(T("hrset.saveFailed") + " — " + ex.message));
  };
  const removeArea = (row) => {
    API.loginRestriction.areas.remove(row.id)
      .then(() => { notify(T("hrset.removed", { name: row.label })); return reload(); })
      .catch((ex) => notify(T("hrset.saveFailed") + " — " + ex.message));
  };

  const toggleNetwork = (row) =>
    API.loginRestriction.networks.update(row.id, { active: !row.active }).then(reload).catch(() => {});
  const toggleArea = (row) =>
    API.loginRestriction.areas.update(row.id, { active: !row.active }).then(reload).catch(() => {});

  const branchOptions = [{ value: "", label: T("hrset.noBranch") }]
    .concat(branches.map((b) => ({ value: b.id, label: b.name })));

  // Every warning the server flags means the rule reads stricter than it acts.
  const WARN_TEXT = {
    no_networks: T("hrset.warnNoNetworks"),
    no_areas: T("hrset.warnNoAreas"),
    any_needs_both_lists: T("hrset.warnAnyNeedsBoth"),
    audit_only: T("hrset.warnAuditOnly")
  };

  const CODE_TEXT = {
    outside_allowed_ip: T("hrset.codeOutsideIp"),
    outside_allowed_area: T("hrset.codeOutsideArea"),
    location_required: T("hrset.codeLocationRequired"),
    location_inaccurate: T("hrset.codeLocationInaccurate")
  };

  return (
    <>
      <section className="card pad geo-card">
        <SectionHead title={T("hrset.signInRule")} />
        <p className="formhint">{T("hrset.signInRuleIntro")}</p>

        <div className="seg geo-modes" role="group" aria-label={T("hrset.signInRule")}>
          {MODES.map((m) => (
            <button key={m.id} type="button" className={"seg-btn" + (cfg.mode === m.id ? " on" : "")}
              aria-pressed={cfg.mode === m.id} disabled={busy} onClick={() => save({ mode: m.id })}>
              {m.label}
            </button>
          ))}
        </div>
        <p className="geo-note">{activeMode.hint}</p>

        {cfg.warnings.length ? (
          <ul className="geo-warns">
            {cfg.warnings.map((w) => (
              <li key={w} className="geo-note warn"><Icon name="alert" size={13} /> {WARN_TEXT[w] || w}</li>
            ))}
          </ul>
        ) : null}

        {cfg.mode !== "off" ? (
          <div className="switches solo">
            <GeoSwitch label={T("hrset.auditOnly")} hint={T("hrset.auditOnlyHint")}
              value={cfg.auditOnly} onChange={(v) => save({ auditOnly: v })} />
          </div>
        ) : null}
      </section>

      <section className="card geo-card">
        <div className="geo-pad">
          <SectionHead title={T("hrset.allowedNetworks")} />
          <p className="formhint">
            {T("hrset.allowedNetworksIntro")}
            {cfg.yourIp ? <> {T("hrset.yourIpIs")} <b className="mono" dir="ltr">{cfg.yourIp}</b></> : null}
          </p>
        </div>

        <DataTable rowKey={(r) => r.id} rows={cfg.networks} empty={T("hrset.noNetworks")} cols={[
          { k: "label", label: T("hrset.name"), render: (r) => r.label },
          { k: "cidr", label: T("hrset.address"), render: (r) => <span className="mono" dir="ltr">{r.cidr}</span> },
          { k: "branch", label: T("hrset.branch"), render: (r) => r.branchName || "—" },
          { k: "active", label: T("hrset.enabled"), w: "110px",
            render: (r) => (
              <button className="geo-toggle" onClick={() => toggleNetwork(r)}>
                <Badge tone={r.active ? "green" : "neutral"}>{r.active ? T("hrset.on") : T("hrset.off")}</Badge>
              </button>
            ) },
          { k: "rm", label: "", w: "48px",
            render: (r) => (
              <button type="button" className="inv-del" title={T("common.remove")} onClick={() => removeNetwork(r)}>
                <Icon name="trash" size={14} />
              </button>
            ) }
        ]} />

        <div className="geo-pad">
          <div className="geo-add">
            <div className="geo-add-head">
              <span className="geo-add-title">{T("hrset.addAddress")}</span>
            </div>
            <div className="formgrid three">
              <Field label={T("hrset.name")}>
                <Input value={netLabel} onChange={(e) => setNetLabel(e.target.value)} placeholder={T("hrset.networkNameEg")} />
              </Field>
              <Field label={T("hrset.address")} error={netErr || undefined}>
                <Input value={netCidr} onChange={(e) => setNetCidr(e.target.value)} placeholder="212.34.56.78" dir="ltr" />
              </Field>
              <Field label={T("hrset.branch")}>
                <Select options={branchOptions} value={netBranch} onChange={(e) => setNetBranch(e.target.value)} />
              </Field>
            </div>
            <div className="geo-acts">
              <Btn variant="primary" icon="plus" onClick={addNetwork}>{T("hrset.addAddress")}</Btn>
              {cfg.yourIp ? (
                <Btn icon="globe" onClick={() => { setNetCidr(cfg.yourIp); if (!netLabel) setNetLabel(T("hrset.thisPlace")); }}>
                  {T("hrset.useMyIp")}
                </Btn>
              ) : null}
            </div>
          </div>
        </div>
      </section>

      <section className="card geo-card">
        <div className="geo-pad">
          <SectionHead title={T("hrset.allowedAreas")} />
          <p className="formhint">{T("hrset.allowedAreasIntro")}</p>

          <AreaMap areas={cfg.areas} draft={areaLat == null ? null : { lat: areaLat, lng: areaLng, radius: areaRadius }}
            onPick={(lat, lng) => { setAreaLat(round6(lat)); setAreaLng(round6(lng)); setAreaErr(""); }} />
        </div>

        <DataTable rowKey={(r) => r.id} rows={cfg.areas} empty={T("hrset.noAreas")} cols={[
          { k: "label", label: T("hrset.name"), render: (r) => r.label },
          { k: "point", label: T("hrset.point"),
            render: (r) => <span className="mono dim" dir="ltr">{r.lat.toFixed(5)}, {r.lng.toFixed(5)}</span> },
          { k: "radius", label: T("hrset.radius"), render: (r) => T("hrset.metres", { n: r.radius }) },
          { k: "branch", label: T("hrset.branch"), render: (r) => r.branchName || "—" },
          { k: "active", label: T("hrset.enabled"), w: "110px",
            render: (r) => (
              <button className="geo-toggle" onClick={() => toggleArea(r)}>
                <Badge tone={r.active ? "green" : "neutral"}>{r.active ? T("hrset.on") : T("hrset.off")}</Badge>
              </button>
            ) },
          { k: "rm", label: "", w: "48px",
            render: (r) => (
              <button type="button" className="inv-del" title={T("common.remove")} onClick={() => removeArea(r)}>
                <Icon name="trash" size={14} />
              </button>
            ) }
        ]} />

        <div className="geo-pad">
          <div className="geo-add">
            <div className="geo-add-head">
              <span className="geo-add-title">{T("hrset.addArea")}</span>
              {/* The pending point belongs beside the fields it is part of. As a
                  caption underneath it read as a status line about the map. */}
              <span className={"geo-pin" + (areaLat == null ? " empty" : "")}>
                <Icon name="target" size={12} />
                {areaLat == null ? T("hrset.pickOnMap")
                  : <>{T("hrset.picked")} <b className="mono" dir="ltr">{areaLat.toFixed(5)}, {areaLng.toFixed(5)}</b></>}
              </span>
            </div>
            <div className="formgrid three">
              <Field label={T("hrset.name")} error={areaErr || undefined}>
                <Input value={areaLabel} onChange={(e) => setAreaLabel(e.target.value)} placeholder={T("hrset.areaNameEg")} />
              </Field>
              <Field label={T("hrset.radius")}>
                <Input type="number" min="50" step="50" value={areaRadius}
                  onChange={(e) => setAreaRadius(parseInt(e.target.value, 10) || 0)} />
              </Field>
              <Field label={T("hrset.branch")}>
                <Select options={branchOptions} value={areaBranch} onChange={(e) => setAreaBranch(e.target.value)} />
              </Field>
            </div>
            <p className="geo-note">{T("hrset.radiusAdvice")}</p>
            <div className="geo-acts">
              <Btn variant="primary" icon="plus" onClick={addArea}>{T("hrset.addArea")}</Btn>
              <Btn icon="target" disabled={locating} onClick={useMyLocation}>
                {locating ? T("hrset.locating") : T("hrset.useMyLocation")}
              </Btn>
            </div>
          </div>
        </div>
      </section>

      <section className="card geo-card">
        <div className="geo-pad">
          <SectionHead title={T("hrset.refusedSignIns")} />
          <p className="formhint">{T("hrset.refusedIntro")}</p>
        </div>
        <DataTable dense rowKey={(r) => r.id} rows={attempts} empty={T("hrset.noRefusals")} cols={[
          { k: "who", label: T("hrset.who"), render: (r) => r.who },
          { k: "code", label: T("hrset.reason"), render: (r) => CODE_TEXT[r.code] || r.code },
          { k: "ip", label: T("hrset.address"), render: (r) => <span className="mono dim" dir="ltr">{r.ip || "—"}</span> },
          { k: "where", label: T("hrset.point"),
            render: (r) => r.lat == null ? "—" : <span className="mono dim" dir="ltr">{r.lat.toFixed(4)}, {r.lng.toFixed(4)}</span> },
          { k: "blocked", label: T("hrset.outcome"),
            render: (r) => <Badge tone={r.blocked ? "red" : "amber"}>{r.blocked ? T("hrset.wasBlocked") : T("hrset.auditPass")}</Badge> },
          { k: "at", label: T("hrset.when"), render: (r) => API.fmt.prettyFull((r.at || "").slice(0, 10)) }
        ]} />
      </section>
    </>
  );
}

window.HrSettings = HrSettings;
