import { useEffect } from 'react';
import { ClientEvent, MatrixClient, SetPresence, SyncState } from 'matrix-js-sdk';
import { useAppActive } from './useAppActive';

// Drives the `set_presence` query parameter on long-poll /sync so that
// Synapse stops marking the user as "active right now" whenever a
// background tab or backgrounded Android process keeps the sync loop
// running. Without this, every long-poll /sync — and there is one
// in-flight at all times — implicitly bumps `last_active_ago` to ~0 on
// the homeserver. Peer clients then read that via m.presence and render
// "был в сети только что" for a user whose phone has been in their
// pocket for hours.
//
// The matrix-js-sdk default is `presence === undefined`, which omits
// `set_presence` from the sync URL entirely — Synapse treats the
// missing param as `online`. By calling `setSyncPresence(Online)`
// whenever sync is ready and toggling to `Unavailable` whenever the app
// goes to background, we make the server-side `last_active_ago`
// actually reflect foreground activity instead of the JS sync loop's
// heartbeat.
//
// We deliberately do NOT call `setSyncPresence(Offline)` on cleanup:
// `Offline` means "I'm logged out" in Matrix-presence semantics, and
// the hook unmount on logout already triggers the explicit PUT
// /presence handled by `logoutClient`. A spurious Offline emit on
// transient hook remount would briefly take the user offline for peers.
//
// Foreground/background signal comes from `useAppActive`, which is the
// shared lifecycle hook that consolidates Capacitor App.pause / resume
// (Android + iOS) and App.appStateChange (web).
export const useSyncPresenceGate = (mx: MatrixClient | undefined): void => {
  const active = useAppActive();

  useEffect(() => {
    if (!mx) return undefined;

    const desired = active ? SetPresence.Online : SetPresence.Unavailable;
    mx.setSyncPresence(desired);

    // The very first /sync that starts the long-poll loop will almost
    // always go out with our `desired` value already missed — by the
    // time this effect runs, `startClient` may not yet have constructed
    // `syncApi` (`mx.setSyncPresence` no-ops when `this.syncApi` is
    // undefined), and `Prepared/Syncing` aren't emitted until the
    // initial /sync response lands. We accept that: the cold-start
    // user IS actively launching the app, so one bumped
    // `last_active_ts` is honest. What this hook prevents is the
    // *background long-poll loop* lying about activity for hours after.
    //
    // The Sync latch below replays `desired` once the SDK is past
    // PREPARED, which guarantees that the FIRST long-poll /sync (the
    // one that runs after the initial response) carries the right
    // `set_presence`. For a cold-start launched into a hidden tab,
    // this means a single Online ping then immediate Unavailable on
    // the very next sync — acceptable noise vs the previous "always
    // online for as long as the tab is open" behaviour.
    let primed = false;
    const onSync = (state: SyncState) => {
      if (primed) return;
      if (state === SyncState.Prepared || state === SyncState.Syncing) {
        primed = true;
        mx.setSyncPresence(desired);
      }
    };
    mx.on(ClientEvent.Sync, onSync);

    return () => {
      mx.removeListener(ClientEvent.Sync, onSync);
    };
  }, [mx, active]);
};
