import React from "react";
import { Zap, ExternalLink } from "lucide-react";
import { CHIP_CONFIG } from "../utils/fplLogic";
export const SolverOutputPanel = ({
pendingSolutions, setPendingSolutions, isSolving, globalPlayers, applySolution, appliedPlanSummary, setAppliedPlanSummary, baselineEv = 0,comprehensiveSettings
}) => {
// BULLETPROOF RELATIVE EV
const getRelativeEv = (sol) => {
if (baselineEv === undefined || !sol) return "+0.00";
const base = sol.lockedBaselineEv !== undefined ? sol.lockedBaselineEv : baselineEv;
if (typeof sol === "number") {
const diff = sol - base;
return diff >= 0 ? `+${diff.toFixed(2)}` : diff.toFixed(2);
}
if (!sol.plan || !Array.isArray(sol.plan) || sol.plan.length === 0) {
const fallbackEv = sol.ev !== undefined ? sol.ev : 0;
const diff = fallbackEv - base;
return diff >= 0 ? `+${diff.toFixed(2)}` : diff.toFixed(2);
}
let pathEV = 0;
let hasValidGw = false;
sol.plan.forEach(gwPlan => {
const gw = gwPlan.gw;
if (gw === undefined) return;
hasValidGw = true;
const gwChip = gwPlan.chip;
const gwCapMult = gwChip === "tc" ? 3 : 2;
let gwPts = 0;
const getPlayer = (id) => globalPlayers.find(p => String(p.ID) === String(id));
(gwPlan.lineup || []).forEach(id => {
const p = getPlayer(id);
if (p && !p.isBlank) {
const pts = Number(p[`${gw}_Pts`]) || 0;
gwPts += pts * (String(p.ID) === String(gwPlan.captain) ? gwCapMult : 1);
}
});
let ofIdx = 0;
(gwPlan.bench || []).forEach(id => {
const p = getPlayer(id);
if (p && !p.isBlank) {
const pts = Number(p[`${gw}_Pts`]) || 0;
if (gwChip === "bb") {
gwPts += pts;
} else {
// THE FIX: Output Panel now perfectly matches Python and the main UI!
const rawBw = comprehensiveSettings?.bench_weights || { 0: 0.03, 1: 0.21, 2: 0.06, 3: 0.002 };
const gkWeight = Number(rawBw[0] || 0.03);
const outWeights = [Number(rawBw[1] || 0.21), Number(rawBw[2] || 0.06), Number(rawBw[3] || 0.002)];
if (p.Pos === "G") {
gwPts += pts * gkWeight;
} else {
gwPts += pts * (outWeights[ofIdx] || 0.02);
ofIdx++;
}
}
}
});
pathEV += gwPts - (gwPlan.hits || 0) * 4;
});
if (!hasValidGw || Number.isNaN(pathEV)) {
const fallbackEv = sol.ev !== undefined ? sol.ev : 0;
const diff = fallbackEv - base;
return diff >= 0 ? `+${diff.toFixed(2)}` : diff.toFixed(2);
}
const diff = pathEV - base;
return diff >= 0 ? `+${diff.toFixed(2)}` : diff.toFixed(2);
};
return (
{/* Left Side: Original Title & Description */}
Solver output
Nothing changes your squad until you apply a path.
{/* Right Side: Sleek, Minimalist Credit */}
{/* Right Side: Sleek, Minimalist Credit */}
Credit
Sertalp-Moose Solver
{pendingSolutions.length > 0 && !isSolving && (
Optimal Paths Found
{pendingSolutions.map((sol, index) => (
ITERATION {sol.id || index + 1}
{sol.chips_used && Object.entries(sol.chips_used).map(([gw, chip]) => {
const cfg = CHIP_CONFIG[chip];
return cfg ? {cfg.short}{gw} : null;
})}
{getRelativeEv(sol)} pts
{sol.objective_score != null && eval: {sol.objective_score.toFixed(2)}}
{sol.plan.map((gwPlan) => (
(gwPlan.transfers_in.length > 0 || gwPlan.transfers_out.length > 0) && (
GW {gwPlan.gw}
{gwPlan.chip && CHIP_CONFIG[gwPlan.chip] && {CHIP_CONFIG[gwPlan.chip].short}{gwPlan.gw}}
ITB: £{Math.abs(gwPlan.itb) < 0.05 ? "0.0" : Number(gwPlan.itb).toFixed(1)}
FT Spend: {gwPlan.chip === "wc" || gwPlan.chip === "fh" ? `${gwPlan.transfers_out?.length || 0}/∞` : `${gwPlan.transfers_out?.length || 0}/${gwPlan.ft_at_start ?? 1}${gwPlan.hits > 0 ? ` (-${gwPlan.hits * 4})` : ""}`}
{gwPlan.chip === "wc" || gwPlan.chip === "fh" ?
{gwPlan.chip === "wc" ? "Wildcard active — unlimited free transfers" : "Free Hit active — squad reverts after the FH"}
: null}
{gwPlan.transfers_out.map((id, i) => (
{globalPlayers.find((p) => String(p.ID) === String(id))?.Name || id}
»
{globalPlayers.find((p) => String(p.ID) === String(gwPlan.transfers_in[i]))?.Name || gwPlan.transfers_in[i]}
))}
)
))}
))}
)}
{!isSolving && pendingSolutions.length === 0 && (
appliedPlanSummary ? (
Last Applied · {appliedPlanSummary.horizon}
{getRelativeEv(appliedPlanSummary)} pts {appliedPlanSummary.objectiveScore != null && ` · eval ${appliedPlanSummary.objectiveScore.toFixed(2)}`}
{appliedPlanSummary.transfers.map((t, i) => (
GW {t.gw}
{t.chip && CHIP_CONFIG[t.chip] && {CHIP_CONFIG[t.chip].short}{t.gw}}
FT Spend: {t.chip === "wc" || t.chip === "fh" ? `${t.outs?.length || 0}/∞` : `${t.outs?.length || 0}/${t.ft_at_start ?? 1}${t.hits > 0 ? ` (-${t.hits * 4})` : ""}`}
£{Math.abs(t.itb) < 0.05 ? "0.0" : Number(t.itb).toFixed(1)}m
{t.outs.length === 0 && t.ins.length === 0 ? (
{t.chip ? `${CHIP_CONFIG[t.chip]?.label || t.chip} active` : 'Hold — no transfers'}
) : (
t.outs.map((name, j) => (
{name}
»
{t.ins[j] || "?"}
))
)}
))}
) : (
Configure settings and hit Solve in the left panel.
)
)}
);
};