// Förväntad real/nominell avkastning per marknad, uppdelad i sina termer.
//   • Aktier real:     1/CAPE + långsiktig real EPS
//   • Aktier nominell: 1/CAPE + långsiktig real EPS + inflation
//   • Räntor real:     real 5Y yield (en enda term — ingen uppdelning)
//   • Räntor nominell: real 5Y yield + inflation
// Termerna visas som kolumner till vänster om whisker-charten och summerar
// till värdet i summakolumnen, som också är "nu"-markören i charten.
// Portföljens viktade real OCH nominell visas båda i headern med delta
// mot Multi-Asset.
(function () {
  const { useMemo, useState } = React;

  const REGION_ORDER = [
    "Sweden", "Core Europe", "UK", "USA", "Asia Ex Japan", "Japan",
  ];
  const CCY_ORDER = ["SEK", "EUR", "USD"];
  const CCY_TO_COUNTRY = { SEK: "Sverige", EUR: "Europa", USD: "USA" };
  // För att räkna nominell aktie-E[r] (1/CAPE + lokal inflation)
  const REGION_TO_CCY = {
    "Sweden":        "SEK",
    "Core Europe":   "EUR",
    "USA":           "USD",
    "Asia Ex Japan": "USD",
    "UK":            "GBP",
    "Japan":         "JPY",
  };

  // Två steg, inte fyra. Mellanstegen (#91B8FF, #DC93B0) lästes som samma färg
  // med sänkt opacitet — Japan hamnade ensam över 0,25 i "all"-fönstret och såg
  // ut som en renderingsmiss i stället för information.
  function pctlColor(p) {
    return p > 0.5 ? "#214BB7"   // billigt mot egen historik = blå
                   : "#E83F6F";  // dyrt = pink
  }

  // volSelected/volStrat = historisk annualiserad volatilitet ur backtestet.
  // Valfria: utan dem visas bara de två avkastningsraderna, som tidigare.
  Kv.CapePanel = function CapePanel({ cape, realYield, nominalYield, wSelected, instruments,
                                      volSelected, volStrat, showComparison = true }) {
    const [mode, setMode] = useState("real");
    const [win,  setWin]  = useState("all");

    // Plocka rätt window-block. Falls back: 10y → all → 3y.
    function pickWin(obj) {
      if (!obj) return null;
      return obj[win] || obj["all"] || obj["10y"] || obj["3y"] || null;
    }

    const stratW = useMemo(() => instruments.map(i => i.strat_w), [instruments]);

    // Portföljnivå via Kv.weightedExpected — samma hjälpare som nyckeltalen och
    // Multi-Asset-rutan, så alla tre visar samma tal. Nominellt summeras varje
    // rad i sin egen valuta, dvs. under antagande om oförändrade valutakurser.
    const wx = (w, nominal) =>
      Kv.weightedExpected(instruments, w, cape, realYield, nominalYield, nominal);
    const deps = [instruments, cape, realYield, nominalYield];
    const eRealSel   = useMemo(() => wx(wSelected, false), [wSelected, ...deps]);
    const eRealStrat = useMemo(() => wx(stratW,    false), [stratW,    ...deps]);
    const eNomSel    = useMemo(() => wx(wSelected, true),  [wSelected, ...deps]);
    const eNomStrat  = useMemo(() => wx(stratW,    true),  [stratW,    ...deps]);

    // "1 år av 6": under ett normalfördelningsantagande hamnar utfallet under
    // µ − 1 standardavvikelse med 15,9 % sannolikhet, dvs. ungefär vart sjätte
    // år. Volatiliteten är historisk och nominell, så termen räknas mot den
    // NOMINELLA förväntade avkastningen — att blanda den med den reala hade
    // gett en distribution vars mitt och spridning mäts i olika enheter.
    const hasVol    = volSelected != null || volStrat != null;
    const downSel   = (eNomSel   != null && volSelected != null) ? eNomSel   - volSelected : null;
    const downStrat = (eNomStrat != null && volStrat    != null) ? eNomStrat - volStrat    : null;

    const hasCape    = cape && Object.keys(cape).length > 0;
    const hasReal    = realYield && Object.keys(realYield).length > 0;
    const hasNominal = nominalYield && Object.keys(nominalYield).length > 0;
    if (!hasCape && !hasReal) return null;

    const yieldSource = mode === "nominal" && hasNominal ? nominalYield : realYield;
    const yieldLabel  = mode === "nominal" ? "nominell 5Y" : "real 5Y";

    const rows = useMemo(() => {
      const out = [];
      const isNom = mode === "nominal";
      if (hasCape) {
        out.push({
          type: "section",
          label: "Aktier",
          cols: isNom ? ["1/CAPE", "Real EPS", "Inflation"] : ["1/CAPE", "Real EPS"],
          sumLabel: isNom ? "= Nominell" : "= Real",
        });
        const known   = new Set(REGION_ORDER);
        const ordered = REGION_ORDER.concat(
          Object.keys(cape).filter(r => !known.has(r)).sort()
        );
        for (const r of ordered) {
          const c = cape[r];
          if (!c) continue;
          // Nominell: använd c.nominal[window]-stats om det finns. Annars
          // fall back till real-stats för det fönstret (= ingen inflation pålagd).
          const useNom = isNom && c.nominal;
          const n = useNom ? pickWin(c.nominal) : null;
          const real = pickWin(c.real);
          if (!real && !n) continue;

          // Real EPS är en konstant, så hela fördelningen skiftas lika mycket.
          // Annars hade "nu"-markören hamnat utanför sitt eget historikspann.
          const eps = Kv.realEps(r);
          // Backend räknar c.nominal som 1/CAPE + inflation per datum. Inflations-
          // termen härleds därför som differensen, så de visade termerna summerar
          // exakt till markören i stället för att hämtas från en annan tidpunkt.
          const infl = useNom ? c.nominal.current - c.eyld_real : null;
          const terms = isNom ? [c.eyld_real, eps, infl] : [c.eyld_real, eps];

          out.push({
            type:    "data",
            label:   Kv.regionLabel(r),
            suffix:  c.current != null ? c.current.toFixed(1) : null,
            terms,
            // Real-stats är i CAPE-units, invertera till 1/CAPE-skala.
            eMin:    (n ? n.min    : 1 / real.max)    + eps,
            eMax:    (n ? n.max    : 1 / real.min)    + eps,
            eP25:    (n ? n.p25    : 1 / real.p75)    + eps,
            eP75:    (n ? n.p75    : 1 / real.p25)    + eps,
            eMed:    (n ? n.median : 1 / real.median) + eps,
            // Aktuellt värde ligger på c.nominal.current resp. c.eyld_real (toppnivå).
            eCur:    (n ? c.nominal.current : c.eyld_real) + eps,
            // Real-percentilen är på CAPE → invertera (1 − pct = 1/CAPE-pctl).
            // Nominal-percentilen är redan på 1/CAPE+inflation-skalan.
            // Ett konstant skift ändrar inte percentilen.
            yPctl:   n ? n.percentile : 1 - real.percentile,
          });
        }
      }
      if (yieldSource) {
        out.push({
          type: "section",
          label: "Räntor (" + yieldLabel + " yield)",
          // Real: räntan ÄR summan, ingen uppdelning. Nominell: real + inflation.
          cols: isNom ? ["Real 5Y", "Inflation"] : [],
          sumLabel: isNom ? "= Nominell" : "Real 5Y",
        });
        const known   = new Set(CCY_ORDER);
        const ordered = CCY_ORDER.concat(
          Object.keys(yieldSource).filter(c => !known.has(c)).sort()
        );
        for (const ccy of ordered) {
          const y = yieldSource[ccy];
          if (!y) continue;
          const yw = pickWin(y);
          if (!yw) continue;
          // I nominellt läge är yieldSource nominalYield; realdelen hämtas ur
          // realYield och inflationen är differensen dem emellan.
          const yr = isNom && realYield ? realYield[ccy] : null;
          const rv = yr && yr.eyld_real != null ? yr.eyld_real : null;
          out.push({
            type:   "data",
            label:  CCY_TO_COUNTRY[ccy] || ccy,
            suffix: null,
            terms:  isNom ? [rv, rv != null ? y.eyld_real - rv : null] : [],
            eMin:   yw.min,
            eMax:   yw.max,
            eP25:   yw.p25,
            eP75:   yw.p75,
            eMed:   yw.median,
            eCur:   y.eyld_real,
            yPctl:  yw.percentile,
          });
        }
      }
      return out;
    }, [cape, yieldSource, yieldLabel, hasCape, mode, win, realYield]);

    // Vänstermarginalen är en liten tabell: etikett + upp till tre termkolumner
    // + summa. Bredderna anges explicit och marginalen räknas ut ur dem — all
    // text här är textAnchor="end" och växer alltså åt VÄNSTER från sin
    // högerkant, så en för smal kolumn skriver över kolumnen till vänster.
    const MAX_TERMS = 3;
    const labelW = 180;   // rymmer "RÄNTOR (NOMINELL 5Y YIELD)" i versaler
    const colW   = 60;    // en termkolumn, ~28 px text + luft
    const sumW   = 66;    // summan är fetare och kan bli tvåsiffrig: "10.13%"
    const padR   = 14;    // luft mellan summakolumnen och chartens vänsterkant

    const W = 1140;
    const mg = { l: labelW + MAX_TERMS * colW + sumW + padR, r: 16, t: 8, b: 32 };
    const dataH = 20, sectH = 18, gap = 3;

    const xLabelR = labelW;
    const xTermR  = slot => labelW + (slot + 1) * colW;
    const xSumR   = mg.l - padR;
    // Termerna högerställs mot summakolumnen, så samma term (t.ex. inflation)
    // hamnar i samma kolumn i båda sektionerna även när de har olika många.
    const slotFor = (j, k) => MAX_TERMS - k + j;

    let yEnd = mg.t;
    rows.forEach(r => {
      yEnd += (r.type === "section" ? sectH : dataH) + gap;
    });
    yEnd -= gap;
    const H = yEnd + mg.b;

    const xLeft  = mg.l;
    const xRight = W - mg.r;
    const xRange = xRight - xLeft;

    // Global skala över alla synliga datarader så x-axeln längst ner är meningsfull.
    let globalMin = Infinity, globalMax = -Infinity;
    rows.forEach(r => {
      if (r.type !== "data") return;
      if (r.eMin < globalMin) globalMin = r.eMin;
      if (r.eMax > globalMax) globalMax = r.eMax;
    });
    if (!isFinite(globalMin)) { globalMin = 0; globalMax = 0.1; }
    // Snappa till 2%-grid så ticks landar på jämna procent
    const axStep = 0.02;
    globalMin = Math.floor(globalMin / axStep) * axStep;
    globalMax = Math.ceil(globalMax  / axStep) * axStep;
    const axRange = Math.max(globalMax - globalMin, 1e-9);
    const normGlobal = v => xLeft + ((v - globalMin) / axRange) * xRange;

    const nodes = [];
    let yc = mg.t;
    let dataRowIdx = 0;
    rows.forEach((r, idx) => {
      if (r.type === "section") {
        const cols = r.cols || [];
        nodes.push(
          <g key={`s-${idx}`}>
            <text x={xLabelR} y={yc + sectH * 0.7} textAnchor="end"
                  style={{ fontSize: 10, fill: "#646464", fontWeight: 700,
                           letterSpacing: "0.04em" }}>
              {r.label.toUpperCase()}
            </text>
            {cols.map((cl, j) => (
              <text key={j} x={xTermR(slotFor(j, cols.length))}
                    y={yc + sectH * 0.7} textAnchor="end"
                    style={{ fontSize: 9, fill: "#646464" }}>
                {cl}
              </text>
            ))}
            <text x={xSumR} y={yc + sectH * 0.7} textAnchor="end"
                  style={{ fontSize: 9, fill: "#646464", fontWeight: 700 }}>
              {r.sumLabel}
            </text>
          </g>
        );
        yc += sectH + gap;
      } else {
        if (dataRowIdx % 2 === 1) {
          nodes.push(
            <rect key={`bg-${idx}`} x={0} y={yc - gap / 2}
                  width={W} height={dataH + gap}
                  fill="#00003B" fillOpacity="0.03" />
          );
        }
        dataRowIdx++;
        const yMid = yc + dataH / 2;
        const norm = normGlobal;
        const xMinX = norm(r.eMin);
        const xMaxX = norm(r.eMax);
        const xCur = norm(r.eCur);
        const xP25 = norm(r.eP25);
        const xP75 = norm(r.eP75);
        const xMed = norm(r.eMed);
        const color = pctlColor(r.yPctl);
        const terms = r.terms || [];
        nodes.push(
          <g key={`d-${idx}`}>
            <text x={xLabelR} y={yMid + 4} textAnchor="end"
                  style={{ fontVariantNumeric: "tabular-nums" }}>
              <tspan style={{ fontSize: 11, fill: "#00003B", fontWeight: 600 }}>
                {r.label}
              </tspan>
              {r.suffix != null && (
                <tspan dx="4" style={{ fontSize: 10, fill: "#646464" }}>
                  ({r.suffix})
                </tspan>
              )}
            </text>
            {/* Termkolumnerna: första utan tecken, resten med + så att raden
                läses som en summa. */}
            {terms.map((v, j) => (
              <text key={j} x={xTermR(slotFor(j, terms.length))}
                    y={yMid + 4} textAnchor="end"
                    style={{ fontSize: 10, fill: "#646464",
                             fontVariantNumeric: "tabular-nums" }}>
                {v == null ? "—" : j === 0 ? Kv.fmtPct(v, 2) : Kv.fmtPp(v, 2)}
              </text>
            ))}
            <text x={xSumR} y={yMid + 4} textAnchor="end"
                  style={{ fontSize: 11, fill: color, fontWeight: 700,
                           fontVariantNumeric: "tabular-nums" }}>
              {Kv.fmtPct(r.eCur, 2)}
            </text>
            <line x1={xMinX} x2={xMaxX} y1={yMid} y2={yMid}
                  stroke="#E0E5EC" strokeWidth="2" strokeLinecap="round" />
            <line x1={xP25} x2={xP75} y1={yMid} y2={yMid}
                  stroke="#646464" strokeOpacity="0.35" strokeWidth="7"
                  strokeLinecap="round" />
            <line x1={xMed} x2={xMed} y1={yMid - 5} y2={yMid + 5}
                  stroke="#00003B" strokeWidth="1.5" />
            <circle cx={xCur} cy={yMid} r="6"
                    fill={color} stroke="#FAFAFA" strokeWidth="1.5" />
          </g>
        );
        yc += dataH + gap;
      }
    });

    // Fotnoten är tre stycken med samma stil; marginTop ger luften mellan dem.
    const noteStyle = { marginTop: "0.7rem", marginBottom: 0,
                        fontSize: "0.72rem", fontStyle: "italic" };

    // invert = högre värde är sämre (volatilitet), så delta-färgen vänds.
    function ExpRow({ label, sel, strat, invert }) {
      const delta = (sel != null && strat != null) ? sel - strat : null;
      const sig   = delta != null && Math.abs(delta) >= 5e-5;
      const dCls  = !sig ? "" :
                    (invert ? delta < 0 : delta > 0) ? "pos" : "neg";
      return (
        <tr>
          <th>{label}</th>
          <td className="ar">{strat != null ? Kv.fmtPct(strat, 1) : "—"}</td>
          {showComparison && (
            <td className="ar">{sel != null ? Kv.fmtPct(sel, 1) : "—"}</td>
          )}
          {showComparison && (
            <td className={"ar " + dCls}>
              {delta != null ? Kv.fmtPp(delta, 2) : "—"}
            </td>
          )}
        </tr>
      );
    }

    return (
      <div>
        <div style={{ maxWidth: "640px", marginBottom: "0.8rem" }}>
          <table className="summary-table">
            <thead>
              <tr>
                <th></th>
                <th className="ar">Multi-Asset</th>
                {showComparison && <th className="ar">Din portfölj</th>}
                {showComparison && <th className="ar">Differens</th>}
              </tr>
            </thead>
            <tbody>
              <ExpRow label="Förväntad real avkastning"     sel={eRealSel} strat={eRealStrat} />
              <ExpRow label="Förväntad nominell avkastning" sel={eNomSel}  strat={eNomStrat} />
              {hasVol && (
                <ExpRow label="Historisk volatilitet"
                        sel={volSelected} strat={volStrat} invert />
              )}
              {hasVol && (
                <ExpRow label="1 år av 6 sämre än"
                        sel={downSel} strat={downStrat} />
              )}
            </tbody>
          </table>
        </div>

        <div style={{ display: "flex", gap: "0.5rem", flexWrap: "wrap",
                       marginBottom: "0.5rem",
                       maxWidth: "1280px",
                       justifyContent: "flex-end" }}>
          {hasNominal && hasReal && (
            <div className="mini-toggle" role="tablist" aria-label="Räntetyp">
              <button role="tab"
                      className={mode === "real" ? "active" : ""}
                      onClick={() => setMode("real")}>
                Real
              </button>
              <button role="tab"
                      className={mode === "nominal" ? "active" : ""}
                      onClick={() => setMode("nominal")}>
                Nominell
              </button>
            </div>
          )}
          <div className="mini-toggle" role="tablist" aria-label="Historikfönster">
            <button role="tab"
                    className={win === "3y" ? "active" : ""}
                    onClick={() => setWin("3y")}>
              3Y
            </button>
            <button role="tab"
                    className={win === "10y" ? "active" : ""}
                    onClick={() => setWin("10y")}>
              10Y
            </button>
            <button role="tab"
                    className={win === "all" ? "active" : ""}
                    onClick={() => setWin("all")}>
              All
            </button>
          </div>
        </div>

        <div className="chart-wrap" style={{ maxWidth: "1280px" }}>
          <svg viewBox={`0 0 ${W} ${H}`}>
            {nodes}
            {/* X-axel längst ner: tick + label per "snyggt" steg */}
            {(() => {
              const axY = yEnd + 4;  // strax under sista datarad
              const tickStep = 0.02;
              const ticks = [];
              for (let v = globalMin; v <= globalMax + 1e-9; v += tickStep) {
                ticks.push(+v.toFixed(4));
              }
              return (
                <g>
                  <line x1={xLeft} x2={xRight} y1={axY} y2={axY}
                        stroke="#646464" strokeOpacity="0.5" />
                  {ticks.map((t, i) => {
                    const xt = normGlobal(t);
                    return (
                      <g key={i}>
                        <line x1={xt} x2={xt} y1={axY} y2={axY + 3}
                              stroke="#646464" strokeOpacity="0.6" />
                        <text x={xt} y={axY + 12}
                              textAnchor="middle"
                              style={{ fontSize: 9, fill: "#646464",
                                       fontVariantNumeric: "tabular-nums" }}>
                          {Kv.fmtPct(t, 0)}
                        </text>
                      </g>
                    );
                  })}
                </g>
              );
            })()}
            <g transform={`translate(8, ${H - 4})`}>
              <line x1="0" x2="12" y1="0" y2="0" stroke="#E0E5EC" strokeWidth="2" />
              <text x="16" y="3" style={{ fontSize: 9, fill: "#646464" }}>min–max</text>
              <line x1="62" x2="74" y1="0" y2="0" stroke="#646464"
                    strokeOpacity="0.35" strokeWidth="6" />
              <text x="78" y="3" style={{ fontSize: 9, fill: "#646464" }}>p25–p75</text>
              <line x1="124" x2="124" y1="-4" y2="4" stroke="#00003B" strokeWidth="1.5" />
              <text x="128" y="3" style={{ fontSize: 9, fill: "#646464" }}>median</text>
              <circle cx="166" cy="0" r="4" fill="#214BB7" />
              <text x="174" y="3" style={{ fontSize: 9, fill: "#646464" }}>
                nu
              </text>
            </g>
          </svg>
        </div>
        <p className="subtitle" style={noteStyle}>
          Förväntad real avkastning är ett långsiktigt värderingsmått som visar
          avkastningspotentialen vid dagens värderingar, inte en prognos. För
          aktier beräknas den som 1/CAPE plus ett antagande om långsiktig real
          vinsttillväxt (EPS). Antagandena är fasta: USA 2,25 %, Europa 1,00 %,
          Storbritannien 1,00 %, Sverige 1,25 %, Japan 1,00 % och Asien
          exklusive Japan 2,00 %. Det historiska intervallet bygger på den
          historiska fördelningen för 1/CAPE, med samma antagande om real
          vinsttillväxt.
        </p>
        <p className="subtitle" style={noteStyle}>
          För räntor används real femårsränta. Nominell förväntad avkastning
          beräknas genom att lägga till femårig förväntad inflation från
          inflationsswappar. För Sverige används euroområdets inflation eftersom
          den svenska marknaden är illikvid. Varje marknad räknas i sin egen
          valuta, och portföljens summa därmed under antagande om oförändrade
          valutakurser.
        </p>
        <p className="subtitle" style={noteStyle}>
          Volatiliteten är historisk och nominell. ”1 år av 6 sämre än”
          motsvarar förväntad nominell avkastning minus en standardavvikelse.
          Med antagande om normalfördelade utfall är sannolikheten för ett sämre
          år 15,9 %, alltså ungefär ett år av sex. I praktiken förekommer
          extrema utfall oftare än normalfördelningen antyder.
        </p>
      </div>
    );
  };
})();
