// Browser-Ersatz für das frühere Session-Management im Electron-Hauptprozess (main.cjs):
// Access-Token nur als Modul-Variable im Speicher (verschwindet bei Reload/Tab-Schließen),
// Refresh-Token liegt jetzt als HttpOnly-Cookie beim Server (nie per JS lesbar, siehe
// omsorgCore/CLAUDE.md "Auth-Flow") - refreshSilently() schickt dafür nur noch
// credentials:"include" (via authApi.refresh()), ohne den Token selbst zu kennen.
import { refresh as refreshRequest, me as meRequest } from "./authApi.js";

const REFRESH_BUFFER_MS = 2 * 60 * 1000;

let accessToken = null;
let expiresAt = null;
let user = null;
let mustChangePassword = false;
let refreshTimer = null;
const listeners = new Set();

export function getSessionSnapshot() {
  return { isAuthenticated: !!accessToken, user, mustChangePassword };
}

function notify() {
  const snapshot = getSessionSnapshot();
  listeners.forEach((cb) => cb(snapshot));
}

export function getAccessToken() {
  return accessToken;
}

export function onSessionChanged(callback) {
  listeners.add(callback);
  return () => listeners.delete(callback);
}

// Rein für die Anzeige "angemeldet als ..." - die Signaturprüfung passiert serverseitig
// bei jedem authentifizierten Aufruf, hier wird nichts sicherheitsrelevantes entschieden.
function decodeJwtClaims(token) {
  try {
    const payload = token.split(".")[1];
    const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
    const json = decodeURIComponent(
      atob(base64)
        .split("")
        .map((c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0"))
        .join("")
    );
    return JSON.parse(json);
  } catch {
    return {};
  }
}

function userFromAccessToken(token) {
  const claims = decodeJwtClaims(token);
  return {
    username: claims["name"] || claims["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] || null,
    role: claims["role"] || claims["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"] || null
  };
}

function scheduleRefresh() {
  if (refreshTimer) clearTimeout(refreshTimer);
  if (!expiresAt) return;
  const delay = Math.max(new Date(expiresAt).getTime() - Date.now() - REFRESH_BUFFER_MS, 5000);
  refreshTimer = setTimeout(refreshSilently, delay);
}

export function applySession(tokenPair) {
  accessToken = tokenPair.accessToken;
  expiresAt = tokenPair.expiresAt;
  user = userFromAccessToken(tokenPair.accessToken);
  mustChangePassword = !!tokenPair.mustChangePassword;
  scheduleRefresh();
  notify();
  refreshPermissions();
}

// Rechte kommen nicht aus dem JWT (das trägt nur den Rollennamen), sondern aus /api/auth/me -
// läuft nach dem schnellen JWT-basierten Zwischenstand nach und broadcastet erneut, sobald da.
async function refreshPermissions() {
  const tokenAtCallTime = accessToken;
  if (!tokenAtCallTime) return;
  const profile = await meRequest(tokenAtCallTime);
  if (!profile.ok || accessToken !== tokenAtCallTime) return;
  user = { username: profile.username, role: profile.role, permissions: profile.permissions };
  notify();
}

export function clearSession() {
  accessToken = null;
  expiresAt = null;
  user = null;
  mustChangePassword = false;
  if (refreshTimer) clearTimeout(refreshTimer);
  refreshTimer = null;
  notify();
}

// Proaktiver Timer kurz vor Ablauf UND Fallback bei 401 (z.B. nach langer Inaktivität) -
// schickt keinen Token mehr mit, die HttpOnly-Cookie übernimmt das.
export async function refreshSilently() {
  const result = await refreshRequest();
  if (!result.ok) {
    clearSession();
    return false;
  }
  applySession(result);
  return true;
}

// Ersetzt main.cjs' bootstrapSession(): läuft einmal beim App-Start, damit ein Reload
// (der die Modul-Variable oben leert) die Session über die noch gültige HttpOnly-Cookie
// transparent wiederherstellt, statt den Nutzer erneut einloggen zu lassen.
export async function bootstrapSession() {
  if (accessToken) return true;
  return refreshSilently();
}

// Ruft callFn() auf und wiederholt einmal nach Silent-Refresh bei 401 - gemeinsame
// Grundlage für alle <resource>Api.js-Aufrufe und den generischen api:*-Proxy.
export async function withAuthRetry(callFn) {
  if (!accessToken) return { ok: false, status: 401, data: null };
  let result = await callFn();
  if (result.status === 401 && (await refreshSilently())) {
    result = await callFn();
  }
  return result;
}
