import { isAxiosError } from "axios";

import {
  REFRESH_TOKEN_COOKIE,
  clearSessionCookies,
  getCookie,
} from "./cookies";
import { refreshSessionTokens } from "./http";

/**
 * Proactively refreshes the token pair on a fixed cadence so the server-side
 * session (a sliding 1-hour window, extended only by /auth/refresh-token) is
 * kept alive while the app is open. The 401 interceptor in http.ts remains
 * the reactive fallback; both share the same single-flight refresh call.
 *
 * The interval MUST stay well under the server session window (60 min).
 */
const DEFAULT_INTERVAL_MINUTES = 10;

const getIntervalMs = () => {
  const minutes = Number(import.meta.env.VITE_TOKEN_REFRESH_INTERVAL_MINUTES);
  return (
    (Number.isFinite(minutes) && minutes > 0
      ? minutes
      : DEFAULT_INTERVAL_MINUTES) * 60_000
  );
};

let timerId: number | null = null;
let lastRefreshAt = 0;

const refreshNow = async () => {
  if (!getCookie(REFRESH_TOKEN_COOKIE)) {
    // Logged out elsewhere; nothing to keep alive.
    stopTokenRefreshScheduler();
    return;
  }

  try {
    await refreshSessionTokens();
    lastRefreshAt = Date.now();
  } catch (error) {
    // Network hiccups are retried on the next tick; only an explicit server
    // rejection means the session is dead.
    if (isAxiosError(error) && error.response) {
      stopTokenRefreshScheduler();
      clearSessionCookies();
      window.location.replace("/auth");
    }
  }
};

/**
 * Browsers freeze timers in background tabs — a tab waking up past its
 * refresh deadline refreshes immediately instead of waiting a full interval.
 */
const onVisibilityChange = () => {
  if (document.visibilityState !== "visible") return;
  if (Date.now() - lastRefreshAt >= getIntervalMs()) {
    void refreshNow();
  }
};

export const startTokenRefreshScheduler = () => {
  stopTokenRefreshScheduler();

  // Token age is unknown here (fresh login vs. hours-old page reload), so
  // refresh right away to extend the session window from "now".
  lastRefreshAt = 0;
  void refreshNow();

  timerId = window.setInterval(() => void refreshNow(), getIntervalMs());
  document.addEventListener("visibilitychange", onVisibilityChange);
};

export const stopTokenRefreshScheduler = () => {
  if (timerId !== null) {
    window.clearInterval(timerId);
    timerId = null;
  }
  document.removeEventListener("visibilitychange", onVisibilityChange);
};
