<?php
declare(strict_types=1);

/**
 * Bewertungsmethode: Strategische Profile (Starter + Premium-Flags + Guardrails)
 * Datei: bewertung_strategic-profiles.php
 * Submodes: high_performer | solide | ertragreich
 *
 * Macht die API-Calls (fmp_multi) PRO SYMBOL.
 * Premium-Flags werden berechnet, wenn Daten verfügbar sind; sonst bleiben sie null.
 * Guardrails filtern illiquide/fragile Titel & Loss-Maker je nach Profil.
 */

if (!function_exists('fmp_multi')) {
	require_once __DIR__ . '/../api-handling/fmp_client.php';
}

/** ---------- Helper ---------- */
function sp_ramp(?float $v, float $low, float $high, bool $clamp=true): ?float {
	if (!is_numeric($v)) return null;
	if ($clamp) {
		if ($v <= $low) return 0.0;
		if ($v >= $high) return 1.0;
	}
	return ($v - $low) / max(1e-9, ($high - $low));
}
function sp_inv_ramp(?float $v, float $low, float $high): ?float {
	if (!is_numeric($v)) return null;
	if ($v <= $low) return 1.0;
	if ($v >= $high) return 0.0;
	return 1.0 - (($v - $low) / max(1e-9, ($high - $low)));
}
function sp_add(?float $frac, float $weight, float &$score, float &$total, array &$why, string $label): void {
	if ($frac === null) return;
	$total += $weight;
	$score += max(0.0, min(1.0, $frac)) * $weight;
	if ($frac >= 0.8) $why[] = $label;
}
function sp_sma(array $closes, int $n): ?float {
	if (count($closes) < $n) return null;
	$sum = 0.0; $c=0;
	for ($i=count($closes)-$n; $i<count($closes); $i++) {
		$v = $closes[$i] ?? null;
		if (!is_numeric($v)) return null;
		$sum += (float)$v; $c++;
	}
	return $c === $n ? ($sum / $n) : null;
}
function sp_extract_closes(array $hist): array {
	$rows = $hist['historical'] ?? $hist;
	if (!is_array($rows)) return [];
	usort($rows, fn($a,$b)=>strcmp($a['date'] ?? '', $b['date'] ?? ''));
	$out = [];
	foreach ($rows as $r) {
		$out[] = (float)($r['close'] ?? $r['adjClose'] ?? $r['Close'] ?? 0);
	}
	return array_values(array_filter($out, fn($v)=>$v>0));
}
function sp_extract_volumes(array $hist): array {
	$rows = $hist['historical'] ?? $hist;
	if (!is_array($rows)) return [];
	usort($rows, fn($a,$b)=>strcmp($a['date'] ?? '', $b['date'] ?? ''));
	$out = [];
	foreach ($rows as $r) {
		$out[] = (float)($r['volume'] ?? $r['Volume'] ?? 0);
	}
	return array_values(array_filter($out, fn($v)=>$v>0));
}
function sp_pct(?float $a, ?float $b): ?float {
	if (!is_numeric($a) || !is_numeric($b) || $b == 0.0) return null;
	return ($a/$b - 1.0) * 100.0;
}
function sp_avg(array $arr, int $n): ?float {
	if (count($arr) < $n) return null;
	$chunk = array_slice($arr, -$n);
	$sum = 0.0;
	foreach ($chunk as $v) { if (!is_numeric($v)) return null; $sum += (float)$v; }
	return $sum / $n;
}

// --- Tuning-Config: ---
$SP_WEIGHTS = [
  'hp' => [ // High-Performer
	'rev'  => 20,
	'eps'  => 20,
	'gm'   => 10,
	'debt' => 10,
	'trend'=> 15,
	'mom'  => 15,
	'peg'  => 10,
  ],
  'sol' => [ // Solide
	'roic'     => 20,
	'roic_top' => 5,
	'margin'   => 15,
	'fcf'      => 15,
	'debt'     => 15,
	'dte'      => 10,
	'liq'      => 10,
	'beta'     => 10,
	'div'      => 15,
  ],
  'erg' => [ // Ertragreich
	'pe'      => 15,
	'ev_ebit' => 15,
	'ev_sales'=> 10,
	'eps'     => 20,
	'fcf'     => 15,
	'peg'     => 12,
	'debt'    => 10,
	'divQ'    => 18,
	'bonus_safe_leverage' => 5,
	'bonus_strong_fcf'    => 5,
  ],
];

/** ---------- API-Load pro Symbol ---------- */
function bewertung_strategic_profiles_fetch(string $symbol, array $opts = []): array {
	$symbol = strtoupper(trim($symbol));
	$from = (new DateTimeImmutable('now'))->sub(new DateInterval('P540D'))->format('Y-m-d');

	$wantPremium = (bool)($opts['premium'] ?? ($_POST['premium_flags'] ?? false));

	$req = [
		'profile' => ['key'=>'profile','vars'=>['symbol'=>$symbol]],
		'km_ttm'  => ['key'=>'key_metrics_ttm','vars'=>['symbol'=>$symbol]],
		'rt_ttm'  => ['key'=>'ratios_ttm','vars'=>['symbol'=>$symbol]],
		'growth'  => ['key'=>'financial_growth','vars'=>['symbol'=>$symbol]],
		'hist'    => ['key'=>'historical_price_full','vars'=>['symbol'=>$symbol],'query'=>['serietype'=>'line','from'=>$from]],
		'quote'   => ['key'=>'quote','vars'=>['symbol'=>$symbol]],
		'divhist' => ['key'=>'historical_stock_dividend','vars'=>['symbol'=>$symbol], 'query'=>['limit'=>120]],
	];

	if ($wantPremium) {
		$req += [
			'estimates' => ['key'=>'analyst_estimates','query'=>['symbol'=>$symbol,'period'=>'annual']],
			'earnings'  => ['key'=>'earnings','vars'=>['symbol'=>$symbol]], // surprise history (stable endpoint per your registry)
			'holders'   => ['key'=>'institutional_holders','vars'=>['symbol'=>$symbol]],
			'insiders'  => ['key'=>'insider_trading','query'=>['symbol'=>$symbol]],
			'shares'    => ['key'=>'shares_outstanding','vars'=>['symbol'=>$symbol]],
			'divhist'   => ['key'=>'historical_stock_dividend','vars'=>['symbol'=>$symbol]],
			'ev_values' => ['key'=>'enterprise_values','vars'=>['symbol'=>$symbol],'query'=>['limit'=>2]],
		];
	}

	$raw = fmp_multi($req, 12);

	// robust extrahieren
	$out = [
		'profile' => is_array($raw['profile'] ?? null) ? (($raw['profile'][0] ?? []) ?: $raw['profile']) : [],
		'km'      => is_array($raw['km_ttm'] ?? null)  ? (($raw['km_ttm'][0] ?? [])  ?: $raw['km_ttm']) : [],
		'rt'      => is_array($raw['rt_ttm'] ?? null)  ? (($raw['rt_ttm'][0] ?? [])  ?: $raw['rt_ttm']) : [],
		'growth'  => is_array($raw['growth'] ?? null)  ? ($raw['growth'][0] ?? $raw['growth']) : [],
		'hist'    => $raw['hist']  ?? [],
		'quote'   => (is_array($raw['quote'] ?? null) && isset(($raw['quote'])[0])) ? $raw['quote'][0] : ($raw['quote'] ?? []),
		'divhist' => $raw['divhist'] ?? null,
	];

	if ($wantPremium) {
		$out['estimates'] = $raw['estimates'] ?? null;
		$out['earnings']  = $raw['earnings'] ?? null;
		$out['holders']   = $raw['holders'] ?? null;
		$out['insiders']  = $raw['insiders'] ?? null;
		$out['divhist']   = $raw['divhist'] ?? null;
		$out['ev_values'] = $raw['ev_values'] ?? null;
	}

	return $out;
}

/** ---------- Scoring + Premium-Flags + Guardrails ---------- */
function bewertung_strategic_profiles_score(string $symbol, string $mode = 'high_performer', array $opts = []): array {
	$mode = in_array($mode, ['high_performer','solide','ertragreich'], true) ? $mode : 'high_performer';
	$wantPremium = (bool)($opts['premium'] ?? ($_POST['premium_flags'] ?? false));
	
	global $SP_WEIGHTS;
	$W = $SP_WEIGHTS;

	$d = bewertung_strategic_profiles_fetch($symbol, ['premium'=>$wantPremium]);

	$profile = $d['profile'] ?? [];
	$km      = $d['km'] ?? [];
	$rt      = $d['rt'] ?? [];
	$fg      = $d['growth'] ?? [];
	$hist    = $d['hist'] ?? [];
	$quote   = $d['quote'] ?? [];

	$name     = $profile['companyName']       ?? ($profile['company-name'] ?? ($profile['company'] ?? $symbol));
	$exchange = $profile['exchangeShortName'] ?? ($profile['exchange'] ?? '');
	$sector   = $profile['sector'] ?? '';
	$price    = $profile['price']  ?? ($quote['price'] ?? null);
	$mcap     = $profile['mktCap'] ?? $quote['mktCap'] ?? null;

	$closes = sp_extract_closes($hist);
	$vols   = sp_extract_volumes($hist);
	$sma50  = sp_sma($closes, 50);
	$sma200 = sp_sma($closes, 200);
	$mom12m = null;
	if (count($closes) >= 252) {
		$last = end($closes);
		$ago  = $closes[count($closes)-252];
		if ($ago > 0) $mom12m = ($last/$ago - 1.0) * 100.0;
	}

	// === Guardrail 0: Universum-Filter ===
	$adv20 = null;
	if ($closes && $vols) {
		$avgVol20 = sp_avg($vols, 20);
		$avgPx20  = sp_avg($closes, 20) ?? $price;
		if (is_numeric($avgVol20) && is_numeric($avgPx20)) $adv20 = $avgVol20 * $avgPx20;
	} elseif (isset($quote['avgVolume']) && is_numeric($quote['avgVolume']) && is_numeric($price)) {
		$adv20 = (float)$quote['avgVolume'] * (float)$price; // fallback
	}
	$universeFails = [];
	if (is_numeric($price) && $price < 5)                     $universeFails[] = 'Filter: Preis < 5 USD';
	if (is_numeric($adv20) && $adv20 < 1_000_000)             $universeFails[] = 'Filter: Ø-Dollarvolumen 20T < 1 Mio';
	if (is_numeric($mcap)  && $mcap  < 300_000_000)           $universeFails[] = 'Filter: Marktkap < 300 Mio';

	if (!empty($universeFails)) {
		return [
			'percent'  => 0.0,
			'name'     => $name ?: $symbol,
			'exchange' => $exchange ?: '',
			'sector'   => $sector ?: null,
			'why'      => $universeFails,
			'premium'  => null,
		];
	}

	$why=[]; $score=0.0; $total=0.0;

	switch ($mode) {
		case 'solide':
			// --- Eingangsmetriken ---
			$roic 		 = $km['roicTTM'] ?? ($rt['returnOnInvestedCapitalTTM'] ?? null);
			$netMargin   = $rt['netProfitMarginTTM'] ?? null;
			$fcfYield    = $rt['freeCashFlowYieldTTM'] ?? null;
			$debtEbitda  = $rt['netDebtToEBITDATTM'] ?? null;
			$interestCov = $km['interestCoverageTTM'] ?? ($rt['interestCoverageTTM'] ?? null);
			$currentRat  = $rt['currentRatioTTM'] ?? null;
			$beta        = $profile['beta'] ?? null;
			$payout      = $rt['payoutRatioTTM'] ?? null;
			$divYield    = $rt['dividendYieldTTM'] ?? null;
			$dte         = $rt['debtToEquityTTM'] ?? null;
			
			// ---------- Guard (smart) ----------
			// Nur als "kritisch" werten, wenn Zinsdeckung messbar niedrig UND weitere Hebel-/Liquiditätsrisiken vorhanden.
			$guard_fail = false;
			
			if (is_numeric($interestCov) && $interestCov > 0 && $interestCov < 5) {
				if ( (is_numeric($debtEbitda) && $debtEbitda > 3.0) ||
					(is_numeric($currentRat) && $currentRat < 1.0) ||
					(is_numeric($dte) && $dte > 2.0) ) {
					$guard_fail = true;
					$why[] = 'Guard: Interest Coverage < 5';
				}
			} elseif (!is_numeric($interestCov) || $interestCov <= 0) {
				// 0 / null oft bei Netto-Cash (keine Zinsen) -> nur failen, wenn andere harte Risiken gleichzeitig da sind
				if ( (is_numeric($debtEbitda) && $debtEbitda > 3.0) ||
					(is_numeric($currentRat) && $currentRat < 1.0) ||
					(is_numeric($dte) && $dte > 2.0) ) {
					$guard_fail = true;
					$why[] = 'Guard: Zinsdeckung n/a + andere Risiken';
				}
			}
			
			// KEIN hartes return mehr – wir scoren weiter; bei guard_fail dämpfen wir die Maximalwertung am Ende.
			
			// ---------- Kern-Scoring ----------
			if (is_numeric($roic)) {
			  sp_add(sp_ramp($roic, 0.08, 0.15), $W['sol']['roic'], $score, $total, $why, 'ROIC solide');
			
			  // Bonus für echte Top-Quality (>=15%)
			  if ($roic >= 0.15 && !empty($W['sol']['roic_top'])) {
				sp_add(1.0, $W['sol']['roic_top'], $score, $total, $why, 'Top-ROIC Bonus');
			  }
			}
			
			sp_add(sp_ramp($netMargin, 0.05, 0.15),  $W['sol']['margin'], $score,$total,$why,'NetMargin stabil');
			sp_add(sp_ramp($fcfYield,  0.00, 0.05),  $W['sol']['fcf'],    $score,$total,$why,'FCF positiv');
			
			if ($debtEbitda !== null) {
			  sp_add(sp_inv_ramp($debtEbitda, 1.0, 3.0), $W['sol']['debt'], $score,$total,$why,'Debt/EBITDA moderat');
			
			  // kleiner Bonus für konservative Verschuldung
			  if (is_numeric($debtEbitda) && $debtEbitda <= 1.0 && !empty($W['sol']['bonus_low_leverage'])) {
				sp_add(1.0, $W['sol']['bonus_low_leverage'], $score,$total,$why,'Konservative Verschuldung');
			  }
			} else {
			  if ($dte !== null) sp_add(sp_inv_ramp($dte, 0.5, 1.5), $W['sol']['dte'], $score,$total,$why,'Debt/Equity moderat');
			}
			
			sp_add(sp_ramp($currentRat, 1.2, 2.0), $W['sol']['liq'],  $score,$total,$why,'Liquidität ok');
			if ($beta !== null)                    sp_add(sp_inv_ramp($beta, 0.9, 1.3), $W['sol']['beta'], $score,$total,$why,'Beta ≤ Markt');
			
			if ($divYield !== null && $payout !== null) {
			  $divScore = min(
				1.0,
				max(0.0, sp_ramp($divYield, 0.01, 0.03) ?? 0) * 0.6
				+ (sp_inv_ramp($payout, 0.3, 0.7) ?? 0) * 0.4
			  );
			  sp_add($divScore, $W['sol']['div'], $score,$total,$why,'Dividende nachhaltig');
			}
			
			// ---------- Guard-Dämpfung ----------
			// Wenn Guard triggert: Deckel auf Gesamt-Score (z. B. max 40/100)
			if ($guard_fail && $total > 0) {
				$rawPercent = ($score / $total) * 100.0;
				$capped     = min($rawPercent, 40.0);
				// wir "simulieren" den Deckel, indem wir total so anheben, dass Prozent = capped wird
				if ($rawPercent > 0) {
					$factor = $capped / $rawPercent; // < 1
					$score  = $score * $factor;
				}
				$why[] = 'Guard aktiv: Score gedeckelt';
			}
			break;

		case 'ertragreich':
		  // --- Bewertungsinputs ---
		  $pe         = $km['peRatioTTM'] ?? ($rt['priceEarningsRatioTTM'] ?? null);
		  $ev_ebitda  = $km['enterpriseValueOverEBITDATTM'] ?? ($rt['enterpriseValueOverEBITDATTM'] ?? null);
		  $ev_sales   = $km['enterpriseValueOverRevenueTTM'] ?? ($rt['enterpriseValueOverRevenueTTM'] ?? null);
		  $epsCagr    = $fg['epsgrowth'] ?? ($fg['epsGrowth'] ?? null);
		  $fcfYield   = $rt['freeCashFlowYieldTTM'] ?? null;
		  $peg        = $km['pegRatioTTM'] ?? ($km['pegRatio'] ?? ($rt['priceEarningsToGrowthRatioTTM'] ?? null));
		  $debtEbitda = $rt['netDebtToEBITDATTM'] ?? null;
		
		  // --- Dividenden-Qualität (Yield/Payout + Fallbacks + optional Historie) ---
		  $divYield = $rt['dividendYieldTTM'] ?? null;   // Dezimal (0.02 = 2%)
		  $payout   = $rt['payoutRatioTTM']   ?? null;   // Dezimal (0.45 = 45%)
		  $divQual  = null;
		
		  // Fallbacks (ohne Premium)
		  if (!is_numeric($divYield) || $divYield <= 0) {
			$divYield = $quote['yield'] ?? $quote['dividendYield'] ?? $divYield;
		  }
		  if (!is_numeric($divYield) || $divYield <= 0) {
			$lastDiv = $profile['lastDiv'] ?? null; // oft Quartalsdividende
			$px      = $price ?? null;
			if (is_numeric($lastDiv) && $lastDiv > 0 && is_numeric($px) && $px > 0) {
			  $divYield = ($lastDiv * 4) / $px; // grobe TTM-Schätzung
			}
		  }
		  // TTM aus Historie, falls noch immer nicht vorhanden
		  if ((!is_numeric($divYield) || $divYield <= 0) && isset($d['divhist']) && is_array($d['divhist'])) {
			$events = $d['divhist']['historical'] ?? $d['divhist'];
			if (is_array($events) && is_numeric($price) && $price > 0) {
			  $sumTTM = 0.0;
			  $cutoff = (new DateTimeImmutable('now'))->sub(new DateInterval('P365D'));
			  foreach ($events as $ev) {
				$dt = isset($ev['date']) ? new DateTimeImmutable(substr($ev['date'],0,10)) : null;
				if ($dt && $dt >= $cutoff) {
				  $amt = (float)($ev['adjDividend'] ?? $ev['dividend'] ?? 0);
				  $sumTTM += max(0.0, $amt);
				}
			  }
			  if ($sumTTM > 0) $divYield = $sumTTM / $price; // Dezimal
			}
		  }
		  // Plausibilitätskappung (Sonderdividenden etc.)
		  if (is_numeric($divYield) && $divYield > 0.20) $divYield = 0.20;
		
		  // Payout-Score
		  $payoutScore = null;
		  if (is_numeric($payout) && $payout > 0) {
			$payoutMidLow  = sp_ramp($payout, 0.15, 0.35) ?? 0.0;
			$payoutMidHigh = sp_inv_ramp($payout, 0.55, 0.85) ?? 0.0;
			$payoutScore   = min(1.0, 0.5*$payoutMidLow + 0.5*$payoutMidHigh);
		  }
		
		  // Yield-Score (nach allen Fallbacks)
		  $yieldScore = null;
		  if (is_numeric($divYield) && $divYield > 0) {
			// etwas breiterer Sweet-Spot: 1.5%..4.5%
			$yieldScore = sp_ramp($divYield, 0.015, 0.045);
		  }
		
		  // Basisscore Dividendenqualität
		  if ($yieldScore !== null || $payoutScore !== null) {
			$baseDiv = 0.0; $cnt=0;
			if ($yieldScore !== null)  { $baseDiv += $yieldScore;  $cnt++; }
			if ($payoutScore !== null) { $baseDiv += $payoutScore; $cnt++; }
			if ($cnt>0) $divQual = $baseDiv / $cnt;
		  }
		
		  // Premium: Historie-Boni (Streak & 5Y-CAGR)
		  $streak = null; $cagr5 = null;
		  if ($wantPremium && isset($d['divhist']) && is_array($d['divhist'])) {
			$events = $d['divhist']['historical'] ?? $d['divhist'];
			if (is_array($events)) {
			  // Jahresaggregation
			  $byYear = [];
			  foreach ($events as $ev) {
				$y = (int)substr((string)($ev['date'] ?? ''), 0, 4);
				$amt = (float)($ev['adjDividend'] ?? $ev['dividend'] ?? 0);
				if ($y > 0) $byYear[$y] = ($byYear[$y] ?? 0) + max(0.0, $amt);
			  }
			  ksort($byYear);
			  $years  = array_keys($byYear);
			  $last10 = array_slice($years, -10);
		
			  // Streak (zusammenhängende Jahre mit Auszahlung)
			  $streak = 0; $run=0; $prev=null;
			  foreach ($last10 as $y) {
				$paid = ($byYear[$y] ?? 0) > 0;
				if ($prev !== null && $y !== $prev+1) $run = 0;
				if ($paid) $run++; else $run = 0;
				$streak = max($streak, $run);
				$prev = $y;
			  }
		
			  // 5Y-CAGR
			  if (count($years) >= 6) {
				$yEnd = end($years);
				$yStart = $yEnd - 5;
				$divStart = $byYear[$yStart] ?? null;
				$divEnd   = $byYear[$yEnd] ?? null;
				if (is_numeric($divStart) && $divStart > 0 && is_numeric($divEnd) && $divEnd > 0) {
				  $cagr5 = pow($divEnd/$divStart, 1/5) - 1.0; // Dezimal
				}
			  }
		
			  // Bonis
			  if ($streak >= 5)                         sp_add(1.0, 3, $score,$total,$why,'Dividenden-Streak ≥5J');
			  if (is_numeric($cagr5) && $cagr5 >= 0.05) sp_add(1.0, 2, $score,$total,$why,'Dividendenwachstum ≥5% p.a.');
			}
		  }
		
		  // --- Kern-Scoring (nur valide Werte) ---
		  if (is_numeric($pe) && $pe > 0)
			sp_add(sp_inv_ramp($pe, 12, 25),              $W['erg']['pe'],       $score,$total,$why,'KGV günstig');
		
		  if (is_numeric($ev_ebitda) && $ev_ebitda > 0)
			sp_add(sp_inv_ramp($ev_ebitda, 6, 12),        $W['erg']['ev_ebit'],  $score,$total,$why,'EV/EBITDA günstig');
		
		  if (is_numeric($ev_sales) && $ev_sales > 0)
			sp_add(sp_inv_ramp($ev_sales, 1.0, 3.0),      $W['erg']['ev_sales'], $score,$total,$why,'EV/Sales moderat');
		
		  if (is_numeric($epsCagr))
			sp_add(sp_ramp($epsCagr, 0.08, 0.15),         $W['erg']['eps'],      $score,$total,$why,'EPS-Wachstum');
		
		  if (is_numeric($fcfYield))
			sp_add(sp_ramp($fcfYield, 0.04, 0.08),        $W['erg']['fcf'],      $score,$total,$why,'FCF-Yield attraktiv');
		
		  if (is_numeric($peg) && $peg > 0)
			sp_add(sp_inv_ramp($peg, 1.0, 2.0),           $W['erg']['peg'],      $score,$total,$why,'PEG vertretbar');
		
		  if (is_numeric($debtEbitda))
			sp_add(sp_inv_ramp($debtEbitda, 1.0, 3.0),    $W['erg']['debt'],     $score,$total,$why,'Debt/EBITDA moderat');
		
		  // Dividenden-Qualität jetzt hinzufügen (nachdem sie berechnet wurde)
		  if ($divQual !== null) {
			sp_add($divQual, $W['erg']['divQ'], $score,$total,$why,'Dividendenqualität');
		  }
		
		  // kleine Boni (optional)
		  if (is_numeric($debtEbitda) && $debtEbitda <= 1.0 && !empty($W['erg']['bonus_safe_leverage']))
			sp_add(1.0, $W['erg']['bonus_safe_leverage'], $score,$total,$why,'Konservative Verschuldung');
		
		  if (is_numeric($fcfYield) && $fcfYield >= 0.06 && !empty($W['erg']['bonus_strong_fcf']))
			sp_add(1.0, $W['erg']['bonus_strong_fcf'],    $score,$total,$why,'Starker FCF-Yield');
		
		  // Badge (Ampel) optional ergänzen, wenn noch nicht vorhanden
		  if ($divQual !== null) {
			$hasLabel = false;
			foreach ($why as $w) { if (stripos($w, 'Dividendenqualität') !== false) { $hasLabel = true; break; } }
			if (!$hasLabel) {
			  $bucket = ($divQual >= 0.8) ? 'grün' : (($divQual >= 0.6) ? 'gelb' : 'rot');
			  $why[] = 'Dividendenqualität (' . $bucket . ')';
			}
		  }
		
		  // --- optional: Debug-Badges aktivieren via ?debug_div=1 ---
		  $debugDiv = !empty($_GET['debug_div']) || !empty($_POST['debug_div']) || !empty($opts['debug']);
		  if ($debugDiv) {
			$yPct = is_numeric($divYield) ? ($divYield * 100.0) : null;
			$pPct = is_numeric($payout)   ? ($payout   * 100.0) : null;
			$why_debug = [];
			$why_debug[] = 'DEBUG DivYieldTTM=' . (is_numeric($yPct) ? number_format($yPct,2,'.','') . '%' : 'n/a');
			$why_debug[] = 'DEBUG PayoutTTM='   . (is_numeric($pPct) ? number_format($pPct,1,'.','') . '%' : 'n/a');
			if ($wantPremium && (isset($streak) || isset($cagr5))) {
			  if (isset($streak)) $why_debug[] = 'DEBUG DivStreakYears=' . (is_numeric($streak) ? (string)$streak : 'n/a');
			  if (isset($cagr5))  $why_debug[] = 'DEBUG DivCAGR5=' . (is_numeric($cagr5) ? number_format($cagr5*100.0,2,'.','') . '%' : 'n/a');
			}
			foreach (array_reverse($why_debug) as $dl) array_unshift($why, $dl);
		  }
		  break;

		case 'high_performer':
		default:
			$revG   = $fg['revenueGrowth'] ?? ($fg['revenuegrowth'] ?? null);
			$epsG   = $fg['epsgrowth'] ?? ($fg['epsGrowth'] ?? null);
			$gm     = $rt['grossProfitMarginTTM'] ?? null;
			$debtEb = $rt['netDebtToEBITDATTM'] ?? null;
			$peg    = $km['pegRatioTTM'] ?? ($km['pegRatio'] ?? ($rt['priceEarningsToGrowthRatioTTM'] ?? null));

			// Guardrails - Trend & Momentum Pflicht
			if (!is_numeric($sma200) || !is_numeric($price) || $price <= $sma200) {
				return ['percent'=>0.0,'name'=>$name,'exchange'=>$exchange,'sector'=>$sector,'why'=>['Guard: Preis ≤ SMA200'],'premium'=>null];
			}
			if (!is_numeric($mom12m) || $mom12m < 10) {
				return ['percent'=>0.0,'name'=>$name,'exchange'=>$exchange,'sector'=>$sector,'why'=>['Guard: 12M Momentum < +10%'],'premium'=>null];
			}

			sp_add(sp_ramp($revG, 0.10, 0.25),          $W['hp']['rev'],   $score,$total,$why,'Umsatzwachstum');
			sp_add(sp_ramp($epsG, 0.15, 0.30),          $W['hp']['eps'],   $score,$total,$why,'EPS-Wachstum');
			sp_add(sp_ramp($gm,   0.30, 0.45),          $W['hp']['gm'],    $score,$total,$why,'Gross Margin');
			
			if ($debtEb !== null)
			  sp_add(sp_inv_ramp($debtEb, 1.0, 2.5),    $W['hp']['debt'],  $score,$total,$why,'Debt/EBITDA ok');
			
			$trend = ($price > ($sma50 ?? 0) && ($sma50 ?? 0) > ($sma200 ?? 0)) ? 1.0 : 0.0;
			sp_add($trend,                               $W['hp']['trend'], $score,$total,$why,'Trend > SMA50 > SMA200');
			
			if ($mom12m !== null)
			  sp_add(sp_ramp($mom12m, 0.10, 0.30),      $W['hp']['mom'],   $score,$total,$why,'12M Momentum');
			
			sp_add(sp_inv_ramp($peg, 1.0, 2.0),          $W['hp']['peg'],   $score,$total,$why,'PEG ≤ 2');

			break;
	}

	$premium = null;
	if ($wantPremium) {
		// Estimates: Revisions-Tonus (rudimentär)
		$est = $d['estimates'] ?? null;
		$rev_eps = null;
		if (is_array($est) && !empty($est)) {
			$latest = $est[0] ?? null;
			if (is_array($latest)) {
				$rev_eps = $latest['epsTrend'] ?? ($latest['epsRevision3M'] ?? null);
			}
		}

		// Surprises via /stable/earnings: zähle positive surprises in den letzten 4 Events
		$sur = $d['earnings'] ?? null;
		$pos_sur = null;
		if (is_array($sur)) {
			$rows = array_slice($sur, 0, 4);
			$c=0;
			foreach ($rows as $r) {
				$perc = $r['surprisePercent'] ?? $r['surprisePercentage'] ?? null;
				if (is_numeric($perc) && $perc > 0) $c++;
			}
			$pos_sur = $c;
		}

		// Insider Signal: Summe Käufe - Verkäufe (einfach)
		$ins = $d['insiders'] ?? null;
		$insider_net = null;
		if (is_array($ins)) {
			$s = 0.0;
			foreach (array_slice($ins, 0, 50) as $r) {
				$type = strtolower((string)($r['type'] ?? $r['transactionType'] ?? ''));
				$val  = (float)($r['value'] ?? $r['transactionValue'] ?? 0);
				if (str_contains($type,'buy')) $s += $val;
				elseif (str_contains($type,'sell')) $s -= $val;
			}
			$insider_net = $s;
		}
		
		$buyback_yield = null;
		if (isset($d['ev_values']) && is_array($d['ev_values'])) {
			// FMP liefert meist ein Array (neu->alt). Wir brauchen zwei Zeitpunkte.
			$rows = $d['ev_values'];
			if (is_array($rows) && count($rows) >= 2) {
				// sicherstellen, dass wir zwei aufeinanderfolgende Einträge nehmen
				$a = $rows[0]['numberOfShares'] ?? null; // jüngster
				$b = $rows[1]['numberOfShares'] ?? null; // davor
				if (is_numeric($a) && is_numeric($b) && $b > 0) {
					$chg = ($a/$b - 1.0) * 100.0; // + = Verwässerung, − = Buyback
					$buyback_yield = -$chg;       // positives Yield = Rückkauf
				}
			}
		}

		$premium = [
			'est_eps_revision_3m' => $rev_eps,
			'surprises_pos_last4' => $pos_sur,
			'insider_net_value'   => $insider_net,
			'buyback_yield_pct'   => $buyback_yield,
		];
	}

	$percent = $total > 0 ? round(($score / $total) * 100, 2) : 0.0;

	return [
		'percent'  => $percent,
		'name'     => $name ?: $symbol,
		'exchange' => $exchange ?: '',
		'sector'   => $sector ?: null,
		'why'      => $why,
		'premium'  => $premium,
	];
}