// budsjett.jsx — Budsjettering på avdelingsnivå med konsernfordeling,
// viderefakturering, selskapsintern fordeling og eliminering.
// Henter alt fra Supabase (ingen ekstern API ennå). Full CRUD.
//
// Screen-nøkkel: 'budsjett'. Eksporterer BudsjettWorkspace til window.

// ── Hjelpere ─────────────────────────────────────────────────
function budFmtKr(n) {
  if (n == null) return '—';
  const abs = Math.abs(n);
  if (abs >= 1e6) return (n/1e6).toFixed(1).replace('.', ',') + ' M';
  if (abs >= 1e3) return Math.round(n/1e3) + ' k';
  return Math.round(n).toString();
}
function budFmtKrFull(n) {
  if (n == null) return '—';
  return new Intl.NumberFormat('nb-NO').format(Math.round(n)) + ' kr';
}
function budPct(n) { return (n||0).toFixed(1).replace('.', ',') + ' %'; }

// ── Datahenting ──────────────────────────────────────────────
async function budHentAlt() {
  const sb = window._sb;
  const [ve, en, se, dr, dv, nk, fs, li, fo, fm, rl, km, kp, dm, lo, gj, ba, op, av, pr] = await Promise.all([
    sb.from('bud_versjoner').select('*').order('ar', { ascending: false }),
    sb.from('enheter').select('id, nokkel, navn, avd_nr, selskap_id, type, aktiv').eq('aktiv', true),
    sb.from('selskaper').select('id, nokkel, navn, type, parent_id').order('sortering'),
    sb.from('bud_drivere').select('*'),
    sb.from('bud_driververdier').select('*'),
    sb.from('bud_fordelingsnokler').select('*'),
    sb.from('bud_fordeling_satser').select('*'),
    sb.from('bud_linjer').select('*'),
    sb.from('bud_fordelinger').select('*'),
    sb.from('bud_fordeling_mottakere').select('*'),
    sb.from('bud_rapportlinjer').select('*').order('sortering'),
    sb.from('bud_kontomapping').select('*'),
    sb.from('bud_kontoplan').select('*').order('sortering'),
    sb.from('bud_deltaker_matrise').select('*'),
    sb.from('bud_lonn').select('*'),
    sb.from('bud_grunnlag_justering').select('*'),
    sb.from('bud_avtaler').select('*'),
    sb.from('bud_oppgaver').select('*'),
    sb.from('avtaler').select('id, name, type, motpart, avdeling, enhet_id, eier_navn, arsverdi, totalverdi, status, start_dato, slutt_dato'),
    sb.from('profiles').select('id, navn, epost, enhet_id').eq('status','aktiv'),
  ]);
  const feil = [ve,en,se,dr,dv,nk,fs,li,fo,fm,rl,km,kp,dm,lo,gj,ba,op].find(r => r.error);
  if (feil) throw feil.error;
  return {
    versjoner: ve.data || [], enheter: en.data || [], selskaper: se.data || [],
    drivere: dr.data || [], driververdier: dv.data || [],
    nokler: nk.data || [], satser: fs.data || [],
    linjer: li.data || [], fordelinger: fo.data || [], mottakere: fm.data || [],
    rapportlinjer: rl.data || [], kontomapping: km.data || [], kontoplan: kp.data || [],
    deltakere: dm.data || [], lonn: lo.data || [], grunnlagJust: gj.data || [],
    budAvtaler: ba.data || [], oppgaver: op.data || [], avtaler: av.data || [], profiler: pr.data || [],
  };
}

// ── CRUD-hjelpere ────────────────────────────────────────────
const budDB = {
  async lagre(tabell, rad)   { const { data, error } = await window._sb.from(tabell).insert([rad]).select().single(); if (error) throw error; return data; },
  async oppdater(tabell, id, felt) { const { error } = await window._sb.from(tabell).update(felt).eq('id', id); if (error) throw error; },
  async slett(tabell, id)    { const { error } = await window._sb.from(tabell).delete().eq('id', id); if (error) throw error; },
  // upsert kontomapping per (versjon, selskap, konto)
  async lagreMapping(rad)    { const { error } = await window._sb.from('bud_kontomapping').upsert(rad, { onConflict: 'versjon_id,selskap_id,konto' }); if (error) throw error; },
};

// ── KJERNE: beregn fordelinger til konteringer per avdeling ──
// Hver postering får intern_type fra kontomapping (per selskap) hvis satt,
// ellers fra fordelingens eliminer_nivaa, ellers kontoplanens standard.
function budBeregnPosteringer(d, versjonId) {
  const enhById = Object.fromEntries(d.enheter.map(e => [e.id, e]));
  const kpByKonto = Object.fromEntries(d.kontoplan.map(k => [k.konto, k]));
  const post = [];

  // Slå opp intern_type for (selskap, konto): mapping-spesifikk → kontoplan-standard
  function internType(selskapId, konto, fallback) {
    const m = d.kontomapping.find(x => x.versjon_id === versjonId && x.konto === konto &&
      (x.selskap_id === selskapId || x.selskap_id == null));
    if (m && m.intern_type) return m.intern_type;
    if (fallback) return fallback;
    return kpByKonto[konto]?.standard_intern || 'ekstern';
  }

  // 1a. Lønnsbudsjett → posteringer per (avd, konto)
  const lonnMap = {};
  d.lonn.filter(l => l.versjon_id === versjonId).forEach(l => {
    const mnd = Math.max(0,(Number(l.mnd_til||12)-Number(l.mnd_fra||1)+1));
    const grunn = Number(l.manedslonn||0)*(Number(l.stillingspct||100)/100)*Number(l.antall||1)*mnd;
    let sosial = grunn*((Number(l.aga_pct||0)+Number(l.pensjon_pct||0)+Number(l.sosial_pct||0))/100);
    (Array.isArray(l.sosiale_poster)?l.sosiale_poster:[]).forEach(p => {
      if (p.pct!=null && p.pct!=='') sosial += grunn*(Number(p.pct)/100);
      else if (p.belop!=null && p.belop!=='') sosial += Number(p.belop);
    });
    const key = l.avdeling_id+'|'+(l.konto||5000);
    lonnMap[key] = (lonnMap[key]||0) + Math.round(grunn+sosial);
  });
  Object.entries(lonnMap).forEach(([key, belop]) => {
    const [avd, konto] = key.split('|');
    const e = enhById[avd];
    post.push({ avd_id:avd, selskap_id:e?.selskap_id, konto:Number(konto), belop,
      type:'lonn', kilde:'Lønnsbudsjett', eliminer_nivaa:internType(e?.selskap_id, Number(konto),'ingen'),
      tekst:'Lønnsbudsjett', lonn:true });
  });

  // 1. Grunnbudsjett
  d.linjer.filter(l => l.versjon_id === versjonId).forEach(l => {
    const e = enhById[l.avdeling_id];
    post.push({
      avd_id: l.avdeling_id, selskap_id: e?.selskap_id, konto: l.konto,
      belop: Number(l.belop_ar), type: 'grunnbudsjett', kilde: null,
      eliminer_nivaa: internType(e?.selskap_id, l.konto, 'ingen'),
      tekst: l.tekst, linje_id: l.id,
    });
  });

  function nokkelAndeler(nokkel) {
    const andeler = {};
    if (nokkel.grunnlag === 'fast') {
      d.satser.filter(s => s.nokkel_id === nokkel.id).forEach(s => { andeler[s.avdeling_id] = Number(s.prosent)/100; });
    } else if (nokkel.grunnlag === 'driver' && nokkel.driver_id) {
      const drv = d.drivere.find(x => x.id === nokkel.driver_id);
      if (drv && drv.nokkel === 'deltakere') {
        // Bruk deltakermatrisen som grunnlag
        return budDeltakerAndeler(d, versjonId);
      }
      const verdier = d.driververdier.filter(v => v.driver_id === nokkel.driver_id);
      const sum = verdier.reduce((s,v)=>s+Number(v.verdi),0);
      if (sum > 0) verdier.forEach(v => { andeler[v.avdeling_id] = Number(v.verdi)/sum; });
    }
    return andeler;
  }

  // 2. Fordelinger
  d.fordelinger.filter(f => f.versjon_id === versjonId && f.aktiv).forEach(f => {
    const kildeEnh = enhById[f.kilde_avd_id];
    let grunnlag = Number(f.grunnlag_belop) || 0;
    if (!grunnlag) {
      grunnlag = d.linjer.filter(l => l.versjon_id===versjonId && l.avdeling_id===f.kilde_avd_id && l.konto===f.konto)
        .reduce((s,l)=>s+Number(l.belop_ar),0);
    }
    const just = d.grunnlagJust.filter(g => g.fordeling_id === f.id).reduce((s,g)=>s+Number(g.belop),0);
    const medPaaslag = (grunnlag + just) * (1 + Number(f.paaslag_pct)/100);

    const eksplisitt = d.mottakere.filter(m => m.fordeling_id === f.id);
    let fordeling = {};
    if (eksplisitt.length) {
      eksplisitt.forEach(m => {
        if (m.fast_belop != null) fordeling[m.avdeling_id] = Number(m.fast_belop) * (1 + Number(f.paaslag_pct)/100);
        else if (m.andel_pct != null) fordeling[m.avdeling_id] = medPaaslag * (Number(m.andel_pct)/100);
      });
    } else if (f.nokkel_id) {
      const nokkel = d.nokler.find(n => n.id === f.nokkel_id);
      if (nokkel) {
        const andeler = nokkelAndeler(nokkel);
        Object.entries(andeler).forEach(([avd, andel]) => { if (avd !== f.kilde_avd_id) fordeling[avd] = medPaaslag*andel; });
        const sumF = Object.values(fordeling).reduce((s,x)=>s+x,0);
        if (sumF > 0 && Math.abs(sumF - medPaaslag) > 1) { const sk = medPaaslag/sumF; Object.keys(fordeling).forEach(k => fordeling[k]*=sk); }
      }
    }
    const totalUt = Object.values(fordeling).reduce((s,x)=>s+x,0);
    const elim = f.eliminer_nivaa || internType(kildeEnh?.selskap_id, f.motpart_konto || f.konto, 'konsern');

    post.push({
      avd_id: f.kilde_avd_id, selskap_id: kildeEnh?.selskap_id,
      konto: f.motpart_konto || f.konto, belop: -totalUt,
      type: f.type, kilde: f.navn, eliminer_nivaa: elim,
      tekst: f.navn + ' (kreditt)', fordeling_id: f.id, motpost: true,
    });
    Object.entries(fordeling).forEach(([avd, belop]) => {
      const mEnh = enhById[avd];
      post.push({
        avd_id: avd, selskap_id: mEnh?.selskap_id, konto: f.konto, belop,
        type: f.type, kilde: f.navn, eliminer_nivaa: elim, tekst: f.navn, fordeling_id: f.id,
      });
    });
  });

  return post;
}

// ── Aggregering med valgbar eliminering ──────────────────────
function budAggreger(posteringer, nivaa) {
  return posteringer.filter(p => {
    if (nivaa === 'avdeling') return true;
    if (nivaa === 'selskap')  return p.eliminer_nivaa !== 'selskap';
    if (nivaa === 'konsern')  return p.eliminer_nivaa === 'ingen' || p.eliminer_nivaa === 'ekstern';
    return true;
  });
}


// ── Lønnsberegning ───────────────────────────────────────────
// Beregner årlig lønnskostnad inkl. sosiale kostnader for en lønnsrad.
function budLonnAarlig(rad) {
  const mnd = Math.max(0, (Number(rad.mnd_til||12) - Number(rad.mnd_fra||1) + 1));
  const grunnlonn = Number(rad.manedslonn||0) * (Number(rad.stillingspct||100)/100) * Number(rad.antall||1) * mnd;
  // Faste prosent-poster
  let sosial = grunnlonn * ((Number(rad.aga_pct||0) + Number(rad.pensjon_pct||0) + Number(rad.sosial_pct||0))/100);
  // Valgfrie ekstra sosiale poster (jsonb): [{navn, pct}] eller [{navn, belop}]
  const ekstra = Array.isArray(rad.sosiale_poster) ? rad.sosiale_poster : [];
  ekstra.forEach(p => {
    if (p.pct != null && p.pct !== '') sosial += grunnlonn * (Number(p.pct)/100);
    else if (p.belop != null && p.belop !== '') sosial += Number(p.belop);
  });
  return { grunnlonn, sosial, total: grunnlonn + sosial };
}

// Summer lønnsbudsjett per (avdeling, konto) → bidrar til posteringer
function budLonnPerAvdKonto(d, versjonId) {
  const ut = {}; // key "avd|konto" -> belop
  d.lonn.filter(l => l.versjon_id === versjonId).forEach(l => {
    const { total } = budLonnAarlig(l);
    const key = l.avdeling_id + '|' + (l.konto || 5000);
    ut[key] = (ut[key] || 0) + Math.round(total);
  });
  return ut;
}

// ── Deltakermatrise som driver-andeler ───────────────────────
// Returnerer { avd_id: andel(0-1) } basert på sum deltakere per avdeling.
function budDeltakerAndeler(d, versjonId, kategori) {
  const rader = d.deltakere.filter(x => x.versjon_id === versjonId && (!kategori || x.kategori === kategori));
  const perAvd = {};
  rader.forEach(r => { perAvd[r.avdeling_id] = (perAvd[r.avdeling_id]||0) + Number(r.antall); });
  const sum = Object.values(perAvd).reduce((s,x)=>s+x,0);
  const andeler = {};
  if (sum > 0) Object.entries(perAvd).forEach(([a,v]) => { andeler[a] = v/sum; });
  return andeler;
}

// ── CSV eksport/import av kontomapping ───────────────────────
function budMappingTilCSV(d, versjonId) {
  const selByid = Object.fromEntries(d.selskaper.map(s => [s.id, s]));
  const rlById = Object.fromEntries(d.rapportlinjer.map(r => [r.id, r]));
  const rader = [['konto','kontonavn','selskap_nokkel','intern_type','rapportlinje_nr']];
  // Én rad per (kontoplan-konto × selskap) + felles
  d.kontoplan.forEach(k => {
    // Felles (selskap_nokkel tom)
    const felles = d.kontomapping.find(m => m.versjon_id===versjonId && m.konto===k.konto && m.selskap_id==null);
    rader.push([k.konto, k.navn, '', felles?.intern_type || k.standard_intern || 'ekstern', felles?.rapportlinje_id ? (rlById[felles.rapportlinje_id]?.linjenr||'') : '']);
    // Per selskap som har egen rad
    d.kontomapping.filter(m => m.versjon_id===versjonId && m.konto===k.konto && m.selskap_id!=null).forEach(m => {
      rader.push([k.konto, k.navn, selByid[m.selskap_id]?.nokkel||'', m.intern_type||'ekstern', m.rapportlinje_id ? (rlById[m.rapportlinje_id]?.linjenr||'') : '']);
    });
  });
  return rader.map(r => r.map(c => {
    const s = String(c ?? '');
    return /[",;\n]/.test(s) ? '"'+s.replace(/"/g,'""')+'"' : s;
  }).join(';')).join('\n');
}

function budParseCSV(tekst) {
  const linjer = tekst.replace(/\r/g,'').split('\n').filter(l => l.trim());
  if (!linjer.length) return [];
  const splitt = (l) => {
    const ut=[]; let cur=''; let q=false;
    for (let i=0;i<l.length;i++){ const c=l[i];
      if(q){ if(c==='"'){ if(l[i+1]==='"'){cur+='"';i++;} else q=false; } else cur+=c; }
      else { if(c==='"') q=true; else if(c===';'||c===',') { ut.push(cur); cur=''; } else cur+=c; }
    } ut.push(cur); return ut;
  };
  const head = splitt(linjer[0]).map(h => h.trim().toLowerCase());
  return linjer.slice(1).map(l => { const c=splitt(l); const o={}; head.forEach((h,i)=>o[h]=(c[i]||'').trim()); return o; });
}

// Importer CSV-rader → upsert kontomapping. Returnerer { ok, feil[] }
async function budImporterMapping(d, versjonId, rader) {
  const selByNokkel = Object.fromEntries(d.selskaper.map(s => [s.nokkel, s]));
  const rlByNr = Object.fromEntries(d.rapportlinjer.filter(r=>r.versjon_id===versjonId).map(r => [String(r.linjenr), r]));
  let ok = 0; const feil = [];
  for (const r of rader) {
    const konto = parseInt(r.konto, 10);
    if (!konto) { feil.push(`Ugyldig konto: ${r.konto}`); continue; }
    const it = (r.intern_type||'ekstern').toLowerCase();
    if (!['ekstern','konsern','selskap'].includes(it)) { feil.push(`Konto ${konto}: ugyldig intern_type "${r.intern_type}"`); continue; }
    const sel = r.selskap_nokkel ? selByNokkel[r.selskap_nokkel] : null;
    if (r.selskap_nokkel && !sel) { feil.push(`Konto ${konto}: ukjent selskap "${r.selskap_nokkel}"`); continue; }
    const rl = r.rapportlinje_nr ? rlByNr[String(r.rapportlinje_nr)] : null;
    try {
      await budDB.lagreMapping({
        versjon_id: versjonId, selskap_id: sel ? sel.id : null, konto,
        intern_type: it, rapportlinje_id: rl ? rl.id : null,
      });
      ok++;
    } catch(e){ feil.push(`Konto ${konto}: ${e.message}`); }
  }
  return { ok, feil };
}

// ── Avtale → budsjettlinje (innarbeiding) ────────────────────
async function budInnarbeidAvtale(ba) {
  // Opprett en budsjettlinje fra en avtalekobling og merk koblingen innarbeidet
  const linje = await budDB.lagre('bud_linjer', {
    versjon_id: ba.versjon_id, avdeling_id: ba.avdeling_id, konto: ba.konto || 3000,
    tekst: 'Avtale: ' + (ba.note || ''), belop_ar: -(Math.abs(ba.budsjett_belop)||0), kilde: 'avtale',
  });
  await budDB.oppdater('bud_avtaler', ba.id, { status: 'innarbeidet', linje_id: linje.id });
  return linje;
}

// Avtaler som IKKE er koblet til budsjettet for en versjon
function budAvtalerUtenforBudsjett(d, versjonId) {
  const koblede = new Set(d.budAvtaler.filter(b => b.versjon_id === versjonId).map(b => b.avtale_id));
  return d.avtaler.filter(a => !koblede.has(a.id) && a.status !== 'utløpt');
}

Object.assign(window, {
  budFmtKr, budFmtKrFull, budPct, budHentAlt, budBeregnPosteringer, budAggreger, budDB,
  budLonnAarlig, budLonnPerAvdKonto, budDeltakerAndeler,
  budMappingTilCSV, budParseCSV, budImporterMapping,
  budInnarbeidAvtale, budAvtalerUtenforBudsjett,
});
