import type { SuperbetLiveAdvice } from "@/domain/entities"; import { formatPercent } from "@/presentation/theme"; function OverroundBadge({ overround }: { overround: number }) { const pct = overround * 100; const { cls, icon, tip } = pct < 5 ? { cls: "bg-neon-green/15 text-neon-green border border-neon-green/30", icon: "↓", tip: "Margem baixa — favorável ao apostador", } : pct < 10 ? { cls: "bg-amber-500/15 text-amber-300 border border-amber-500/30", icon: "~", tip: "Margem típica", } : { cls: "bg-red-500/15 text-red-300 border border-red-500/30", icon: "↑", tip: "Margem alta — desfavorável ao apostador", }; return ( {icon} Margem {pct.toFixed(1)}% ); } interface ProbBarProps { label: string; prob: number; color?: "green" | "amber" | "slate" | "blue"; title?: string; } function ProbBar({ label, prob, color = "slate", title }: ProbBarProps) { const barColors: Record = { green: "bg-neon-green/70", amber: "bg-amber-400/70", blue: "bg-sky-400/70", slate: "bg-slate-400/50", }; const textColors: Record = { green: "text-neon-green", amber: "text-amber-300", blue: "text-sky-300", slate: "text-slate-300", }; const widthPct = Math.max(0, Math.min(100, prob * 100)); return (

{label}

{formatPercent(prob)}
); } function h2hColor(key: "1" | "X" | "2", probs: { home: number; draw: number; away: number }): "green" | "amber" | "slate" { const max = Math.max(probs.home, probs.draw, probs.away); if (key === "1" && probs.home === max) return "green"; if (key === "2" && probs.away === max) return "green"; if (key === "X" && probs.draw === max) return "amber"; return "slate"; } interface EdgeMiniCardProps { label: string; labelFull?: string; odd: number | undefined; implied: number | undefined; model: number | undefined; edge: number | undefined; } function EdgeMiniCard({ label, odd, implied, model, edge }: EdgeMiniCardProps) { const edgePp = edge != null ? edge * 100 : null; const edgeColor = edgePp == null ? "text-slate-400" : edgePp > 2 ? "text-neon-green" : edgePp < -2 ? "text-red-400" : "text-slate-300"; return (

{label}

{odd != null ? ( <>

{odd.toFixed(2)} {implied != null && ( ({formatPercent(implied)}) )}

{model != null && edgePp != null && (

Modelo {formatPercent(model)} · edge {edgePp > 0 ? "+" : ""} {edgePp.toFixed(1)} pp

)} {edgePp != null && edgePp > 2 && (

Valor p/ apostador

)} {edgePp != null && edgePp < -2 && (

Casa otimista

)} ) : (

)}
); } interface LiveModelPanelProps { data: SuperbetLiveAdvice; } export function LiveModelPanel({ data }: LiveModelPanelProps) { const s = data.inplaySummary; const probs = { home: s.probFinalHome, draw: s.probFinalDraw, away: s.probFinalAway }; function teamLabel(name: string, maxLen = 14): string { const t = name.trim(); return t.length <= maxLen ? t : `${t.slice(0, maxLen - 1)}…`; } return (
{/* Probabilidades de resultado final */}

Modelo in-play

{(s.over25 != null || s.btts != null) && ( <>
{s.over25 != null && ( )} {s.btts != null && ( )} {s.probNextGoalHome != null && ( )} {s.probNextGoalAway != null && ( )} {s.probNoMoreGoals != null && ( )}
)}
{/* Posição da casa — edge 1X2 */}

Posição da casa

{data.h2hOverround != null && }
{(["1", "X", "2"] as const).map((key) => { const odd = data.h2hOdds[key]; const implied = data.h2hImplied[key]; const bench = data.marketBenchmark?.h2h?.[key]; const label = key === "1" ? teamLabel(data.homeTeam) : key === "2" ? teamLabel(data.awayTeam) : "Empate"; const fullLabel = key === "1" ? data.homeTeam : key === "2" ? data.awayTeam : "Empate"; return ( ); })}
{/* Totais de gols */} {data.marketBenchmark?.totals && Object.keys(data.marketBenchmark.totals).length > 0 && (

Totais de gols

{Object.entries(data.marketBenchmark.totals).map(([line, row]) => { const edgePp = row.edgeOver * 100; return ( Over {line}{" "} 0 ? "text-neon-green" : "text-red-400"}> {edgePp > 0 ? "+" : ""} {edgePp.toFixed(1)} pp ); })}
)} {/* Generosity — prob real da casa (sem margem) */} {Object.keys(data.generosityProbs).length > 0 && (

Probabilidade real da Superbet (sem margem)

{data.generosityProbs.home != null && (
{teamLabel(data.homeTeam, 10)} {formatPercent(data.generosityProbs.home)}
)} {data.generosityProbs.away != null && (
{teamLabel(data.awayTeam, 10)} {formatPercent(data.generosityProbs.away)}
)}

A casa já removeu a margem — esse é o "preço justo" que ela atribui a cada time. Quando nosso modelo diverge muito daqui, há valor.

)} {/* Confiança do modelo */} {data.confidence && (

Confiança nos dados

= 0.7 ? "bg-emerald-500/15 text-emerald-300" : data.confidence.score >= 0.3 ? "bg-amber-500/15 text-amber-300" : "bg-red-500/15 text-red-300" }`} > {data.confidence.label} · {(data.confidence.score * 100).toFixed(0)}%

{data.confidence.reason}

)}
); }