/* ==================================================================
   Admin → Bus Tracking
   ------------------------------------------------------------------
   Two tabs:
     • Live Map  — pick a bus, watch its position move over SSE on a
                   Leaflet + OpenStreetMap map (token minted per bus).
     • Manage    — create/disable buses, assign students to a bus
                   (the assignment is what lets a parent see that bus).
   Leaflet is loaded from CDN in index.html (global `L`); the marker is a
   divIcon so we never depend on Leaflet's bundled marker images.
   ================================================================== */

const { Icon: BtIcon } = window.KXUI;

// Default map centre until the first fix arrives (school timezone is IST).
const BT_DEFAULT_CENTER = [22.5726, 88.3639];

const busDivIcon = () => L.divIcon({
  className: "bus-div-icon",
  html: '<div style="font-size:22px;line-height:34px;width:34px;height:34px;text-align:center;'
      + 'background:#3b82f6;border:2px solid #fff;border-radius:50%;box-shadow:0 1px 6px rgba(0,0,0,.5)">🚌</div>',
  iconSize: [34, 34],
  iconAnchor: [17, 17],
});

function btTimeAgo(iso) {
  if (!iso) return "—";
  const s = Math.round((Date.now() - new Date(iso).getTime()) / 1000);
  if (s < 2) return "just now";
  if (s < 60) return s + "s ago";
  if (s < 3600) return Math.floor(s / 60) + "m ago";
  return Math.floor(s / 3600) + "h ago";
}

/* ---------------- Live Map tab ---------------- */
const BusLiveTab = () => {
  const [buses, setBuses] = React.useState([]);
  const [busId, setBusId] = React.useState("");
  const [fix, setFix] = React.useState(null);
  const [stale, setStale] = React.useState(false);
  const [err, setErr] = React.useState(null);

  const mapRef = React.useRef(null);
  const markerRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const esRef = React.useRef(null);
  const reconnectRef = React.useRef(null);

  // Load the bus list once.
  React.useEffect(() => {
    window.KXApi.get("/bus/visible")
      .then((d) => {
        setBuses(d.buses || []);
        const live = (d.buses || []).find((b) => b.live);
        setBusId(live ? live.id : (d.buses[0] ? d.buses[0].id : ""));
      })
      .catch((e) => setErr(e.message));
  }, []);

  // Create the Leaflet map once the container exists.
  React.useEffect(() => {
    if (!containerRef.current || mapRef.current) return;
    const map = L.map(containerRef.current).setView(BT_DEFAULT_CENTER, 13);
    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      attribution: "&copy; OpenStreetMap contributors", maxZoom: 19,
    }).addTo(map);
    mapRef.current = map;
    return () => { map.remove(); mapRef.current = null; markerRef.current = null; };
  }, []);

  // (Re)subscribe whenever the selected bus changes.
  React.useEffect(() => {
    if (!busId) return;
    let cancelled = false;

    function closeStream() {
      if (esRef.current) { esRef.current.close(); esRef.current = null; }
      if (reconnectRef.current) { clearTimeout(reconnectRef.current); reconnectRef.current = null; }
    }

    async function connect() {
      try {
        const snap = await window.KXApi.get(`/bus/${busId}/snapshot`);
        if (cancelled) return;
        if (snap.fix) { setFix(snap.fix); setStale(snap.fix.status === "ended"); }
        else { setFix(null); setStale(false); }

        const { token } = await window.KXApi.post("/bus/stream-token", { bus_id: busId });
        if (cancelled) return;
        const es = new EventSource(`/api/bus/stream?bus_id=${encodeURIComponent(busId)}&token=${encodeURIComponent(token)}`);
        es.onmessage = (ev) => {
          const f = JSON.parse(ev.data);
          if (f.status === "ended") { setStale(true); return; }
          setFix(f); setStale(false);
        };
        es.onerror = () => {
          // EventSource will try to reconnect, but our token may have expired —
          // close and re-mint a fresh one after a short delay.
          setStale(true);
          closeStream();
          if (!cancelled) reconnectRef.current = setTimeout(connect, 3000);
        };
        esRef.current = es;
      } catch (e) {
        if (!cancelled) setErr(e.message);
      }
    }

    setErr(null);
    connect();
    return () => { cancelled = true; closeStream(); };
  }, [busId]);

  // Move the marker on each fix.
  React.useEffect(() => {
    const map = mapRef.current;
    if (!map || !fix) return;
    const ll = [fix.lat, fix.lng];
    if (!markerRef.current) {
      markerRef.current = L.marker(ll, { icon: busDivIcon() }).addTo(map);
      map.setView(ll, 15);
    } else {
      markerRef.current.setLatLng(ll);
      map.panTo(ll, { animate: true, duration: 0.8 });
    }
  }, [fix]);

  // Staleness watchdog — flip to "offline" if no fix for >30s.
  React.useEffect(() => {
    const t = setInterval(() => {
      if (fix && fix.recorded_at && Date.now() - new Date(fix.recorded_at).getTime() > 30000) setStale(true);
    }, 4000);
    return () => clearInterval(t);
  }, [fix]);

  const selected = buses.find((b) => b.id === busId);

  return (
    <div>
      <div style={{ display: "flex", gap: 10, alignItems: "center", marginBottom: 12, flexWrap: "wrap" }}>
        <select className="input" style={{ width: 280 }} value={busId} onChange={(e) => setBusId(e.target.value)}>
          {buses.length === 0 && <option value="">No buses yet</option>}
          {buses.map((b) => (
            <option key={b.id} value={b.id}>{b.label}{b.plate_no ? ` (${b.plate_no})` : ""}</option>
          ))}
        </select>
        {selected && (
          <span className={`pill ${stale ? "" : "green"}`} style={{ fontSize: 11 }}>
            <span className="swatch"></span>
            {!selected.live ? "Not running" : stale ? "Offline" : "Live"}
            {fix && fix.recorded_at ? ` · ${btTimeAgo(fix.recorded_at)}` : ""}
          </span>
        )}
        {fix && fix.accuracy != null && (
          <span className="muted" style={{ fontSize: 11 }}>±{Math.round(fix.accuracy)} m</span>
        )}
      </div>

      {err && <div style={{ color: "var(--red)", fontSize: 12, marginBottom: 10 }}>{err}</div>}

      <div style={{ position: "relative" }}>
        <div ref={containerRef} style={{ height: "62vh", width: "100%", borderRadius: 20, overflow: "hidden", border: "1px solid var(--line)", boxShadow: "var(--shadow-card)" }} />
        {!fix && (
          <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", pointerEvents: "none" }}>
            <div style={{ background: "var(--bg-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "10px 16px", color: "var(--ink-2)", fontSize: 13 }}>
              Waiting for the bus to start its trip…
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

/* ---------------- Manage tab ---------------- */
const BusManageTab = () => {
  const [buses, setBuses] = React.useState([]);
  const [roster, setRoster] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState(null);
  const [form, setForm] = React.useState({ label: "", plate_no: "", driver_name: "", driver_phone: "", capacity: "" });
  const [assignBus, setAssignBus] = React.useState(null);     // bus row being edited
  const [assigned, setAssigned] = React.useState([]);
  const [pick, setPick] = React.useState([]);                 // selected student ids to add

  const reload = React.useCallback(async () => {
    setLoading(true); setErr(null);
    try {
      const [b, s] = await Promise.all([
        window.KXApi.get("/bus/admin/buses"),
        window.KXApi.get("/bus/admin/students"),
      ]);
      setBuses(b.buses || []);
      setRoster(s.students || []);
    } catch (e) { setErr(e.message); }
    finally { setLoading(false); }
  }, []);

  React.useEffect(() => { reload(); }, [reload]);

  async function createBus(e) {
    e.preventDefault();
    if (!form.label.trim()) return;
    try {
      await window.KXApi.post("/bus/admin/buses", {
        label: form.label.trim(),
        plate_no: form.plate_no.trim() || undefined,
        driver_name: form.driver_name.trim() || undefined,
        driver_phone: form.driver_phone.trim() || undefined,
        capacity: form.capacity ? Number(form.capacity) : undefined,
      });
      setForm({ label: "", plate_no: "", driver_name: "", driver_phone: "", capacity: "" });
      reload();
    } catch (e2) { setErr(e2.message === "label_taken" ? "A bus with that name already exists." : e2.message); }
  }

  async function toggleActive(bus) {
    try { await window.KXApi.patch(`/bus/admin/buses/${bus.id}`, { active: !bus.active }); reload(); }
    catch (e) { setErr(e.message); }
  }

  async function openAssign(bus) {
    setAssignBus(bus); setPick([]);
    try { const d = await window.KXApi.get(`/bus/admin/buses/${bus.id}/students`); setAssigned(d.students || []); }
    catch (e) { setErr(e.message); }
  }

  async function addAssignments() {
    if (!assignBus || pick.length === 0) return;
    try {
      await window.KXApi.post(`/bus/admin/buses/${assignBus.id}/assign`, { student_ids: pick });
      openAssign(assignBus);
    } catch (e) { setErr(e.message); }
  }

  async function removeAssignment(assignmentId) {
    try { await window.KXApi.del(`/bus/admin/assignments/${assignmentId}`); openAssign(assignBus); }
    catch (e) { setErr(e.message); }
  }

  const assignedIds = new Set(assigned.map((a) => a.student_id));
  const addable = roster.filter((r) => !assignedIds.has(r.id));

  return (
    <div style={{ display: "grid", gridTemplateColumns: assignBus ? "1fr 1fr" : "1fr", gap: 18 }}>
      {/* Left: buses list + create */}
      <div>
        <form onSubmit={createBus} style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 14, alignItems: "flex-end" }}>
          <label style={{ fontSize: 11, color: "var(--ink-2)" }}>Bus name
            <input className="input" style={{ display: "block", width: 180 }} value={form.label}
              onChange={(e) => setForm({ ...form, label: e.target.value })} placeholder="Bus 4 — North Loop" />
          </label>
          <label style={{ fontSize: 11, color: "var(--ink-2)" }}>Plate
            <input className="input" style={{ display: "block", width: 110 }} value={form.plate_no}
              onChange={(e) => setForm({ ...form, plate_no: e.target.value })} placeholder="WB 00 0000" />
          </label>
          <label style={{ fontSize: 11, color: "var(--ink-2)" }}>Driver
            <input className="input" style={{ display: "block", width: 130 }} value={form.driver_name}
              onChange={(e) => setForm({ ...form, driver_name: e.target.value })} />
          </label>
          {/* Shown to parents on the Transport card as a tap-to-call link. */}
          <label style={{ fontSize: 11, color: "var(--ink-2)" }}>Driver phone
            <input className="input" style={{ display: "block", width: 130 }} value={form.driver_phone}
              onChange={(e) => setForm({ ...form, driver_phone: e.target.value })} placeholder="10-digit" />
          </label>
          <label style={{ fontSize: 11, color: "var(--ink-2)" }}>Seats
            <input className="input" type="number" style={{ display: "block", width: 70 }} value={form.capacity}
              onChange={(e) => setForm({ ...form, capacity: e.target.value })} />
          </label>
          <button className="btn primary" type="submit"><BtIcon name="plus" size={11} /> Add bus</button>
        </form>

        {err && <div style={{ color: "var(--red)", fontSize: 12, marginBottom: 10 }}>{err}</div>}
        {loading ? <div className="muted">Loading…</div> : (
          <div className="cx-tablecard"><div className="cx-tablescroll">
          <table className="cx-table">
            <thead>
              <tr>
                <th style={{ padding: "6px 8px" }}>Bus</th>
                <th style={{ padding: "6px 8px" }}>Plate</th>
                <th style={{ padding: "6px 8px" }}>Driver</th>
                <th style={{ padding: "6px 8px" }}>Phone</th>
                <th style={{ padding: "6px 8px" }}></th>
              </tr>
            </thead>
            <tbody>
              {buses.map((b) => (
                <tr key={b.id} style={{ borderTop: "1px solid var(--line)", opacity: b.active ? 1 : 0.5 }}>
                  <td style={{ padding: "8px", color: "var(--ink-0)" }}>{b.label}</td>
                  <td style={{ padding: "8px", color: "var(--ink-2)" }}>{b.plate_no || "—"}</td>
                  <td style={{ padding: "8px", color: "var(--ink-2)" }}>{b.driver_name || "—"}</td>
                  <td style={{ padding: "8px", color: "var(--ink-2)" }}>{b.driver_phone || "—"}</td>
                  <td style={{ padding: "8px", textAlign: "right", whiteSpace: "nowrap" }}>
                    <button className="btn ghost sm" onClick={() => openAssign(b)}>Students</button>{" "}
                    <button className="btn ghost sm" onClick={() => toggleActive(b)}>{b.active ? "Disable" : "Enable"}</button>
                  </td>
                </tr>
              ))}
              {buses.length === 0 && <tr><td colSpan={5} style={{ padding: 16, color: "var(--ink-3)" }}>No buses yet — add one above.</td></tr>}
            </tbody>
          </table>
          </div></div>
        )}
      </div>

      {/* Right: assignment panel */}
      {assignBus && (
        <div style={{ borderLeft: "1px solid var(--line)", paddingLeft: 18 }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10 }}>
            <h3 style={{ margin: 0, color: "var(--ink-0)", fontSize: 14 }}>{assignBus.label} · students</h3>
            <button className="btn ghost sm" onClick={() => setAssignBus(null)}>✕</button>
          </div>

          <div style={{ marginBottom: 12 }}>
            <div style={{ color: "var(--ink-3)", fontSize: 11, marginBottom: 6 }}>Assigned ({assigned.length})</div>
            {assigned.length === 0 && <div className="muted" style={{ fontSize: 12 }}>None yet.</div>}
            {assigned.map((a) => (
              <div key={a.assignment_id} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 0", fontSize: 13 }}>
                <span style={{ color: "var(--ink-1)" }}>{a.name} <span className="muted">· {a.roll_no} · {a.class_label}</span></span>
                <button className="btn ghost sm" onClick={() => removeAssignment(a.assignment_id)}><BtIcon name="trash" size={12} /></button>
              </div>
            ))}
          </div>

          <div>
            <div style={{ color: "var(--ink-3)", fontSize: 11, marginBottom: 6 }}>Add students</div>
            <select multiple className="input" style={{ width: "100%", height: 200 }} value={pick}
              onChange={(e) => setPick(Array.from(e.target.selectedOptions).map((o) => o.value))}>
              {addable.map((r) => (
                <option key={r.id} value={r.id}>{r.name} · {r.roll_no} · {r.class_label}</option>
              ))}
            </select>
            <button className="btn primary sm" style={{ marginTop: 8 }} disabled={pick.length === 0} onClick={addAssignments}>
              Assign {pick.length || ""}
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

/* ---------------- Screen shell ---------------- */
const BusTrackingScreen = () => {
  const [tab, setTab] = React.useState("live");
  return (
    <div style={{ padding: 20 }}>
      <div style={{ display: "flex", gap: 6, marginBottom: 16 }}>
        {[["live", "Live Map"], ["manage", "Manage Buses"]].map(([id, label]) => (
          <button key={id} className={`btn ${tab === id ? "primary" : "ghost"} sm`} onClick={() => setTab(id)}>{label}</button>
        ))}
      </div>
      {tab === "live" ? <BusLiveTab /> : <BusManageTab />}
    </div>
  );
};

window.BusTrackingScreen = BusTrackingScreen;
