/* ============================================================
   Telemetron Integration
   - Base URL: https://api.telemetron.net
   - Auth: POST /auth/ (grant types: password | auth_code | refresh_token)
   - Token cached in sessionStorage
   - Credentials persisted in yappari_settings (key: 'telemetron')
   - Auto-sync every 5 minutes while admin tab is open
   ============================================================ */

const { useState: useStateTele, useEffect: useEffectTele } = React;

const TELEMETRON_BASE = "https://api.telemetron.net";
const TELE_TOKEN_KEY  = "yappari_telemetron_token";

// ── Settings shape ──────────────────────────────────────────
//   { client_id, client_secret, username, password,
//     machine_map: { telemetronVMId: localMachineId, ... } }

const telemetron = {
  // — token —
  getCachedToken() {
    try {
      const raw = sessionStorage.getItem(TELE_TOKEN_KEY);
      if (!raw) return null;
      const t = JSON.parse(raw);
      if (t.expires_at && Date.now() > t.expires_at - 30_000) return null; // expire 30s early
      return t;
    } catch { return null; }
  },
  cacheToken(t) {
    const enriched = {
      ...t,
      expires_at: Date.now() + (Number(t.expires_in) || 3600) * 1000,
    };
    sessionStorage.setItem(TELE_TOKEN_KEY, JSON.stringify(enriched));
    return enriched;
  },
  clearToken() { sessionStorage.removeItem(TELE_TOKEN_KEY); },

  // — credentials —
  async loadCreds()      { return (await db.getSetting("telemetron")) || {}; },
  async saveCreds(creds) { return db.setSetting("telemetron", creds); },

  // — auth —
  async authenticate(creds) {
    if (!creds.client_id || !creds.client_secret || !creds.username || !creds.password) {
      throw new Error("Lengkapi semua field: client_id, client_secret, username, password.");
    }
    const body = new URLSearchParams({
      grant_type:    "password",
      client_id:     creds.client_id,
      client_secret: creds.client_secret,
      username:      creds.username,
      password:      creds.password,
    });
    const res = await fetch(`${TELEMETRON_BASE}/auth/`, {
      method:  "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" },
      body,
    });
    const data = await res.json().catch(() => ({}));
    if (!res.ok) throw new Error(data.message || data.error_description || `Auth gagal (${res.status})`);
    if (!data.access_token) throw new Error("Auth respons tidak mengandung access_token.");
    return telemetron.cacheToken(data);
  },

  async ensureToken() {
    const cached = telemetron.getCachedToken();
    if (cached) return cached;
    const creds = await telemetron.loadCreds();
    return telemetron.authenticate(creds);
  },

  async apiGet(path) {
    const t = await telemetron.ensureToken();
    const res = await fetch(`${TELEMETRON_BASE}${path}`, {
      headers: { "Authorization": `Bearer ${t.access_token}`, "Accept": "application/json" },
    });
    if (res.status === 401) {
      telemetron.clearToken();
      const fresh = await telemetron.ensureToken();
      const res2  = await fetch(`${TELEMETRON_BASE}${path}`, {
        headers: { "Authorization": `Bearer ${fresh.access_token}`, "Accept": "application/json" },
      });
      if (!res2.ok) throw new Error(`API ${path} → ${res2.status}`);
      return res2.json();
    }
    if (!res.ok) {
      const text = await res.text().catch(() => "");
      throw new Error(`API ${path} → ${res.status}: ${text.slice(0, 200)}`);
    }
    return res.json();
  },

  // — fetchers —
  async listMachines()                  { return telemetron.apiGet("/v2/vending_machines"); },
  async machineDetail(id)               { return telemetron.apiGet(`/v2/vending_machines/${id}`); },
  async salesSummary(query = "")        { return telemetron.apiGet(`/v2/stats/vends/summary${query}`); },
  async machineEvents(id, q = "")       { return telemetron.apiGet(`/v2/vending_machines/${id}/events${q}`); },

  // — sync —
  async syncAll() {
    const creds  = await telemetron.loadCreds();
    const map    = creds.machine_map || {};
    const list   = await telemetron.listMachines();
    const remote = list?.data || list?.vending_machines || list || [];

    const today  = new Date(); today.setHours(0,0,0,0);
    const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1);
    const range  = `?date_from=${today.toISOString().slice(0,10)}&date_to=${tomorrow.toISOString().slice(0,10)}`;
    let summary;
    try { summary = await telemetron.salesSummary(range); } catch { summary = null; }
    // Telemetron `/stats/vends/summary` returns AGGREGATE (no per-VM split) — e.g.
    //   [{ "number": <total vends>, "value": <total revenue> }]
    // so we can only attribute it back to a single VM if the user has exactly one
    // mapped machine. Otherwise sales_today/transactions_today are left null.
    const summaryRows = summary?.data || summary?.summary || summary || [];
    const summaryArr  = Array.isArray(summaryRows) ? summaryRows : [];
    const perMachineSales = {};
    let aggregateTotal = null;
    summaryArr.forEach(row => {
      const teleId = row.vending_machine_id ?? row.vm_id ?? row.id;
      const sales  = Number(row.amount ?? row.total ?? row.revenue ?? row.value ?? 0);
      const txns   = Number(row.count  ?? row.vends ?? row.transactions ?? row.number ?? 0);
      if (teleId != null) perMachineSales[teleId] = { sales, transactions: txns };
      else aggregateTotal = { sales, transactions: txns };
    });
    const mappedCount = Object.keys(map).length;

    const rows = [];
    for (const vm of remote) {
      const teleId   = vm.id ?? vm.vm_id;
      const localId  = map[teleId];
      if (!localId) continue;                    // skip unmapped
      // Per-VM stats if available; else attribute single-machine aggregate.
      let stats = perMachineSales[teleId] || null;
      if (!stats && aggregateTotal && mappedCount === 1) stats = aggregateTotal;
      // last_event_dates is the canonical Telemetron v2 shape; older fields kept as fallback.
      const lastSeen = vm.last_event_dates?.last_report
                    || vm.last_event_dates?.last_sale
                    || vm.last_event_dates?.last_acquired_report
                    || vm.last_event_at || vm.last_seen || vm.updated_at || null;
      // Vending machines may go quiet between sales; widen the online window to 60 min.
      const online = lastSeen ? (Date.now() - new Date(lastSeen).getTime() < 60 * 60_000) : (vm.online ?? null);
      rows.push({
        machine_id:         localId,
        sales_today:        stats?.sales ?? null,
        transactions_today: stats?.transactions ?? null,
        temperature:        vm.temperature ?? vm.cabinet_temperature ?? null,
        is_online:          online,
        stock_level:        vm.stock_level ?? null,
        recorded_at:        new Date().toISOString(),
      });
    }
    if (rows.length) await db.insertTelemetry(rows);
    return {
      remoteCount: remote.length,
      syncedCount: rows.length,
      unmappedCount: remote.length - rows.length,
      raw: { machines: remote, summary: summaryArr },
    };
  },
};

/* ============================================================
   ADMIN: TELEMETRON SETTINGS VIEW
   ============================================================ */
function TelemetronSettingsView() {
  const { toast, allMachines } = useApp();
  const [creds, setCreds]       = useStateTele({
    client_id: "a1dd429f-712d-4d6c-be13-be57cfe38c4f",
    client_secret: "", username: "", password: "",
    machine_map: {},
  });
  const [remote, setRemote]     = useStateTele([]); // [{id, name, ...}]
  const [loading, setLoading]   = useStateTele(true);
  const [busy, setBusy]         = useStateTele(false);
  const [lastSync, setLastSync] = useStateTele(null);
  const [autoSync, setAutoSync] = useStateTele(false);

  useEffectTele(() => {
    let cancelled = false;
    (async () => {
      try {
        const c = await telemetron.loadCreds();
        if (!cancelled && c && Object.keys(c).length) {
          setCreds(prev => ({ ...prev, ...c, machine_map: c.machine_map || {} }));
        }
      } catch (e) { console.warn("loadCreds:", e); }
      finally    { if (!cancelled) setLoading(false); }
    })();
    return () => { cancelled = true; };
  }, []);

  // Auto-sync interval (only while view mounted + toggle on)
  useEffectTele(() => {
    if (!autoSync) return;
    const id = setInterval(() => { runSync(true); }, 5 * 60_000);
    return () => clearInterval(id);
  }, [autoSync]);

  async function saveCreds() {
    setBusy(true);
    try {
      await telemetron.saveCreds(creds);
      toast("Credentials Telemetron tersimpan.", "success");
    } catch (e) {
      toast("Gagal simpan: " + e.message, "danger");
    } finally { setBusy(false); }
  }

  async function testAuth() {
    setBusy(true);
    try {
      telemetron.clearToken();
      await telemetron.saveCreds(creds);
      const tok = await telemetron.authenticate(creds);
      toast(`Login berhasil. Token expires in ${tok.expires_in || "?"}s.`, "success");
    } catch (e) {
      toast("Login gagal: " + e.message, "danger");
    } finally { setBusy(false); }
  }

  async function fetchRemote() {
    setBusy(true);
    try {
      await telemetron.saveCreds(creds);
      const list = await telemetron.listMachines();
      const arr  = list?.data || list?.vending_machines || list || [];
      setRemote(Array.isArray(arr) ? arr : []);
      toast(`Ditemukan ${arr.length} mesin di Telemetron.`, "success");
    } catch (e) {
      toast("Gagal ambil daftar mesin: " + e.message, "danger");
    } finally { setBusy(false); }
  }

  async function runSync(silent = false) {
    setBusy(true);
    try {
      const r = await telemetron.syncAll();
      setLastSync(new Date());
      // Log raw responses so we can verify field-name mapping when sales data looks empty.
      console.log("[telemetron] raw machines:", r.raw?.machines);
      console.log("[telemetron] raw summary:",  r.raw?.summary);
      if (!silent) toast(`Sync OK · ${r.syncedCount}/${r.remoteCount} mesin (${r.unmappedCount} belum di-map).`, "success");
    } catch (e) {
      if (!silent) toast("Sync gagal: " + e.message, "danger");
      console.warn("syncAll error:", e);
    } finally { setBusy(false); }
  }

  function setMap(teleId, localId) {
    const next = { ...(creds.machine_map || {}) };
    if (localId) next[teleId] = localId; else delete next[teleId];
    setCreds({ ...creds, machine_map: next });
  }

  if (loading) return (
    <div className="px-5 md:px-8 py-7 md:py-9 max-w-4xl mx-auto">
      <div className="text-stone-500">Memuat pengaturan…</div>
    </div>
  );

  return (
    <div className="px-5 md:px-8 py-7 md:py-9 max-w-4xl mx-auto">
      <PageHeader eyebrow="Admin" jp="設定" title="Pengaturan Telemetron"
        sub="OAuth credentials & mapping mesin Telemetron ke mesin lokal."
      />

      <Card className="p-6 mb-5">
        <div className="text-[11px] font-bold uppercase tracking-widest text-stone-500 mb-3">OAuth Credentials</div>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          <Field label="Client ID *"     value={creds.client_id}     onChange={v => setCreds({...creds, client_id: v})}/>
          <Field label="Client Secret *" type="password" value={creds.client_secret} onChange={v => setCreds({...creds, client_secret: v})}/>
          <Field label="Username *"      value={creds.username}      onChange={v => setCreds({...creds, username: v})}/>
          <Field label="Password *"      type="password" value={creds.password}      onChange={v => setCreds({...creds, password: v})}/>
        </div>
        <div className="mt-3 text-xs text-stone-500 bg-stone-50 border border-stone-100 rounded-lg p-3">
          Grant type yang didukung Telemetron: <code className="font-mono">password</code>, <code className="font-mono">auth_code</code>, <code className="font-mono">refresh_token</code>.
          Karena tidak ada <code className="font-mono">client_credentials</code>, akun service / username+password tetap dibutuhkan.
          Token disimpan di <code className="font-mono">sessionStorage</code> (hilang saat tab ditutup).
        </div>
        <div className="mt-4 flex flex-wrap gap-2">
          <Button onClick={saveCreds} variant="primary" disabled={busy}><Icon name="check" size={14}/> Simpan</Button>
          <Button onClick={testAuth}  variant="dark"    disabled={busy}><Icon name="key"   size={14}/> Test Login</Button>
          <Button onClick={fetchRemote} variant="subtle" disabled={busy}><Icon name="refresh" size={14}/> Ambil Daftar Mesin</Button>
        </div>
      </Card>

      <Card className="p-6 mb-5">
        <div className="flex items-center justify-between mb-3">
          <div>
            <div className="text-[11px] font-bold uppercase tracking-widest text-stone-500">Sinkronisasi Telemetry</div>
            <div className="text-xs text-stone-500 mt-0.5">
              {lastSync ? `Sync terakhir: ${lastSync.toLocaleTimeString("id-ID")}` : "Belum pernah sync."}
            </div>
          </div>
          <label className="flex items-center gap-2 text-sm text-stone-600 cursor-pointer">
            <input type="checkbox" checked={autoSync} onChange={e => setAutoSync(e.target.checked)}/>
            Auto-sync tiap 5 menit
          </label>
        </div>
        <Button onClick={() => runSync(false)} variant="primary" disabled={busy}>
          <Icon name="refresh" size={14}/> Sync Sekarang
        </Button>
        <div className="mt-3 text-[11px] text-stone-400">
          Catatan: auto-sync hanya berjalan selama tab dashboard ini terbuka. Untuk sync background 24/7,
          deploy worker Node.js terpisah (rekomendasi: bridge service à la <code>accurate-bridge</code>).
        </div>
      </Card>

      <Card className="overflow-hidden">
        <div className="px-6 py-4 border-b border-stone-100 flex items-center justify-between">
          <div>
            <div className="text-[11px] font-bold uppercase tracking-widest text-stone-500">Machine Mapping</div>
            <div className="font-display text-lg uppercase text-[#1A1A1A] mt-0.5">Telemetron → Lokal</div>
          </div>
          <Badge tone="neutral">{remote.length} mesin Telemetron</Badge>
        </div>
        {remote.length === 0 ? (
          <div className="px-6 py-8 text-center text-stone-400 text-sm">
            Klik <strong>Ambil Daftar Mesin</strong> untuk memuat daftar dari Telemetron.
          </div>
        ) : (
          <table className="w-full text-sm">
            <thead>
              <tr className="bg-stone-50 border-b border-stone-200 text-[11px] font-bold uppercase tracking-widest text-stone-500">
                <th className="px-5 py-3 text-left">Telemetron ID</th>
                <th className="px-5 py-3 text-left">Nama</th>
                <th className="px-5 py-3 text-left">Mesin Lokal</th>
              </tr>
            </thead>
            <tbody>
              {remote.map(vm => {
                const teleId  = vm.id ?? vm.vm_id;
                const mapped  = creds.machine_map?.[teleId] || "";
                return (
                  <tr key={teleId} className="border-b border-stone-100 last:border-0">
                    <td className="px-5 py-3 font-mono text-xs text-stone-700">{String(teleId)}</td>
                    <td className="px-5 py-3 text-[#1A1A1A]">{vm.name || vm.label || vm.title || "—"}</td>
                    <td className="px-5 py-3">
                      <select value={mapped} onChange={e => setMap(teleId, e.target.value)}
                        className="px-3 py-1.5 rounded-lg border border-stone-300 bg-white text-sm focus:border-[#FF2D55] outline-none">
                        <option value="">— Tidak di-map —</option>
                        {allMachines.map(m => <option key={m.id} value={m.id}>{m.id} ({m.model})</option>)}
                      </select>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
        {remote.length > 0 && (
          <div className="px-6 py-3 bg-stone-50 border-t border-stone-100 text-right">
            <Button onClick={saveCreds} variant="primary" disabled={busy}>Simpan Mapping</Button>
          </div>
        )}
      </Card>
    </div>
  );
}

Object.assign(window, { telemetron, TelemetronSettingsView });
