// Reseller-facing self-service device linking — the customer-side mirror
// of the admin's LinkDevicesModal/VendorLinkSection (see
// admin-assets-page.jsx's own header comment for the full backstory).
//
// Customer's explicit instruction (this session): "we can let the
// customer to do thier own linkin, so it takes the admin away from us."
// Opened from a "Link devices" button on each row of the reseller
// dashboard's "My assets" table (see reseller-portal-dashboard.jsx) —
// same entry point shape as the admin Assets page's per-unit button.
//
// Every company id/scoping happens server-side from the signed-in
// reseller's own session (routes/portal-devices.ts resolves
// ctx.effectiveCompanyId) — this file never sends a company id itself.
//
// Gating: `canManage` (passed down from a GET /api/portal/device-
// connections call — true for company_admin or an is_solo_staff account
// "viewing as" this company, false for a plain member) controls whether
// connect/sync/link/unlink actions are enabled here, same disabled-button
// + explanatory tooltip pattern as the admin side's `isReadOnly`. The
// backend enforces the real boundary (routes/portal-devices.ts's
// companyAdminGateError) — this is UX polish so a member doesn't fill in
// a whole connect form only to hit a 403 at the end.
//
// Reuses ModalShell/ActionButton/ModalError/SectionLabel/EmptyNote/
// MiniField/adminInputStyle exported by admin-assets-page.jsx (window
// globals) — must load after that file (see index.html).

function ResellerDeviceLinkingModal({ unit, canManage, onCancel, onChanged }) {
  const [status, setStatus] = useState(null); // null = loading

  const loadStatus = () => {
    fetch("/api/portal/device-connections", { credentials: "same-origin" })
      .then((r) => (r.ok ? r.json() : Promise.reject()))
      .then((data) => setStatus(data))
      .catch(() => setStatus({ victron: { connected: false }, ajax: { connected: false }, teltonika: { connected: false } }));
  };

  useEffect(loadStatus, []); // eslint-disable-line react-hooks/exhaustive-deps

  return (
    <ModalShell title={`Link devices — ${unit.serial_number}`} onCancel={onCancel} wide>
      <div style={{ fontFamily: "var(--font-body)", fontSize: 12.5, color: "rgba(0,0,0,0.55)", marginBottom: 20 }}>
        {unit.product_name}. Connect your own Victron VRM / Ajax Systems / Teltonika RMS account (once
        per vendor, not per unit), sync its device list, then link the device that matches this
        physical unit. {!canManage && "Only your company's admin can connect, sync, or link — you can still view status below."}
      </div>

      {status === null ? (
        <div style={{ fontFamily: "var(--font-body)", fontSize: 13, color: "rgba(0,0,0,0.45)", marginBottom: 20 }}>Loading…</div>
      ) : (
        <>
          <ResellerVendorPanel
            vendorLabel="Victron VRM" testIdPrefix="reseller-victron"
            unit={unit} canManage={canManage} onChanged={() => { loadStatus(); onChanged(); }}
            connected={status.victron.connected}
            connectFields={[{ key: "apiToken", label: "VRM Personal Access Token" }]}
            connectUrl="/api/portal/device-connections/victron"
            disconnectUrl="/api/portal/device-connections/victron"
            syncUrl="/api/portal/device-connections/victron/sync"
            listUrl="/api/portal/device-connections/victron/installations"
            listKey="installations"
            linkUrlFor={(item) => `/api/portal/device-connections/victron/installations/${item.id}/link`}
            getName={(item) => item.installation_name || item.gateway_identifier || `Installation ${item.vrm_installation_id}`}
            getSubtext={(item) => item.gateway_identifier || null}
            getSyncedAt={(item) => item.last_synced_at}
            connectedSummary={status.victron.connected ? `${status.victron.installationName || "Connected"} · ${status.victron.deviceCount} installation${status.victron.deviceCount === 1 ? "" : "s"} synced` : null}
          />

          <ResellerVendorPanel
            vendorLabel="Ajax Systems" testIdPrefix="reseller-ajax"
            unit={unit} canManage={canManage} onChanged={() => { loadStatus(); onChanged(); }}
            connected={status.ajax.connected}
            connectFields={[
              { key: "apiKey", label: "API Key" },
              { key: "companyIdAjax", label: "Ajax Company ID" },
              { key: "companyToken", label: "Company Token" },
            ]}
            connectUrl="/api/portal/device-connections/ajax"
            disconnectUrl="/api/portal/device-connections/ajax"
            syncUrl="/api/portal/device-connections/ajax/sync"
            listUrl="/api/portal/device-connections/ajax/hubs"
            listKey="hubs"
            linkUrlFor={(item) => `/api/portal/device-connections/ajax/hubs/${item.id}/link`}
            getName={(item) => item.name || `Hub ${item.ajax_hub_id}`}
            getSubtext={(item) => item.model || null}
            getSyncedAt={(item) => item.last_synced_at}
            connectedSummary={status.ajax.connected ? `${status.ajax.deviceCount} hub${status.ajax.deviceCount === 1 ? "" : "s"} synced` : null}
          />

          <ResellerVendorPanel
            vendorLabel="Teltonika RMS" testIdPrefix="reseller-teltonika"
            unit={unit} canManage={canManage} onChanged={() => { loadStatus(); onChanged(); }}
            connected={status.teltonika.connected}
            connectFields={[{ key: "accessToken", label: "RMS Personal Access Token" }]}
            connectUrl="/api/portal/device-connections/teltonika"
            disconnectUrl="/api/portal/device-connections/teltonika"
            syncUrl="/api/portal/device-connections/teltonika/sync"
            listUrl="/api/portal/device-connections/teltonika/devices"
            listKey="devices"
            linkUrlFor={(item) => `/api/portal/device-connections/teltonika/devices/${item.id}/link`}
            getName={(item) => item.name || item.serial || item.mac || `Device ${item.rms_device_id}`}
            getSubtext={(item) => [item.serial, item.mac].filter(Boolean).join(" · ") || null}
            getSyncedAt={(item) => item.last_synced_at}
            connectedSummary={status.teltonika.connected ? `${status.teltonika.rmsCompanyName || "Connected"} · ${status.teltonika.deviceCount} device${status.teltonika.deviceCount === 1 ? "" : "s"} synced` : null}
          />
        </>
      )}

      <div style={{ marginTop: 4 }}>
        <ActionButton onClick={onCancel}>Close</ActionButton>
      </div>
    </ModalShell>
  );
}

// One vendor's connect-or-manage panel. Shows a credentials form when
// not yet connected; once connected, shows the same sync-then-link list
// UI as the admin side (reusing that exact interaction, just against the
// self-service endpoints).
function ResellerVendorPanel({
  vendorLabel, testIdPrefix, unit, canManage, onChanged, connected, connectedSummary,
  connectFields, connectUrl, disconnectUrl, syncUrl, listUrl, listKey, linkUrlFor,
  getName, getSubtext, getSyncedAt,
}) {
  const [values, setValues] = useState(() => Object.fromEntries(connectFields.map((f) => [f.key, ""])));
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");

  const connect = async () => {
    const missing = connectFields.some((f) => !values[f.key]?.trim());
    if (missing) { setError("All fields are required."); return; }
    setBusy(true);
    setError("");
    try {
      const body = Object.fromEntries(connectFields.map((f) => [f.key, values[f.key].trim()]));
      const res = await fetch(connectUrl, {
        method: "POST", credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { setError(data.error || "Couldn't connect. Please check your credentials and try again."); setBusy(false); return; }
      setValues(Object.fromEntries(connectFields.map((f) => [f.key, ""])));
      await onChanged();
    } catch {
      setError("Couldn't reach the server. Check your connection and try again.");
    }
    setBusy(false);
  };

  const disconnect = async () => {
    if (!window.confirm(`Disconnect ${vendorLabel}? This removes every synced device for your company.`)) return;
    setBusy(true);
    try {
      await fetch(disconnectUrl, { method: "DELETE", credentials: "same-origin" });
      await onChanged();
    } catch {
      setError("Couldn't reach the server. Check your connection and try again.");
    }
    setBusy(false);
  };

  return (
    <div style={{ marginBottom: 24, border: "1px solid rgba(0,0,0,0.14)", padding: "16px 18px" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: connected ? 12 : 8, gap: 10 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, minWidth: 0 }}>
          <span style={{
            display: "inline-block", width: 8, height: 8, borderRadius: "50%", flexShrink: 0,
            background: connected ? "rgba(20,140,60,0.95)" : "rgba(0,0,0,0.3)",
          }} />
          <div style={{ minWidth: 0 }}>
            <SectionLabel>{vendorLabel}</SectionLabel>
            {connectedSummary && (
              <div style={{ fontFamily: "var(--font-body)", fontSize: 11.5, color: "rgba(0,0,0,0.5)", marginTop: -12 }}>
                {connectedSummary}
              </div>
            )}
          </div>
        </div>
        {connected && (
          <ActionButton disabled={busy} readOnly={!canManage} danger onClick={disconnect} testId={`${testIdPrefix}-disconnect`}>
            Disconnect
          </ActionButton>
        )}
      </div>

      {error && <div style={{ marginBottom: 8 }}><ModalError>{error}</ModalError></div>}

      {!connected ? (
        <div>
          {connectFields.map((f) => (
            <MiniField key={f.key} label={f.label}>
              <input
                type="text" value={values[f.key]}
                onChange={(e) => setValues((v) => ({ ...v, [f.key]: e.target.value }))}
                style={adminInputStyle} data-testid={`${testIdPrefix}-${f.key}-input`}
              />
            </MiniField>
          ))}
          <ActionButton disabled={busy} readOnly={!canManage} onClick={connect} testId={`${testIdPrefix}-connect`}>
            {busy ? "Connecting…" : "Connect"}
          </ActionButton>
        </div>
      ) : (
        <ResellerVendorSyncList
          vendorLabel={vendorLabel} testIdPrefix={testIdPrefix}
          unit={unit} canManage={canManage} onChanged={onChanged}
          syncUrl={syncUrl} listUrl={listUrl} listKey={listKey} linkUrlFor={linkUrlFor}
          getName={getName} getSubtext={getSubtext} getSyncedAt={getSyncedAt}
        />
      )}
    </div>
  );
}

// Sync-then-link list, once a vendor is connected — same shape as the
// admin side's VendorLinkSection, against the self-service endpoints.
function ResellerVendorSyncList({
  vendorLabel, testIdPrefix, unit, canManage, onChanged,
  syncUrl, listUrl, listKey, linkUrlFor, getName, getSubtext, getSyncedAt,
}) {
  const [items, setItems] = useState(null);
  const [error, setError] = useState("");
  const [syncing, setSyncing] = useState(false);
  const [linkingId, setLinkingId] = useState(null);

  const load = () => {
    fetch(listUrl, { credentials: "same-origin" })
      .then((r) => (r.ok ? r.json() : Promise.reject()))
      .then((data) => setItems(data[listKey] || []))
      .catch(() => setItems([]));
  };

  useEffect(load, [listUrl]); // eslint-disable-line react-hooks/exhaustive-deps

  const sync = async () => {
    setSyncing(true);
    setError("");
    try {
      const res = await fetch(syncUrl, { method: "POST", credentials: "same-origin" });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { setError(data.error || "Couldn't sync. Please try again."); setSyncing(false); return; }
      load();
    } catch {
      setError("Couldn't reach the server. Check your connection and try again.");
    }
    setSyncing(false);
  };

  const link = async (item, assetUnitId) => {
    setLinkingId(item.id);
    setError("");
    try {
      const res = await fetch(linkUrlFor(item), {
        method: "POST", credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ assetUnitId }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { setError(data.error || "Couldn't link. Please try again."); setLinkingId(null); return; }
      load();
      await onChanged();
    } catch {
      setError("Couldn't reach the server. Check your connection and try again.");
    }
    setLinkingId(null);
  };

  return (
    <div>
      <div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 8 }}>
        <ActionButton disabled={syncing} readOnly={!canManage} onClick={sync} testId={`${testIdPrefix}-sync-devices`}>
          {syncing ? "Syncing…" : "Sync now"}
        </ActionButton>
      </div>
      {error && <div style={{ marginBottom: 8 }}><ModalError>{error}</ModalError></div>}

      {items === null ? (
        <EmptyNote>Loading…</EmptyNote>
      ) : items.length === 0 ? (
        <EmptyNote>Nothing synced yet — click Sync now above.</EmptyNote>
      ) : (
        <div style={{ border: "1px solid rgba(0,0,0,0.14)" }}>
          {items.map((item, idx) => {
            const linkedToThis = item.asset_unit_id === unit.id;
            const linkedElsewhere = item.asset_unit_id && !linkedToThis;
            return (
              <div key={item.id} style={{
                padding: "10px 14px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10,
                borderBottom: idx === items.length - 1 ? "none" : "1px solid rgba(0,0,0,0.08)",
                background: linkedToThis ? "rgba(20,140,60,0.05)" : "transparent",
              }}>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontFamily: "var(--font-body)", fontSize: 13, fontWeight: 600, color: "rgba(0,0,0,0.85)" }}>
                    {getName(item)}
                  </div>
                  <div style={{ fontFamily: "var(--font-body)", fontSize: 11, color: "rgba(0,0,0,0.5)" }}>
                    {getSubtext(item) && <>{getSubtext(item)} · </>}
                    {linkedToThis
                      ? "Linked to this unit"
                      : linkedElsewhere
                        ? <>Linked to <strong>{item.linked_serial_number}</strong></>
                        : "Not linked"}
                    {getSyncedAt(item) && <> · synced {getSyncedAt(item)}</>}
                  </div>
                </div>
                {linkedToThis ? (
                  <ActionButton disabled={linkingId === item.id} readOnly={!canManage} danger onClick={() => link(item, null)} testId={`${testIdPrefix}-unlink-${item.id}`}>
                    Unlink
                  </ActionButton>
                ) : (
                  <ActionButton disabled={linkingId === item.id || linkedElsewhere} readOnly={!canManage} onClick={() => link(item, unit.id)} testId={`${testIdPrefix}-link-${item.id}`}>
                    {linkingId === item.id ? "Linking…" : "Link"}
                  </ActionButton>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

Object.assign(window, { ResellerDeviceLinkingModal });
