import { throttle } from "lodash";
import { getLoginParams } from "./auth/loginParams.js";
import { getAuthorizationHeader } from "./auth/helper.js";
import { introspectToken } from "../api/auth/index.js";
import { getLogger } from "./logger.js";
import { logRecords } from "./log-records.js";
import { endSession } from "./auth/index.js";
import { getAppConfigValue } from "./app-config/config.js";
import {
  SESSION_TIMEOUT_INTERVAL,
  SESSION_RENEWAL_INTERVAL
} from "./constants.js";
let lastUserActivityTimeStamp = Date.now();
const warnListeners = [];
const expiryListeners = [];
const resetListeners = [];
const userInteractionEvents = ["click", "scroll", "keypress", "touchstart"];
let sessionExpiryWarningNotified = false;
let timerHandle;
let sessionExpiryWarningNotifiedAt;
const resetUserIdleTime = (resetWarningModal = false) => {
  sessionExpiryWarningNotified = false;
  sessionExpiryWarningNotifiedAt = null;
  lastUserActivityTimeStamp = Date.now();
  Promise.resolve().then(() => {
    resetListeners.forEach((listener) => listener(resetWarningModal));
  }).catch(() => {
  });
};
const renewSession = async () => {
  const loginParams = getLoginParams();
  try {
    resetUserIdleTime(true);
    if (loginParams?.clientId) {
      const authToken = getAuthorizationHeader();
      if (!authToken)
        throw new Error("Authorization token not found to extend session");
      const accessToken = authToken.split(" ")[1];
      await introspectToken({ clientId: loginParams.clientId, accessToken });
    }
  } catch (e) {
    getLogger()?.error({
      ...logRecords.SESSION_RENEWAL_FAILED,
      exception: e
    });
    await endSession({ ...loginParams, skipRevoke: true });
  }
};
const renewSessionAtInterval = throttle(
  renewSession,
  SESSION_RENEWAL_INTERVAL,
  {
    leading: true
  }
);
const isPastWarningTime = (warnInterval = getAppConfigValue("sessionTimeoutWarnInterval") || SESSION_TIMEOUT_INTERVAL.WARN) => Date.now() - lastUserActivityTimeStamp > warnInterval;
const isPastSessionTimeoutTime = (sessionTimeout = getAppConfigValue("sessionTimeoutInterval") || SESSION_TIMEOUT_INTERVAL.EXPIRY) => Date.now() - lastUserActivityTimeStamp > sessionTimeout;
const notifySessionExpiryWarning = () => {
  sessionExpiryWarningNotified = true;
  sessionExpiryWarningNotifiedAt = Date.now();
  Promise.resolve().then(() => {
    warnListeners.forEach(
      (listener) => listener(sessionExpiryWarningNotifiedAt)
    );
    userInteractionEvents.forEach((eventType) => {
      document.removeEventListener(eventType, renewSessionAtInterval);
    });
  }).catch(() => {
  });
};
const notifySessionExpiry = () => Promise.resolve().then(() => {
  expiryListeners.forEach((listener) => listener());
});
const stopSessionMonitoring = () => {
  userInteractionEvents.forEach((eventType) => {
    document.removeEventListener(eventType, renewSessionAtInterval);
  });
  clearInterval(timerHandle);
  warnListeners.length = 0;
  expiryListeners.length = 0;
};
const monitorSessionEvents = () => {
  if (isPastSessionTimeoutTime()) {
    notifySessionExpiry().then(() => {
      stopSessionMonitoring();
    }).catch(() => {
    });
  } else if (!sessionExpiryWarningNotified && isPastWarningTime())
    notifySessionExpiryWarning();
};
const removeListeners = (arr, cb) => {
  const idx = arr.findIndex((r) => r === cb);
  if (idx > -1) {
    arr.splice(idx, 1);
  }
};
const addEventListeners = () => {
  userInteractionEvents.forEach((eventType) => {
    document.addEventListener(eventType, renewSessionAtInterval);
  });
};
const initSessionMonitoring = () => {
  addEventListeners();
  timerHandle = setInterval(monitorSessionEvents, 1e3);
};
const subscribeToSessionExpiryWarning = (onSessionExpiryWarn) => {
  warnListeners.push(onSessionExpiryWarn);
  return removeListeners.bind(null, warnListeners, onSessionExpiryWarn);
};
const subscribeToSessionExpiry = (onSessionExpiry) => {
  expiryListeners.push(onSessionExpiry);
  return removeListeners.bind(null, expiryListeners, onSessionExpiry);
};
const subscribeToResetSession = (onResetListener) => {
  resetListeners.push(onResetListener);
  return removeListeners.bind(null, resetListeners, onResetListener);
};
const trackActivity = (element, cb) => {
  if (!element) return () => {
  };
  const thorttledCb = throttle(cb, SESSION_RENEWAL_INTERVAL, { leading: true });
  userInteractionEvents.forEach(
    (r) => element.addEventListener(r, thorttledCb)
  );
  return () => {
    userInteractionEvents.forEach((r) => element.removeEventListener(r, cb));
    userInteractionEvents.forEach(
      (r) => element.removeEventListener(r, thorttledCb)
    );
  };
};
export {
  addEventListeners,
  initSessionMonitoring,
  renewSession,
  resetUserIdleTime,
  stopSessionMonitoring,
  subscribeToResetSession,
  subscribeToSessionExpiry,
  subscribeToSessionExpiryWarning,
  trackActivity
};
