// tilgangsmotor.jsx — Sentral motor for effektiv modultilgang
// Brukes av navigasjon og "Se som bruker" for å begrense visning og data.
// Lastes tidlig i index.html (etter visning-og-admin.jsx).
//
// PRINSIPP: Når man "ser som" en annen bruker, er den effektive tilgangen
// SKJÆRINGSPUNKTET av egen tilgang og vist brukers tilgang — aldri mer enn
// det laveste av de to. Dette håndheves i frontend som et EKSTRA filter på
// toppen av RLS (som fortsatt er den ytre, ufravikelige grensen i databasen).

// Rangering av nivåer (høyere tall = mer tilgang)
const TM_NIVA_RANG = { '·': 0, 'S': 1, 'L': 2, 'R': 3, 'A': 4 };
// Merk: 'S' (kun egne saker) regnes som svakere enn full lese,
// men gir tilgang til modulen for egne data.

// Modul-nøkkel → hvilke route-screens den dekker
const TM_MODUL_SCREENS = {
  forside:      ['home'],
  prosjekt:     ['projects', 'project'],
  vvj:          ['vvj'],
  ressursbank:  ['ressursbank'],
  protokoll:    ['protocols', 'protocol', 'series', 'new-protocol'],
  tiltak:       ['tasks'],
  organisasjon: ['organisation'],
  avtaler:      ['agreements'],
  arshjul:      ['arshjul'],
  styre:        ['styre'],
  rutiner:      ['rutiner'],
  personalhandbok: ['personalhandbok'],
  lederhandbok: ['lederhandbok'],
  okonomi:      ['finance'],
  hr:           ['hr'],
  kvalitet:     ['kvalitet'],
  intranett:    ['intranett'],
  tilgang:      ['settings'],
};

// Cache for tilgangsdata (rolle→modul-matrise + overstyringer)
window._tilgangsCache = { matrise: null, lastet: false };

async function tmLastMatrise() {
  if (window._tilgangsCache.lastet) return window._tilgangsCache.matrise;
  const { data } = await window._sb.from('roller_tilgang')
    .select('niva, roller(nokkel), moduler(nokkel)');
  const m = {};
  (data || []).forEach(rad => {
    const rn = rad.roller?.nokkel, mn = rad.moduler?.nokkel;
    if (!rn || !mn) return;
    if (!m[rn]) m[rn] = {};
    m[rn][mn] = rad.niva;
  });
  window._tilgangsCache.matrise = m;
  window._tilgangsCache.lastet = true;
  return m;
}

// Hent overstyringer for en gitt bruker
async function tmHentOverstyringer(brukerId) {
  if (!brukerId) return {};
  try {
    const { data } = await window._sb.from('bruker_overstyringer')
      .select('niva, moduler(nokkel)').eq('bruker_id', brukerId);
    const o = {};
    (data || []).forEach(r => { if (r.moduler?.nokkel) o[r.moduler.nokkel] = r.niva; });
    return o;
  } catch(e) { return {}; }
}

// Beregn full tilgangsprofil for én bruker: { modulNokkel: niva }
async function tmTilgangForBruker(bruker) {
  if (!bruker) return {};
  // Admin/superbruker = full tilgang overalt
  if (bruker.admin_nivaa === 'superbruker' || bruker.admin_nivaa === 'administrator') {
    const alle = {};
    Object.keys(TM_MODUL_SCREENS).forEach(mn => { alle[mn] = 'A'; });
    return alle;
  }
  const matrise = await tmLastMatrise();
  const overstyringer = await tmHentOverstyringer(bruker.id);
  const rolleN = bruker.roller?.nokkel || bruker.rolle_nokkel;
  const rolleTilgang = (rolleN && matrise[rolleN]) ? matrise[rolleN] : {};

  const profil = {};
  Object.keys(TM_MODUL_SCREENS).forEach(mn => {
    const over = overstyringer[mn];
    const fraRolle = rolleTilgang[mn] || '·';
    profil[mn] = (over && over !== '·') ? over : fraRolle;
  });
  return profil;
}

// Skjæringspunkt: laveste nivå av to profiler per modul
function tmSkjaering(profilA, profilB) {
  const resultat = {};
  Object.keys(TM_MODUL_SCREENS).forEach(mn => {
    const a = TM_NIVA_RANG[profilA[mn] || '·'] ?? 0;
    const b = TM_NIVA_RANG[profilB[mn] || '·'] ?? 0;
    const lavest = Math.min(a, b);
    resultat[mn] = Object.keys(TM_NIVA_RANG).find(k => TM_NIVA_RANG[k] === lavest) || '·';
  });
  return resultat;
}

// ── Global effektiv tilgang (oppdateres når "se som" endres) ──

window._effektivTilgang = {
  profil: null,        // { modulNokkel: niva } — den effektive tilgangen som gjelder NÅ
  begrenset: false,    // true hvis "se som" innskrenker
  listeners: [],
  abonner(fn) { this.listeners.push(fn); return () => { this.listeners = this.listeners.filter(l => l !== fn); }; },
  _varsle() { this.listeners.forEach(fn => fn({ profil: this.profil, begrenset: this.begrenset })); },

  // Beregn på nytt basert på ekte + evt. vist bruker
  async oppdater(ekteBruker, visningBruker) {
    if (!ekteBruker) { this.profil = null; this.begrenset = false; this._varsle(); return; }
    const egen = await tmTilgangForBruker(ekteBruker);
    if (visningBruker) {
      const vist = await tmTilgangForBruker(visningBruker);
      this.profil = tmSkjaering(egen, vist);   // aldri mer enn egen, begrenset til vist
      this.begrenset = true;
    } else {
      this.profil = egen;
      this.begrenset = false;
    }
    this._varsle();
  },

  // Har aktiv visning tilgang til en gitt modul? (nivå over "ingen")
  harModul(modulNokkel) {
    if (!this.profil) return true; // ikke beregnet ennå — vis alt (RLS beskytter uansett)
    return (this.profil[modulNokkel] || '·') !== '·';
  },

  // Har tilgang til en route-screen?
  harScreen(screen) {
    if (!this.profil) return true;
    const modul = Object.keys(TM_MODUL_SCREENS).find(mn => TM_MODUL_SCREENS[mn].includes(screen));
    if (!modul) return true; // ukjent screen — ikke blokker
    return this.harModul(modul);
  },

  niva(modulNokkel) {
    if (!this.profil) return 'A';
    return this.profil[modulNokkel] || '·';
  },
};

// Hook for komponenter
function useEffektivTilgang() {
  const [state, setState] = React.useState({
    profil: window._effektivTilgang.profil,
    begrenset: window._effektivTilgang.begrenset,
  });
  React.useEffect(() => window._effektivTilgang.abonner(setState), []);
  return state;
}

// Koble tilgangsmotoren til visning-state: oppdater effektiv tilgang
// hver gang ekte bruker settes eller "se som" endres.
if (window._visningState) {
  window._visningState.abonner(({ ekteBruker, visningBruker }) => {
    window._effektivTilgang.oppdater(ekteBruker, visningBruker);
  });
}

Object.assign(window, {
  tmTilgangForBruker, tmSkjaering, useEffektivTilgang,
  TM_MODUL_SCREENS, TM_NIVA_RANG,
});
