/* SkyOps — Production cockpit (Portfolio home for the asset manager)
   Expected → losses by cause → actual, in MWh and €. Filters, baseline selector,
   deviation alerts, decision ranking, 12-month forecast, drill to inverter → string → zone report. */

const { useState: useStatePC, useMemo: useMemoPC } = React;
const pcMWh = (n) => n >= 10000 ? (n / 1000).toFixed(1) + " GWh" : Math.round(n).toLocaleString("en") + " MWh";
const pcEur = (n) => (n < 0 ? "−" : "") + "€" + (Math.abs(n) >= 1e6 ? (Math.abs(n) / 1e6).toFixed(2) + "M" : Math.round(Math.abs(n) / 1000).toLocaleString("en") + "k");
const pcPct = (n) => (n * 100).toFixed(1) + "%";
const CAUSES = [
  { k: "weather",   label: "Weather vs TMY",     color: "#64748b", ctrl: false, hint: "Clouds/aerosols beyond typical year. Not actionable." },
  { k: "curtail",   label: "Curtailment",        color: "#38bdf8", ctrl: false, hint: "Grid / negative-price curtailment." },
  { k: "soiling",   label: "Soiling",            color: "#c79a4a", ctrl: true,  hint: "Dust, bird droppings. Recover with cleaning." },
  { k: "thermal",   label: "Module faults",      color: "#ef4444", ctrl: true,  hint: "Hot cells, PID, bypass diodes, cracks found by inspection." },
  { k: "stringOff", label: "Strings offline",    color: "#d946ef", ctrl: true,  hint: "Open-circuit strings, blown fuses, connectors." },
  { k: "inverter",  label: "Inverter downtime",  color: "#f97316", ctrl: true,  hint: "Trips, derating, comms loss." },
];

function ProductionCockpit({ go, setActiveSite, setActiveMic, setWsStage }) {
  const PM = window.PM;
  const [country, setCountry] = useStatePC("all");
  const [type, setType] = useStatePC("all");
  const [period, setPeriod] = useStatePC("12m");
  const [baseline, setBaseline] = useStatePC("physical");
  const [unit, setUnit] = useStatePC("eur");
  const [siteId, setSiteId] = useStatePC("all");
  const [drill, setDrill] = useStatePC(null); // inverter id

  const list = useMemoPC(() => PM.models.filter(m => (country === "all" || m.site.country === country) && (type === "all" || m.site.type === type) && (siteId === "all" || m.site.id === siteId)), [country, type, siteId]);
  const T = useMemoPC(() => PM.agg(list), [list]);
  const site1 = siteId !== "all" ? PM.byId[siteId] : null;
  const nMonths = period === "12m" ? 12 : period === "6m" ? 6 : 3;
  const mths = T.months.slice(12 - nMonths);
  const S = useMemoPC(() => { const o = {}; ["expected","actual","weather","soiling","thermal","stringOff","inverter","curtail"].forEach(k => o[k] = mths.reduce((a, x) => a + x[k], 0)); o.price = mths.reduce((a, x) => a + x.price, 0) / mths.length; return o; }, [mths]);
  const baseVal = baseline === "physical" ? S.expected : baseline === "contract" ? S.expected * 0.93 : T.base.peer * nMonths / 12;
  const v = (mwh) => unit === "eur" ? pcEur(mwh * S.price) : pcMWh(mwh);
  const ctrl = S.soiling + S.thermal + S.stringOff + S.inverter;
  const gapToBase = baseVal - S.actual;
  const countries = [...new Set(PM.models.map(m => m.site.country))];
  const types = [...new Set(PM.models.map(m => m.site.type))];

  const openSite = (id) => { setActiveSite(id); go("site"); };
  const openZone = (m, zone) => { try { localStorage.setItem("skyops_zr_zone", zone); } catch {} ; setActiveSite(m.site.id); const mic = (window.INSPECTIONS || []).filter(i => i.siteId === m.site.id && i.status !== "scheduled")[0]; if (mic) setActiveMic(mic.id); setWsStage("report"); go("workspace"); };

  return (
    <div className="pc" data-screen-label="Production cockpit">
      <header className="pc-head">
        <div>
          <div className="kicker">Portfolio · production & loss</div>
          <h1>Where the energy went</h1>
          <div className="sub">{list.length} sites · {T.cap.toFixed(1)} MWp · {PM.MONTHS[12 - nMonths]} → {PM.MONTHS[11]} · SCADA + inspections + meteo + market</div>
        </div>
        <div className="pc-filters">
          <select className="sel" value={siteId} onChange={e => { setSiteId(e.target.value); setDrill(null); }}><option key="all-s" value="all">All sites</option>{PM.models.map(m => <option key={m.site.id} value={m.site.id}>{m.site.name}</option>)}</select>
          <select className="sel" value={country} onChange={e => setCountry(e.target.value)}><option key="all-c" value="all">All countries</option>{countries.map(c => <option key={"c-" + c} value={c}>{c}</option>)}</select>
          <select className="sel" value={type} onChange={e => setType(e.target.value)}><option key="all-t" value="all">All tech</option>{types.map(c => <option key={"t-" + c} value={c}>{c}</option>)}</select>
          <div className="seg">{[["3m","3 mo"],["6m","6 mo"],["12m","12 mo"]].map(([k,l]) => <button key={k} className={period === k ? "on" : ""} onClick={() => setPeriod(k)}>{l}</button>)}</div>
          <div className="seg">{[["eur","€"],["mwh","MWh"]].map(([k,l]) => <button key={k} className={unit === k ? "on" : ""} onClick={() => setUnit(k)}>{l}</button>)}</div>
        </div>
      </header>

      <div className="pc-kpis">
        <Kpi lbl="Produced" val={pcMWh(S.actual)} sub={`${pcEur(S.actual * S.price)} revenue · ${S.price.toFixed(0)} €/MWh blended`}/>
        <Kpi lbl="Performance ratio" val={(PM.PR_DESIGN * S.actual / S.expected).toFixed(3)} sub={`design ${PM.PR_DESIGN} · gap ${((PM.PR_DESIGN - PM.PR_DESIGN * S.actual / S.expected) * 100).toFixed(1)} pp`} tone={S.actual / S.expected < 0.9 ? "warn" : ""}/>
        <Kpi lbl="Availability" val={pcPct(1 - (S.stringOff + S.inverter) / S.expected)} sub="energy-weighted"/>
        <Kpi lbl="Controllable loss" val={v(ctrl)} sub={`${pcPct(ctrl / S.expected)} of expected · recoverable`} tone="crit"/>
        <Kpi lbl="Uncontrollable" val={v(S.weather + S.curtail)} sub="weather + curtailment"/>
        <Kpi lbl="Recoverable in 12 mo" val={pcEur(T.forecastGap * nMonths / 12)} sub="if ranked actions executed" tone="pos"/>
      </div>

      <div className="pc-row">
        <section className="card pc-wf">
          <div className="pc-card-hd">
            <div><b>Loss waterfall</b><span className="muted"> · expected → actual</span></div>
            <div className="seg">{[["physical","Physical model"],["contract","Contract"],["peer","Peer twins"]].map(([k,l]) => <button key={k} className={baseline === k ? "on" : ""} onClick={() => setBaseline(k)} title={k === "physical" ? "Irradiance × capacity × design PR" : k === "contract" ? "PPA / O&M guaranteed yield" : "Median specific yield of same-tech peers"}>{l}</button>)}</div>
          </div>
          <Waterfall S={S} baseVal={baseVal} baseline={baseline} unit={unit} price={S.price}/>
          <div className="pc-wf-foot">
            <span>Gap to <b>{baseline}</b> baseline: <b className="mono" style={{ color: gapToBase > 0 ? "var(--sev-critical)" : "var(--sev-low)" }}>{gapToBase > 0 ? "−" : "+"}{v(Math.abs(gapToBase))}</b></span>
            <span className="muted">Controllable share of gap: <b className="mono">{pcPct(Math.min(1, ctrl / Math.max(1, gapToBase)))}</b></span>
          </div>
        </section>

        <section className="card pc-trend">
          <div className="pc-card-hd"><b>Monthly</b><span className="muted"> · expected vs actual, losses stacked</span></div>
          <TrendChart months={mths}/>
        </section>
      </div>

      <div className="pc-row pc-row-2">
        <section className="card pc-dec">
          <div className="pc-card-hd"><b>Decisions ranked by net value</b><span className="muted"> · next 12 months</span></div>
          <table className="tbl pc-tbl">
            <thead><tr><th>Action</th>{siteId === "all" && <th>Site</th>}<th className="r">Cost</th><th className="r">Gain /yr</th><th className="r">Net</th><th className="r">Payback</th><th>Window</th><th/></tr></thead>
            <tbody>
              {(siteId === "all" ? T.interventions.slice(0, 8) : site1.interventions).map((x, i) => (
                <tr key={i} className={x.k === "wait" ? "pc-wait" : ""}>
                  <td><span className={`pc-act pc-act-${x.k}`}/>{x.label}</td>
                  {siteId === "all" && <td className="sec" onClick={() => openSite(x.site.id)} style={{ cursor: "pointer" }}>{x.site.name}</td>}
                  <td className="r mono num">{x.cost ? pcEur(x.cost) : "—"}</td>
                  <td className="r mono num" style={{ color: "var(--sev-low)" }}>{x.gain ? pcEur(x.gain) : "—"}</td>
                  <td className="r mono num"><b style={{ color: x.gain - x.cost > 0 ? "var(--sev-low)" : "var(--fg-muted)" }}>{x.k === "wait" ? pcEur(-(T.forecastGap)) : pcEur(x.gain - x.cost)}</b></td>
                  <td className="r mono num">{x.payback != null ? x.payback.toFixed(1) + " mo" : "—"}</td>
                  <td className="sec">{x.when}</td>
                  <td className="r">{x.k !== "wait" && <button className="btn btn-secondary btn-xs" onClick={() => window.dispatchEvent(new CustomEvent("skyops:new-wo",{detail:{ title: x.label || x.title || x.name || "", siteId: x.siteId || undefined }}))}>Create WO</button>}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </section>

        <section className="card pc-alerts">
          <div className="pc-card-hd"><b>Deviations</b><span className="muted"> · last 30 days · cause from inspections</span><span className="grow"/><span className="pc-badge">{T.alerts.length}</span></div>
          <div className="pc-alert-list">
            {T.alerts.slice(0, 7).map((a, i) => (
              <div key={i} className={`pc-alert pc-alert-${a.sev}`} onClick={() => { setSiteId(a.site.id); setDrill(a.where); }}>
                <div className="pc-alert-top"><span className="pc-sev">{a.sev}</span><b>{a.site.name} · {a.where}</b><span className="grow"/><span className="mono" style={{ color: "var(--sev-critical)" }}>{a.dev.toFixed(1)}%</span></div>
                <div className="pc-alert-cause">{a.cause}</div>
                <div className="pc-alert-meta"><span>since {a.since}</span><span>{pcEur(a.eurMo)}/mo</span><span className="muted">{a.source}</span></div>
                <div className="pc-alert-act"><Icon name="settings" size={10}/>{a.action}</div>
              </div>
            ))}
            {T.alerts.length === 0 && <div className="pc-empty">No deviations above threshold.</div>}
          </div>
        </section>
      </div>

      <div className="pc-row pc-row-3">
        <section className="card pc-fc">
          <div className="pc-card-hd"><b>12-month forecast</b><span className="muted"> · with vs without the ranked actions</span><span className="grow"/><span className="mono" style={{ color: "var(--sev-low)" }}>+{pcEur(T.forecastGap)} if executed</span></div>
          <ForecastChart f={T.forecast}/>
        </section>

        <section className="card pc-sites">
          <div className="pc-card-hd"><b>{site1 ? `${site1.site.name} · inverters` : "Sites"}</b><span className="muted"> · {site1 ? "click an inverter for strings" : "sorted by controllable loss"}</span>{site1 && <><span className="grow"/><button className="btn btn-ghost btn-xs" onClick={() => { setSiteId("all"); setDrill(null); }}>All sites</button></>}</div>
          {!site1 ? (
            <table className="tbl pc-tbl">
              <thead><tr><th>Site</th><th className="r">MWp</th><th className="r">PR</th><th className="r">Avail.</th><th className="r">Spec. yield</th><th style={{ width: "26%" }}>Loss mix</th><th className="r">Controllable</th><th/></tr></thead>
              <tbody>
                {[...list].sort((a, b) => b.tot.eurLost - a.tot.eurLost).map(m => (
                  <tr key={m.site.id} onClick={() => setSiteId(m.site.id)}>
                    <td><b>{m.site.name}</b> <span className="sec" style={{ fontSize: 10 }}>{m.site.country}</span></td>
                    <td className="r mono num">{m.site.capacity_mw}</td>
                    <td className="r mono num" style={{ color: m.tot.pr < 0.76 ? "var(--sev-high)" : undefined }}>{m.tot.pr.toFixed(3)}</td>
                    <td className="r mono num">{pcPct(m.tot.avail)}</td>
                    <td className="r mono num">{Math.round(m.tot.specific)} <span className="sec" style={{ fontSize: 10 }}>MWh/MWp</span></td>
                    <td><LossMix t={m.tot}/></td>
                    <td className="r mono num" style={{ color: "var(--sev-critical)" }}>{pcEur(m.tot.eurLost)}</td>
                    <td className="r"><Icon name="arrow_right" size={12} style={{ color: "var(--fg-muted)" }}/></td>
                  </tr>
                ))}
              </tbody>
            </table>
          ) : (
            <InverterDrill m={site1} drill={drill} setDrill={setDrill} openZone={openZone}/>
          )}
        </section>
      </div>
    </div>
  );
}

function Kpi({ lbl, val, sub, tone }) {
  return <div className={`pc-kpi ${tone || ""}`}><span className="caps">{lbl}</span><b className="mono">{val}</b><em>{sub}</em></div>;
}

function LossMix({ t }) {
  const tot = t.expected - t.actual || 1;
  return <div className="pc-mix" title="weather · curtail · soiling · faults · strings · inverter">{CAUSES.map(c => <i key={c.k} style={{ width: `${t[c.k] / tot * 100}%`, background: c.color }}/>)}</div>;
}

function Waterfall({ S, baseVal, baseline, unit, price }) {
  const steps = [{ k: "start", label: baseline === "physical" ? "Expected (physical)" : baseline === "contract" ? "Contract yield" : "Peer-twin yield", v: baseVal, abs: true }];
  if (baseline !== "physical") steps.push({ k: "adj", label: "Model vs baseline", v: S.expected - baseVal, color: "#475569" });
  CAUSES.forEach(c => steps.push({ k: c.k, label: c.label, v: -S[c.k], color: c.color, ctrl: c.ctrl, hint: c.hint }));
  steps.push({ k: "end", label: "Actual", v: S.actual, abs: true });
  const max = Math.max(baseVal, S.expected) * 1.04;
  const W = 760, H = 250, x0 = 10, bw = (W - 20) / steps.length, scale = (H - 60) / max;
  let run = 0;
  const fmt = (n) => unit === "eur" ? pcEur(n * price) : pcMWh(n);
  return (
    <div className="pc-wfc">
      <svg viewBox={`0 0 ${W} ${H}`} className="pc-wf-svg">
        {steps.map((s, i) => {
          let y, h;
          if (s.abs) { run = s.v; y = H - 40 - s.v * scale; h = s.v * scale; }
          else { const top = run, bot = run + s.v; run = bot; y = H - 40 - Math.max(top, bot) * scale; h = Math.abs(s.v) * scale; }
          const x = x0 + i * bw + bw * 0.14, w = bw * 0.72;
          const fill = s.abs ? (s.k === "start" ? "#334155" : "#4a90d9") : s.color;
          return (
            <g key={s.k}>
              {i > 0 && !s.abs && <line x1={x - bw * 0.14} x2={x} y1={H - 40 - (s.v < 0 ? (run - s.v) : run) * scale} y2={H - 40 - (s.v < 0 ? (run - s.v) : run) * scale} stroke="var(--border-strong)" strokeDasharray="2 2"/>}
              <rect x={x} y={y} width={w} height={Math.max(1.5, h)} rx="2" fill={fill} opacity={s.ctrl === false ? 0.55 : 1}>
                <title>{s.label}: {fmt(Math.abs(s.v))}{s.hint ? " — " + s.hint : ""}</title>
              </rect>
              {s.ctrl && <rect x={x} y={y - 4} width={w} height="2" fill="#fff" opacity=".7"/>}
              <text x={x + w / 2} y={y - 7} textAnchor="middle" fontSize="10" fontFamily="var(--font-mono)" fill={s.abs ? "var(--fg-primary)" : s.color} fontWeight={s.abs ? 700 : 500}>{s.abs ? fmt(s.v) : "−" + fmt(Math.abs(s.v))}</text>
              <text x={x + w / 2} y={H - 24} textAnchor="middle" fontSize="9.5" fill="var(--fg-secondary)">{s.label.split(" ")[0]}</text>
              <text x={x + w / 2} y={H - 12} textAnchor="middle" fontSize="9.5" fill="var(--fg-muted)">{s.label.split(" ").slice(1).join(" ")}</text>
            </g>
          );
        })}
      </svg>
      <div className="pc-wf-lg"><span><i className="ctrl"/>white tick = controllable (actionable)</span><span><i style={{ background: "#64748b", opacity: .55 }}/>uncontrollable</span></div>
    </div>
  );
}

function TrendChart({ months }) {
  const W = 520, H = 250, x0 = 46, max = Math.max(...months.map(m => m.expected)) * 1.05;
  const bw = (W - x0 - 10) / months.length, sc = (H - 50) / max;
  const [hov, setHov] = useStatePC(null);
  return (
    <div className="pc-trc">
      <svg viewBox={`0 0 ${W} ${H}`} className="pc-tr-svg">
        {[0.25, 0.5, 0.75, 1].map(p => <g key={p}><line x1={x0} x2={W - 10} y1={H - 30 - max * p * sc} y2={H - 30 - max * p * sc} stroke="var(--border-base)" strokeDasharray="2 3"/><text x={x0 - 6} y={H - 27 - max * p * sc} textAnchor="end" fontSize="9" fill="var(--fg-muted)" fontFamily="var(--font-mono)">{(max * p / 1000).toFixed(1)}</text></g>)}
        <text x={x0 - 6} y={12} textAnchor="end" fontSize="9" fill="var(--fg-muted)" fontFamily="var(--font-mono)">GWh</text>
        {months.map((m, i) => {
          const x = x0 + i * bw + bw * 0.15, w = bw * 0.7;
          let y = H - 30 - m.actual * sc;
          const segs = [<rect key="a" x={x} y={y} width={w} height={m.actual * sc} fill="#4a90d9" rx="1.5"/>];
          CAUSES.forEach(c => { const h = m[c.k] * sc; y -= h; segs.push(<rect key={c.k} x={x} y={y} width={w} height={h} fill={c.color} opacity={c.ctrl ? .95 : .5}/>); });
          return <g key={m.m} onMouseEnter={() => setHov(m)} onMouseLeave={() => setHov(null)}>{segs}<line x1={x - 2} x2={x + w + 2} y1={H - 30 - m.expected * sc} y2={H - 30 - m.expected * sc} stroke="#fff" strokeWidth="1.2"/><text x={x + w / 2} y={H - 14} textAnchor="middle" fontSize="9" fill="var(--fg-muted)" fontFamily="var(--font-mono)">{m.m.split(" ")[0]}</text></g>;
        })}
      </svg>
      <div className="pc-tr-lg">
        <span><i style={{ background: "#4a90d9" }}/>Actual</span>{CAUSES.map(c => <span key={c.k}><i style={{ background: c.color, opacity: c.ctrl ? 1 : .5 }}/>{c.label}</span>)}<span><i className="exp"/>Expected</span>
      </div>
      {hov && <div className="pc-tr-hov mono">{hov.m}: actual {pcMWh(hov.actual)} · expected {pcMWh(hov.expected)} · controllable loss {pcMWh(hov.soiling + hov.thermal + hov.stringOff + hov.inverter)} · {hov.price.toFixed(0)} €/MWh</div>}
    </div>
  );
}

function ForecastChart({ f }) {
  const W = 620, H = 220, x0 = 46, max = Math.max(...f.map(x => x.expected)) * 1.05, sc = (H - 50) / max;
  const X = i => x0 + i * ((W - x0 - 14) / (f.length - 1)), Y = v => H - 30 - v * sc;
  const line = (k) => f.map((x, i) => `${X(i)},${Y(x[k])}`).join(" ");
  const band = f.map((x, i) => `${X(i)},${Y(x.withAct)}`).join(" ") + " " + [...f].reverse().map((x, i) => `${X(f.length - 1 - i)},${Y(x.noAct)}`).join(" ");
  return (
    <div className="pc-fcc">
      <svg viewBox={`0 0 ${W} ${H}`} className="pc-fc-svg">
        {[0.5, 0.75, 1].map(p => <line key={p} x1={x0} x2={W - 14} y1={Y(max * p)} y2={Y(max * p)} stroke="var(--border-base)" strokeDasharray="2 3"/>)}
        <polygon points={band} fill="color-mix(in oklab, var(--sev-low) 22%, transparent)"/>
        <polyline points={line("expected")} fill="none" stroke="#fff" strokeWidth="1" strokeDasharray="3 3" opacity=".6"/>
        <polyline points={line("noAct")} fill="none" stroke="var(--sev-critical)" strokeWidth="2"/>
        <polyline points={line("withAct")} fill="none" stroke="var(--sev-low)" strokeWidth="2.2"/>
        {f.map((x, i) => <text key={i} x={X(i)} y={H - 12} textAnchor="middle" fontSize="9" fill="var(--fg-muted)" fontFamily="var(--font-mono)">{x.m.split(" ")[0]}</text>)}
      </svg>
      <div className="pc-tr-lg"><span><i className="exp"/>Expected</span><span><i style={{ background: "var(--sev-low)" }}/>With ranked actions</span><span><i style={{ background: "var(--sev-critical)" }}/>Do nothing (degrading)</span><span><i style={{ background: "color-mix(in oklab, var(--sev-low) 40%, transparent)" }}/>Recoverable</span></div>
    </div>
  );
}

function InverterDrill({ m, drill, setDrill, openZone }) {
  const inv = m.inverters.find(i => i.id === drill);
  return (
    <div className="pc-drill">
      <div className="pc-inv-grid">
        {m.inverters.map(i => (
          <button key={i.id} className={`pc-inv ${drill === i.id ? "on" : ""} ${i.loss > 0.08 ? "crit" : i.loss > 0.04 ? "high" : i.loss > 0.025 ? "med" : ""}`} onClick={() => setDrill(drill === i.id ? null : i.id)}>
            <span className="mono pc-inv-id">{i.id}</span>
            <b className="mono">{(-i.loss * 100).toFixed(1)}%</b>
            <span className="pc-inv-bar"><i style={{ width: `${Math.min(100, i.loss * 500)}%` }}/></span>
            <em>{i.cause}</em>
          </button>
        ))}
      </div>
      {inv && (
        <div className="pc-strings">
          <div className="pc-strings-hd"><b>{inv.id}</b><span className="muted"> · {inv.cap.toFixed(1)} MW · {pcEur(inv.eur)}/yr lost · {inv.cause}</span></div>
          <div className="pc-str-grid">
            {inv.strings.map(s => (
              <button key={s.id} className={`pc-str ${s.cause}`} onClick={() => openZone(m, s.zone)} title={`${s.id} · ${s.cause || "nominal"} · zone ${s.zone} → open zone report`}>
                <span className="mono">{s.id}</span><b className="mono">{s.loss >= 1 ? "OFF" : (-s.loss * 100).toFixed(0) + "%"}</b><em>{s.cause || "nominal"}</em><span className="pc-str-z mono">zone {s.zone} ↗</span>
              </button>
            ))}
          </div>
          <div className="pc-strings-ft muted">Click a string to open the module-level zone report that explains the loss.</div>
        </div>
      )}
    </div>
  );
}

window.ProductionCockpit = ProductionCockpit;
