// Formatteringshjälpare + Kvartils färgpalett för chartingen.
(function () {
  Kv.fmtPct = function (v, decimals) {
    if (v == null || Number.isNaN(v)) return "—";
    const d = decimals == null ? 1 : decimals;
    return (v * 100).toFixed(d) + "%";
  };

  Kv.fmtPp = function (v, decimals) {
    if (v == null || Number.isNaN(v)) return "—";
    const d = decimals == null ? 1 : decimals;
    const s = (v * 100).toFixed(d);
    return (v >= 0 ? "+" : "") + s + "%";
  };

  Kv.fmtSign = function (v, decimals) {
    if (v == null || Number.isNaN(v)) return "—";
    const d = decimals == null ? 1 : decimals;
    const s = (v * 100).toFixed(d);
    return (v > 0 ? "+" : "") + s + "%";
  };

  // ── Färger (Kvartil Design System: primary + secondary palette) ───
  // Primary:   #00003B navy   #646464 gray   #E83F6F pink   #FAFAFA off-white
  // Secondary: #214BB7 blue   #91B8FF blue-lt   #E0E5EC base   #DC93B0 pink-lt
  Kv.AC_COLORS = {
    "Equities":     "#214BB7",
    "Fixed Income": "#00003B",
    "Commodities":  "#91B8FF",
    "FX":           "#DC93B0",
    "Other":        "#646464",
  };

  Kv.REGION_COLORS = {
    "USA":           "#214BB7",
    "Core Europe":   "#91B8FF",
    "UK":            "#00003B",
    "Asia Ex Japan": "#DC93B0",
    "Japan":         "#646464",
    "Sweden":        "#E83F6F",
    "Global":        "#E0E5EC",
  };

  Kv.CCY_COLORS = {
    "USD": "#214BB7",
    "EUR": "#91B8FF",
    "GBP": "#00003B",
    "JPY": "#646464",
    "SEK": "#E83F6F",
    "CHF": "#DC93B0",
  };

  // Definition av "hemma" (Norden/Sverige)
  Kv.NORDIC_REGIONS = new Set(["Sweden", "Nordic", "Sverige", "Norden"]);

  Kv.isEquity    = inst => inst.asset_class === "Equities";
  Kv.isFI        = inst => inst.asset_class === "Fixed Income";
  Kv.isFX        = inst => inst.is_fx === true;
  Kv.isCommodity = inst => inst.asset_class === "Commodities";
  Kv.isNordic    = inst => Kv.NORDIC_REGIONS.has(inst.region);
  // Alternativa investeringar = allt som inte är aktier, räntor eller FX-overlay.
  // I praktiken: Commodities + Other (SG Trend etc.).
  Kv.isAlt      = inst =>
    !Kv.isEquity(inst) && !Kv.isFI(inst) && !Kv.isFX(inst);

  // Långsiktigt antagande om real vinsttillväxt (real EPS) per region.
  // HÅRDKODAT — detta är husets långsiktiga antaganden, inte marknadsdata ur
  // databasen. Adderas till 1/CAPE för att få förväntad real avkastning på
  // aktier: en investerare får dagens intjäningsavkastning plus den reala
  // tillväxten i intjäningen över tid.
  Kv.REAL_EPS = {
    // OBS: siffrorna finns även i fotnotstexten i CapePanel.jsx — ändra båda.
    "USA":           0.0225,
    "Core Europe":   0.0100,  // "Europa ex Sverige"
    "UK":            0.0100,  // "Storbritannien"
    "Sweden":        0.0125,
    "Japan":         0.0100,
    "Asia Ex Japan": 0.0200,  // "Emerging Markets" — enda EM-regionen i CAPE-datan
  };
  // Okänd region ⇒ 0. En ny CAPE-region ska inte tyst ärva ett antagande; den
  // syns då som 0,00 % i real EPS-kolumnen och får läggas till här medvetet.
  Kv.realEps = region => (Kv.REAL_EPS[region] != null ? Kv.REAL_EPS[region] : 0);

  // Förväntad REAL avkastning per instrument — en enda källa till sanning,
  // används av WeightsTable, CapePanel och app.jsx så att alla siffror matchar:
  //   • aktier            → 1/CAPE (eyld_real) + långsiktig real EPS per region
  //   • räntor            → real yield i instrumentets valuta
  //   • råvaror/FX/övrigt → 0
  //
  // Råvaror antas följa inflationen utan realavkastning, alltså ingen
  // riskpremie och ingen kollateralavkastning. De hade USD real yield mellan
  // 2026-06-08 och 2026-09-22 ("samma antagande som USD-räntebenet"), men det
  // antagandet passar bara ett fullt kollateraliserat total return-index. Av
  // portföljens två råvaror är BB Comdty (BCOMTR) ett sådant, medan Guld är en
  // fysisk ETC (IGLN) utan säkerhet som förräntas — där hade det överskattat
  // avkastningen med hela den reala räntan. Noll för båda är det konservativa
  // och enhetliga valet.
  Kv.instExpectedReal = function (inst, cape, realYield) {
    if (Kv.isEquity(inst) && cape) {
      const c = cape[inst.region];
      if (c && c.eyld_real != null) return c.eyld_real + Kv.realEps(inst.region);
    } else if (Kv.isFI(inst) && realYield) {
      const y = realYield[inst.ccy];
      if (y && y.eyld_real != null) return y.eyld_real;
    }
    return 0;
  };

  // Förväntad inflation i instrumentets EGEN valuta — påslaget som tar en rad
  // från real till nominell avkastning:
  //   • aktier → regionens, härledd ur cape[region].nominal, som backend räknar
  //              med regionens egen valuta. Det täcker även GBP och JPY, som
  //              saknar nominellt ränteinstrument och därför inte finns i
  //              nominalYield.
  //   • räntor/råvaror → instrumentets valuta, nominell − real yield
  //   • FX     → 0, overlayen har ingen förväntad avkastning
  // Sista utväg är SEK, så en rad aldrig tappar inflationspåslaget helt.
  Kv.instInflation = function (inst, cape, realYield, nominalYield) {
    if (Kv.isFX(inst)) return 0;
    if (Kv.isEquity(inst) && cape) {
      const c = cape[inst.region];
      if (c && c.nominal && c.nominal.current != null && c.eyld_real != null)
        return c.nominal.current - c.eyld_real;
    }
    const ccyInfl = (ccy) => {
      const r = realYield && realYield[ccy];
      const n = nominalYield && nominalYield[ccy];
      if (!r || !n || r.eyld_real == null || n.eyld_real == null) return null;
      return n.eyld_real - r.eyld_real;
    };
    const own = ccyInfl(inst.ccy);
    if (own != null) return own;
    const sek = ccyInfl("SEK");
    return sek != null ? sek : 0;
  };

  // Förväntad NOMINELL avkastning per instrument, i tillgångens egen valuta.
  Kv.instExpectedNominal = function (inst, cape, realYield, nominalYield) {
    if (Kv.isFX(inst)) return 0;
    return Kv.instExpectedReal(inst, cape, realYield)
         + Kv.instInflation(inst, cape, realYield, nominalYield);
  };

  // Viktad förväntad avkastning över non-FX-portföljen. Nämnaren = alla
  // non-FX-vikter (övrigt späder ut). Nominell summeras i lokala valutor, dvs.
  // UNDER ANTAGANDE OM OFÖRÄNDRADE VALUTAKURSER — samma antagande som gör att
  // FX-overlayen har förväntad avkastning noll. En enda källa för CapePanel-
  // headern, nyckeltalen och Multi-Asset-rutan, så alla tre visar samma tal.
  Kv.weightedExpected = function (instruments, w, cape, realYield, nominalYield, nominal) {
    let num = 0, den = 0;
    for (let i = 0; i < instruments.length; i++) {
      const inst = instruments[i];
      if (Kv.isFX(inst)) continue;
      num += w[i] * (nominal
        ? Kv.instExpectedNominal(inst, cape, realYield, nominalYield)
        : Kv.instExpectedReal(inst, cape, realYield));
      den += w[i];
    }
    return den > 1e-12 ? num / den : null;
  };

  // ── Översättningar: DB-värden (en) → display (sv) ─────────────────
  Kv.AC_SV = {
    "Equities":     "Aktier",
    "Fixed Income": "Räntor",
    "Commodities":  "Råvaror",
    "FX":           "Valuta",
    "Other":        "Övrigt",
  };
  Kv.REGION_SV = {
    "Sweden":         "Sverige",
    "Core Europe":    "Europa",
    "UK":             "Storbritannien",
    "USA":            "USA",
    "Asia Ex Japan":  "Asien ex. Japan",
    "Japan":          "Japan",
    "Global":         "Global",
    "Nordic":         "Norden",
  };
  Kv.acLabel     = ac => Kv.AC_SV[ac]     || ac;
  Kv.regionLabel = r  => Kv.REGION_SV[r]  || r;

  // Skalpalett för delta-färgning (positiv = design-blå, negativ = pink)
  Kv.deltaColor = function (delta) {
    if (Math.abs(delta) < 1e-4) return "#646464";
    return delta > 0 ? "#214BB7" : "#E83F6F";
  };
})();
