<?php
declare(strict_types=1);

/**
 * Zentrale Oversold/Overbought Bewertung nach deinem Pine-Skript.
 * - oversold_evaluate($symbol, $opts): bewertet EIN Symbol (binär 0/100) und liefert Details + Name/Exchange.
 *
 * Defaults (entsprechen deinem Screenshot):
 *  RSI: 14 | buy<30 | sell>70
 *  Momentum: 63 Tage, buy<-20%, sell>+20%
 *  MACD: standardmäßig "Kein MACD-Kriterium"
 *  R2AR: 100, Quelle: close
 *  Filter: Trend (aus), Volatilität (an) mit Faktor 0.95
 */

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

/* ---------------- kleine Helfer ---------------- */

function ov_sma(array $arr, int $period): ?float {
	$n = count($arr);
	if ($period <= 0 || $n < $period) return null;
	$sum = 0.0;
	for ($i = $n - $period; $i < $n; $i++) $sum += (float)$arr[$i];
	return $sum / $period;
}

function ov_rsi14(array $closes, int $period = 14): ?float {
	$n = count($closes);
	if ($n <= $period) return null;
	$gain = 0.0; $loss = 0.0;
	for ($i = $n - $period; $i < $n; $i++) {
		$chg = (float)$closes[$i] - (float)$closes[$i-1];
		if ($chg >= 0) $gain += $chg; else $loss += -$chg;
	}
	$avgGain = $gain / $period;
	$avgLoss = $loss / $period;
	if ($avgLoss == 0.0) return 100.0;
	$rs = $avgGain / $avgLoss;
	return 100.0 - (100.0 / (1.0 + $rs));
}

function ov_momentum_pct(array $closes, int $length = 63): ?float {
	$n = count($closes);
	if ($n <= $length) return null;
	$last = (float)$closes[$n - 1];
	$prev = (float)$closes[$n - 1 - $length];
	if ($prev == 0.0) return null;
	return ($last - $prev) / $prev * 100.0;
}

function ov_true_range_bar(array $curr, ?array $prev): ?float {
	if (!isset($curr['high'], $curr['low'], $curr['close'])) return null;
	$hl = (float)$curr['high'] - (float)$curr['low'];
	if ($prev && isset($prev['close'])) {
		$hc = abs((float)$curr['high'] - (float)$prev['close']);
		$lc = abs((float)$curr['low']  - (float)$prev['close']);
		return max($hl, $hc, $lc);
	}
	return $hl;
}

function ov_atr(array $bars, int $period = 14): ?float {
	$n = count($bars);
	if ($n <= $period) return null;
	$trs = [];
	for ($i = 0; $i < $n; $i++) {
		$prev = $i > 0 ? $bars[$i-1] : null;
		$tr = ov_true_range_bar($bars[$i], $prev);
		if ($tr === null) return null;
		$trs[] = $tr;
	}
	return ov_sma($trs, $period);
}

/* ---------------- MACD (12/26/9) ---------------- */

function ov_macd(array $closes, int $fast = 12, int $slow = 26, int $signal = 9): ?array {
	$n = count($closes);
	if ($n < $slow + $signal + 5) return null;

	$kF = 2.0 / ($fast + 1.0);
	$kS = 2.0 / ($slow + 1.0);
	$kSig = 2.0 / ($signal + 1.0);

	$emaFast = []; $emaSlow = [];
	$prevF = (float)$closes[0]; $prevS = (float)$closes[0];
	foreach ($closes as $v) {
		$prevF = (float)$v * $kF + $prevF * (1 - $kF);
		$prevS = (float)$v * $kS + $prevS * (1 - $kS);
		$emaFast[] = $prevF;
		$emaSlow[] = $prevS;
	}

	$macdLine = [];
	for ($i = 0; $i < $n; $i++) $macdLine[$i] = $emaFast[$i] - $emaSlow[$i];

	$sigLine = [];
	$prevSig = $macdLine[0];
	foreach ($macdLine as $v) {
		$prevSig = $v * $kSig + $prevSig * (1 - $kSig);
		$sigLine[] = $prevSig;
	}

	$hist = [];
	for ($i = 0; $i < $n; $i++) $hist[$i] = $macdLine[$i] - $sigLine[$i];

	return [
		'macd'        => $macdLine[$n-1],
		'signal'      => $sigLine[$n-1],
		'hist'        => $hist[$n-1],
		'macd_prev'   => $macdLine[$n-2] ?? null,
		'signal_prev' => $sigLine[$n-2] ?? null,
	];
}

/* ---------------- R2AR (approx) ---------------- */

function ov_corr(array $a, array $b, int $len): ?float {
	// Reindizieren & auf die letzten len Werte beschränken
	$a = array_values($a);
	$b = array_values($b);

	$na = count($a);
	$nb = count($b);
	if ($len <= 1 || $na < $len || $nb < $len) return null;

	$aSeg = array_slice($a, -$len); // 0..len-1
	$bSeg = array_slice($b, -$len); // 0..len-1

	// Summen bilden
	$sumA = $sumB = $sumAB = $sumA2 = $sumB2 = 0.0;
	for ($j = 0; $j < $len; $j++) {
		$x = (float)$aSeg[$j];
		$y = (float)$bSeg[$j];
		$sumA  += $x;
		$sumB  += $y;
		$sumAB += $x * $y;
		$sumA2 += $x * $x;
		$sumB2 += $y * $y;
	}

	// Korrelation
	$num = $len * $sumAB - $sumA * $sumB;
	$den = sqrt(max(1e-12, ($len * $sumA2 - $sumA * $sumA) * ($len * $sumB2 - $sumB * $sumB)));
	return $den == 0.0 ? 0.0 : ($num / $den);
}

function ov_stdev(array $a, int $len): ?float {
	$n = count($a);
	if ($len <= 1 || $n < $len) return null;
	$sum = 0.0; for ($i = $n - $len; $i < $n; $i++) $sum += (float)$a[$i];
	$mean = $sum / $len;
	$v = 0.0; for ($i = $n - $len; $i < $n; $i++) { $d = (float)$a[$i] - $mean; $v += $d*$d; }
	return sqrt($v / $len);
}

function ov_linreg_y(array $y, int $len): ?float {
	$n = count($y);
	if ($len <= 1 || $n < $len) return null;
	$sx=$sy=$sxx=$sxy=0.0;
	for ($i = 0; $i < $len; $i++) {
		$xi = (float)$i;
		$yi = (float)$y[$n - $len + $i];
		$sx += $xi; $sy += $yi; $sxx += $xi*$xi; $sxy += $xi*$yi;
	}
	$den = ($len * $sxx - $sx*$sx);
	if ($den == 0.0) return $y[$n-1];
	$a = ($len*$sxy - $sx*$sy) / $den;   // slope
	$b = ($sy - $a*$sx) / $len;          // intercept
	$x_last = $len - 1;
	return $a * $x_last + $b;
}

function ov_r2ar_last(array $src, int $len = 100): ?float {
	$n = count($src);
	if ($n < $len + 2) return null;

	$outPrev = (float)$src[$n - $len]; // seed

	for ($i = $n - $len + 1; $i < $n; $i++) {
		$winStart = max(0, $i - $len + 1);
		$win = array_slice($src, $winStart, min($len, $i - $winStart + 1));

		$y1  = ov_linreg_y($src, min($len, $i - $winStart + 1));
		if ($y1 === null) $y1 = (float)$src[$i];

		$stdSrc = ov_stdev($src, min($len, $i - $winStart + 1));
		$stdX2  = ov_stdev($win, min($len, count($win)));
		$corr   = ov_corr($src, $win, min($len, count($win)));
		if ($stdSrc === null || $stdX2 === null || $corr === null) $corr = 0.0;

		$fa = ($stdSrc == 0.0 || $stdX2 == 0.0) ? 0.0 : ($stdSrc * $corr / $stdX2);
		$smaSrc = ov_sma($src, min($len, $i - $winStart + 1)) ?? (float)$src[$i];
		$smaX2  = ov_sma($win, min($len, count($win))) ?? (float)$outPrev;
		$fb = $smaSrc - $fa * $smaX2;
		$y2 = $fa * $outPrev + $fb;

		$fr = ($corr ?? 0.0) ** 2;   // proxy
		$out = $fr * $y1 + $fr * $y2 + (1.0 - 2*$fr) * $outPrev;

		$outPrev = (float)$out;
	}
	return $outPrev;
}

function ov_pick_src_series(array $bars, string $src): array {
	$src = strtolower($src);
	$out = [];
	foreach ($bars as $b) {
		$val = $b['close'];
		if ($src === 'open')  $val = $b['open'];
		if ($src === 'high')  $val = $b['high'];
		if ($src === 'low')   $val = $b['low'];
		$out[] = (float)$val;
	}
	return $out;
}

/* ---------------- Kernfunktion: EIN Symbol ---------------- */

function oversold_evaluate(string $symbol, array $opts = []): array {
	$o = array_merge([
		'rsi_len'       => 14,
		'rsi_buy'       => 30.0,   'rsi_sell' => 70.0,
		'mom_len'       => 63,     'mom_buy'  => -20.0, 'mom_sell' => 20.0,
		'macd_fast'     => 12,     'macd_slow' => 26,   'macd_signal' => 9,
		'macd_mode'     => 'Kein MACD-Kriterium',  // standardmäßig aus/neutral
		'macd_hist_max' => 0.0,
		'trend_len'     => 100,
		'r2ar_src'      => 'close',                 // close|open|high|low
		'enable_trend'  => false,                   // wie Screenshot
		'enable_vola'   => true,
		'vola_mult'     => 0.95,
		'direction'     => 'buy',                   // 'buy'|'sell'
		'timeseries'    => 300,
	], $opts);

	$symbol = strtoupper(trim($symbol));

	// Multi-Fetch: Preise + Profil
	$multi = fmp_multi([
		'prices' => [
			'key'   => 'historical_price_full',
			'vars'  => ['symbol' => $symbol],
			'query' => ['timeseries' => max(120, (int)$o['timeseries'])],
		],
		'profile' => [
			'key'  => 'profile',
			'vars' => ['symbol' => $symbol],
		],
	]);

	$hist = $multi['prices']['historical'] ?? ($multi['prices'] ?? null);
	if (!is_array($hist) || count($hist) < 120) {
		return ['success' => false, 'message' => 'insufficient data', 'ticker' => $symbol];
	}

	// chronologisch aufsteigend
	usort($hist, fn($a,$b) => strcmp((string)($a['date'] ?? ''), (string)($b['date'] ?? '')));
	$hist = array_values($hist);

	$bars = [];
	$closes = [];
	foreach ($hist as $h) {
		if (!isset($h['open'],$h['high'],$h['low'],$h['close'])) continue;
		$bar = [
			'open'  => (float)$h['open'],
			'high'  => (float)$h['high'],
			'low'   => (float)$h['low'],
			'close' => (float)$h['close'],
		];
		$bars[]   = $bar;
		$closes[] = $bar['close'];
	}
	
	$bars   = array_values($bars);
	$closes = array_values($closes);

	// Indikatoren
	$rsi   = ov_rsi14($closes, (int)$o['rsi_len']);
	$mom   = ov_momentum_pct($closes, (int)$o['mom_len']);
	$macd  = ov_macd($closes, (int)$o['macd_fast'], (int)$o['macd_slow'], (int)$o['macd_signal']);
	$atr14 = ov_atr($bars, 14);

	// ATR-SMA(50) (als TR-SMA-Proxy, wie besprochen)
	$trs = [];
	for ($i = 0; $i < count($bars); $i++) {
		$prev = $i > 0 ? $bars[$i-1] : null;
		$tr = ov_true_range_bar($bars[$i], $prev);
		if ($tr === null) { $trs[] = null; continue; }
		$trs[] = $tr;
	}
	$atr_sma50 = ov_sma($trs, 50);

	// R2AR
	$srcSeries = ov_pick_src_series($bars, (string)$o['r2ar_src']);
	$r2ar = ov_r2ar_last($srcSeries, (int)$o['trend_len']);
	$closeLast = end($closes);

	// MACD-Modus
	$macdOkBuy = true; $macdOkSell = true; // neutral wenn "Kein MACD-Kriterium"
	if ($o['macd_mode'] !== 'Kein MACD-Kriterium' && is_array($macd)) {
		$m  = $macd['macd'] ?? null;      $s  = $macd['signal'] ?? null;
		$mp = $macd['macd_prev'] ?? null; $sp = $macd['signal_prev'] ?? null;
		switch ($o['macd_mode']) {
			case 'Histogramm < Schwelle':
				$macdOkBuy  = ($macd['hist'] ?? 0) <  (float)$o['macd_hist_max'];
				$macdOkSell = ($macd['hist'] ?? 0) > -(float)$o['macd_hist_max'];
				break;
			case 'Bullish Cross':
				$macdOkBuy  = (is_numeric($mp)&&is_numeric($sp)&&is_numeric($m)&&is_numeric($s)) ? ($mp < $sp && $m > $s) : false; // crossover
				$macdOkSell = (is_numeric($mp)&&is_numeric($sp)&&is_numeric($m)&&is_numeric($s)) ? ($mp > $sp && $m < $s) : false; // crossunder
				break;
			case 'Bearish Cross':
				$macdOkBuy  = (is_numeric($mp)&&is_numeric($sp)&&is_numeric($m)&&is_numeric($s)) ? ($mp > $sp && $m < $s) : false; // crossunder
				$macdOkSell = (is_numeric($mp)&&is_numeric($sp)&&is_numeric($m)&&is_numeric($s)) ? ($mp < $sp && $m > $s) : false; // crossover
				break;
		}
	}

	// Kernkriterien
	$rsiBuy  = is_numeric($rsi) ? ($rsi <  (float)$o['rsi_buy'])  : false;
	$rsiSell = is_numeric($rsi) ? ($rsi >  (float)$o['rsi_sell']) : false;
	$momBuy  = is_numeric($mom) ? ($mom <  (float)$o['mom_buy'])  : false;
	$momSell = is_numeric($mom) ? ($mom >  (float)$o['mom_sell']) : false;

	// Filter
	$trendUp  = is_numeric($r2ar) ? ($closeLast > $r2ar) : false;
	$volaOK   = (is_numeric($atr14) && is_numeric($atr_sma50)) ? ($atr14 > $atr_sma50 * (float)$o['vola_mult']) : false;
	$filterOK = ((!$o['enable_trend']) || $trendUp) && ((!$o['enable_vola']) || $volaOK);

	// Binärlogik:
	// - Wenn MACD aus: BUY = (RSI & Momentum), SELL = (RSI & Momentum)
	// - Wenn MACD an:  3/3 oder 2/3 & filterOK
	if ($o['macd_mode'] === 'Kein MACD-Kriterium') {
		$signalBuy  = ($rsiBuy  && $momBuy);
		$signalSell = ($rsiSell && $momSell);
		$hitsBuy  = ($rsiBuy?1:0) + ($momBuy?1:0);
		$hitsSell = ($rsiSell?1:0) + ($momSell?1:0);
	} else {
		$hitsBuy  = ($rsiBuy?1:0) + ($momBuy?1:0) + ($macdOkBuy?1:0);
		$hitsSell = ($rsiSell?1:0) + ($momSell?1:0)+ ($macdOkSell?1:0);
		$signalBuy  = ($hitsBuy  === 3) || ($hitsBuy  === 2 && $filterOK);
		$signalSell = ($hitsSell === 3) || ($hitsSell === 2 && $filterOK);
	}

	$useSell  = ($o['direction'] === 'sell');
	$isSignal = $useSell ? $signalSell : $signalBuy;
	$hits     = $useSell ? $hitsSell   : $hitsBuy;

	// Profil
	$prof     = $multi['profile'][0] ?? ($multi['profile'] ?? []);
	$name     = $prof['companyName']       ?? $symbol;
	$exchange = $prof['exchangeShortName'] ?? ($prof['exchange'] ?? '');

	// Score binär/Prozent aus Treffern ableiten
	$hitsOf3   = (int)$hits;     // 0..3 (bei MACD=aus max. 2)
	$totalHits = 3;
	$percent   = (int)round(100 * $hitsOf3 / $totalHits);
	$label     = "{$hitsOf3}/{$totalHits}";
	
	return [
		'success'      => true,
		'ticker'       => $symbol,
		'name'         => $name,
		'exchange'     => $exchange,
	
		'score'        => $percent,         // 0, 66, 100
		'signal'       => $isSignal,
		'signal_kind'  => $useSell ? 'overbought' : 'oversold',
	
		'hits'         => $hitsOf3,         // 0..3
		'total'        => $totalHits,       // 3
		'score_label'  => $label,           // "2/3", "3/3"
	
		'details'      => [
			'rsi14'         => $rsi,
			'rsi_ok_buy'    => $rsiBuy,
			'rsi_ok_sell'   => $rsiSell,
			'mom63'         => $mom,
			'mom_ok_buy'    => $momBuy,
			'mom_ok_sell'   => $momSell,
			'macd'          => $macd ?? null,
			'macd_ok_buy'   => $macdOkBuy,
			'macd_ok_sell'  => $macdOkSell,
			'r2ar'          => $r2ar,
			'trendUp'       => $trendUp,
			'atr14'         => $atr14,
			'atr_sma50'     => $atr_sma50,
			'volaOK'        => $volaOK,
			'filterOK'      => $filterOK,
			'direction'     => $o['direction'],
			'macd_mode'     => $o['macd_mode'],
			'r2ar_src'      => $o['r2ar_src'],
		],
	];

}