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

if (!function_exists('compute_seasonality')) {
	/**
	 * Saisonale Auswertung (Monatsrenditen + durchschnittlicher Tagesverlauf).
	 * @param string $symbol
	 * @param array $opts [
	 *   scope: 'last' (Default) | 'weighted' | 'full'
	 *   years: int Jahre für 'last' (Default 15)
	 *   half_life: int Halbwertszeit (Default 7) für 'weighted'
	 *   serietype: 'line' (Default)
	 *   start_day, end_day (optional, 'TT.MM') – nur relevant falls mit Segmente gearbeitet wird
	 *   with_segments: bool (Default false)
	 * ]
	 * @return array {
	 *   avgMonthly: float|null[1..12],
	 *   avgDaySeries: float|null[0..365],
	 *   monthlyCounts: int[1..12],
	 *   meta: { dataStartDate, dataEndDate, yearsOfData, scopeUsed, yearsUsed, halfLifeUsed },
	 *   historical: array,          // DESC (neu -> alt)
	 *   historicalAsc: array,       // ASC (alt -> neu)
	 *   segments?: array            // nur wenn with_segments=true & Start/Ende gesetzt
	 * }
	 */
	function compute_seasonality(string $symbol, array $opts = []): array {
		$scope     = $opts['scope']      ?? 'last';     // 'last' | 'weighted' | 'full'
		$years     = max(1, (int)($opts['years'] ?? 15));
		$halfLife  = max(1, (int)($opts['half_life'] ?? 7));
		$serietype = $opts['serietype']  ?? 'line';
		$startDay  = $opts['start_day']  ?? null;
		$endDay    = $opts['end_day']    ?? null;
		$withSeg   = (bool)($opts['with_segments'] ?? false);

		// Daten holen (Helper kommen aus deiner Codebase)
		$query = ['serietype' => $serietype];
		if ($scope !== 'full' && $scope !== 'weighted') {
			$from = (new DateTime())->modify("-{$years} years")->format('Y-m-d');
			$query['from'] = $from;
		}
		
		$multi = fmp_multi([
			'prices' => [
				'key'  => 'historical_price_full',
				'vars' => ['symbol' => $symbol],
				'query'=> $query,
			],
			'profile' => [
				'key'  => 'profile',
				'vars' => ['symbol' => $symbol],
			],
		]);
		
		$resp = $multi['prices'] ?? [];
		$historical = $resp['historical'] ?? [];
		
		$profRaw  = $multi['profile'] ?? [];
		$profRow  = is_array($profRaw) ? ($profRaw[0] ?? $profRaw) : [];
		$name     = $profRow['companyName']       ?? $symbol;
		$exchange = $profRow['exchangeShortName'] ?? ($profRow['exchange'] ?? '');

		// Rückgabe-Grundgerüst (Keys sind IMMER gesetzt)
		$result = [
			'avgMonthly'    => array_fill(1, 12, null),
			'avgDaySeries'  => array_fill(0, 366, null),
			'monthlyCounts' => array_fill(1, 12, 0),
			'meta'          => [
				'dataStartDate' => null,
				'dataEndDate'   => null,
				'yearsOfData'   => null,
				'scopeUsed'     => $scope,
				'yearsUsed'     => $years,
				'halfLifeUsed'  => $halfLife,
			],
			'historical'    => [],
			'historicalAsc' => [],
		];

		if (empty($historical)) {
			return $result;
		}

		// Metadaten (FMP: neu -> alt)
		$result['meta']['dataEndDate']   = $historical[0]['date'];
		$result['meta']['dataStartDate'] = $historical[count($historical)-1]['date'];
		$diff = (new DateTime($result['meta']['dataStartDate']))->diff(new DateTime($result['meta']['dataEndDate']));
		$result['meta']['yearsOfData'] = $diff->y + ($diff->m > 6 ? 1 : 0);

		// Monatsrenditen je Jahr vorbereiten
		$monthBuckets = []; // 'YYYY-M' => ['start'=>float,'end'=>float]
		foreach ($historical as $row) {
			$d  = new DateTime($row['date']);
			$ym = $d->format('Y') . '-' . (int)$d->format('n');
			if (!isset($monthBuckets[$ym])) {
				$monthBuckets[$ym] = ['start'=>$row['close'], 'end'=>$row['close']];
			} else {
				$monthBuckets[$ym]['start'] = $row['close'];
			}
		}
		$monthlyByYear = array_fill(1, 12, []); // month => [ ['ret'=>%, 'y'=>int], ... ]
		foreach ($monthBuckets as $ym => $px) {
			if ($px['start'] > 0) {
				[$yy, $mm] = explode('-', $ym);
				$ret = ($px['end'] - $px['start']) / $px['start'] * 100.0;
				$monthlyByYear[(int)$mm][] = ['ret'=>$ret, 'y'=>(int)$yy];
			}
		}

		// Gewichtung (nur weighted)
		$nowY = (int)date('Y');
		$weight = function (int $y) use ($scope, $halfLife, $nowY): float {
			if ($scope !== 'weighted') return 1.0;
			$age = max(0, $nowY - $y);
			return pow(0.5, $age / $halfLife);
		};

		// Monats-Durchschnitt
		foreach ($monthlyByYear as $m => $rows) {
			$result['monthlyCounts'][$m] = count($rows);
			if (!$rows) continue;
			if ($scope === 'weighted') {
				$num = 0.0; $den = 0.0;
				foreach ($rows as $r) { $w = $weight($r['y']); $num += $r['ret']*$w; $den += $w; }
				$result['avgMonthly'][$m] = $den>0 ? round($num/$den, 2) : null;
			} else {
				$result['avgMonthly'][$m] = round(array_sum(array_column($rows,'ret'))/count($rows), 2);
			}
		}

		// Jahresdurchschnitt (Index 100 am Jahresanfang)
		$yearStarts = [];
		$dayBuckets = []; // z(0..365) => [ ['v'=>float,'y'=>int], ... ]
		foreach ($historical as $row) {
			$d = new DateTime($row['date']);
			$y = (int)$d->format('Y');
			$z = (int)$d->format('z');
			if (!isset($yearStarts[$y])) $yearStarts[$y] = $row['close'];
			if ($yearStarts[$y] > 0) {
				$v = 100.0 * $row['close'] / $yearStarts[$y];
				$dayBuckets[$z][] = ['v'=>$v, 'y'=>$y];
			}
		}
		for ($i=0; $i<366; $i++) {
			$rows = $dayBuckets[$i] ?? [];
			if (!$rows) continue;
			if ($scope === 'weighted') {
				$num = 0.0; $den = 0.0;
				foreach ($rows as $r) { $w = $weight($r['y']); $num += $r['v']*$w; $den += $w; }
				$result['avgDaySeries'][$i] = $den>0 ? round($num/$den, 2) : null;
			} else {
				$result['avgDaySeries'][$i] = round(array_sum(array_column($rows,'v'))/count($rows), 2);
			}
		}

		// Optionale Segmente (nur falls wirklich gewünscht)
		if ($withSeg && !empty($startDay) && !empty($endDay) && function_exists('get_seasonal_segments')) {
			$result['segments'] = get_seasonal_segments($historical, $startDay, $endDay);
		}

		// Rohserien
		$result['historical']    = array_values($historical);                 // DESC
		$result['historicalAsc'] = array_values(array_reverse($historical));  // ASC
		return $result;
	}
}

if (!function_exists('build_monthly_distribution_from_historical')) {
	function build_monthly_distribution_from_historical(array $historicalDesc): array {
		$monthly = array_fill(1, 12, []);
		if (empty($historicalDesc)) return $monthly;
		$lastPrices = []; // 'YYYY-M' => ['start'=>, 'end'=>]
		foreach ($historicalDesc as $row) {
			$d  = new DateTime($row['date']);
			$ym = $d->format('Y').'-'.(int)$d->format('n');
			if (!isset($lastPrices[$ym])) {
				$lastPrices[$ym] = ['start'=>$row['close'], 'end'=>$row['close']];
			} else {
				$lastPrices[$ym]['start'] = $row['close'];
			}
		}
		foreach ($lastPrices as $ym => $px) {
			if ($px['start'] > 0) {
				[, $m] = explode('-', $ym);
				$monthly[(int)$m][] = ($px['end'] - $px['start']) / $px['start'] * 100.0;
			}
		}
		return $monthly;
	}
}

if (!function_exists('build_monthly_stats')) {
	function build_monthly_stats(array $monthly): array {
		$monthlyStats = [];
		foreach ($monthly as $month => $returns) {
			if (empty($returns)) continue;
			$positive = array_filter($returns, fn($r) => $r > 0);
			$monthlyStats[$month] = [
				'avg'      => round(array_sum($returns) / count($returns), 2),
				'best'     => round(max($returns), 2),
				'worst'    => round(min($returns), 2),
				'count'    => count($returns),
				'positive' => count($positive),
				'hitRate'  => round(100 * count($positive) / count($returns), 1),
			];
		}
		return $monthlyStats;
	}
}

if (!function_exists('month_score_from_stats')) {
	function month_score_from_stats(array $s): int {
		$avg   = $s['avg'];
		$hit   = $s['hitRate'];
		$best  = $s['best'];
		$worst = $s['worst'];
		$spread = abs($best - $worst);

		$scoreRendite    = $avg > 0 ? min(30, round($avg / 5 * 30, 1)) : 0;
		$scoreHit        = $hit >= 50 ? round(($hit - 50) / 50 * 30, 1) : 0;
		$scoreVolatility = $spread < 100 ? round((1 - min(1, $spread / 100)) * 20, 1) : 0;
		$bonus           = ($avg > 0 && $hit > 60) ? 20 : 0;

		return (int) round($scoreRendite + $scoreHit + $scoreVolatility + $bonus);
	}
}

if (!function_exists('make_heatmap_data')) {
	function make_heatmap_data(string $symbol, array $opts = []): array {
		$season = compute_seasonality($symbol, $opts);
		$historicalDesc = $season['historical'] ?? [];
		$monthly = build_monthly_distribution_from_historical($historicalDesc);
		$stats   = build_monthly_stats($monthly);

		$getHeatColor = function (float $avg, float $hit): string {
			$scoreRendite = max(0, min(50, ($avg * 10)));
			$scoreHit     = max(0, min(50, ($hit - 40) * 1.25));
			$score        = round($scoreRendite + $scoreHit);
			if ($score >= 80) return 'rgba(0, 150, 0, 0.7)';
			if ($score >= 60) return 'rgba(100, 180, 0, 0.7)';
			if ($score >= 40) return 'rgba(255, 200, 0, 0.7)';
			if ($score >= 20) return 'rgba(255, 120, 0, 0.7)';
			return 'rgba(220, 50, 50, 0.7)';
		};

		$months = []; $avgArr = []; $hitArr = []; $colors = [];
		for ($m = 1; $m <= 12; $m++) {
			$months[] = DateTime::createFromFormat('!m', $m)->format('M');
			if (isset($stats[$m])) {
				$avg = $stats[$m]['avg'];
				$hit = $stats[$m]['hitRate'];
				$avgArr[] = $avg;
				$hitArr[] = $hit;
				$colors[] = $getHeatColor($avg, $hit);
			} else {
				$avgArr[] = 0;
				$hitArr[] = null;
				$colors[] = 'rgba(200,200,200,0.2)';
			}
		}
		return [
			'months'	=> $months,
			'avg'    	=> $avgArr,
			'hit'    	=> $hitArr,
			'colors' 	=> $colors,
			'meta'   	=> $season['meta'] ?? [],
			'name'     	=> $name,
			'exchange' 	=> $exchange,
		];
	}
}

if (!function_exists('scan_symbols_by_month_score')) {
	function scan_symbols_by_month_score(array $symbols, int $month, int $threshold = 100, array $opts = []): array {
		$results = [];
		$month = max(1, min(12, (int)$month));

		foreach ($symbols as $symbol) {
			$symbol = strtoupper(trim($symbol));
			if ($symbol === '') continue;

			$season = compute_seasonality($symbol, $opts);
			$historicalDesc = $season['historical'] ?? [];
			if (empty($historicalDesc)) continue;

			$monthly = build_monthly_distribution_from_historical($historicalDesc);
			$stats   = build_monthly_stats($monthly);
			if (!isset($stats[$month])) continue;

			$score = month_score_from_stats($stats[$month]);
			if ($score >= $threshold) {
				$results[] = [
					'symbol'  => $symbol,
					'month'   => $month,
					'score'   => $score,
					'avg'     => $stats[$month]['avg'],
					'hitRate' => $stats[$month]['hitRate'],
					'meta'    => $season['meta'] ?? [],
				];
			}
			// Optional: usleep(80000); // 80ms gegen API-Limits
		}

		usort($results, fn($a, $b) => $b['score'] <=> $a['score']);
		return $results;
	}
}

if (!function_exists('get_seasonal_segments')) {
	function get_seasonal_segments(array $historical, string $startDay, string $endDay): array {
		$segments = [];
		$byYear = [];
		foreach ($historical as $row) {
			$year = (int)substr($row['date'], 0, 4);
			$byYear[$year][] = $row;
		}

		[$start_d, $start_m] = explode('.', $startDay);
		[$end_d, $end_m]     = explode('.', $endDay);

		foreach ($byYear as $year => $rows) {
			$segment = [];

			$startDate = DateTime::createFromFormat('d.m.Y', sprintf('%02d.%02d.%04d', $start_d, $start_m, $year));
			$endDate   = DateTime::createFromFormat('d.m.Y', sprintf('%02d.%02d.%04d', $end_d, $end_m, $year));
			if (!$startDate || !$endDate) continue;

			if ($startDate > $endDate) {
				// Periode geht über Jahreswechsel -> Enddatum +1 Jahr
				$endDate->modify('+1 year');
			}

			foreach ($rows as $row) {
				$currDate = DateTime::createFromFormat('Y-m-d', $row['date']);
				if (!$currDate) continue;

				$cmpDate = clone $currDate;
				if ($startDate > $endDate && (int)$currDate->format('n') <= (int)$end_m) {
					// beim Wrap: Datum gedanklich ins Folgejahr legen
					$cmpDate->modify('+1 year');
				}

				if ($cmpDate >= $startDate && $cmpDate <= $endDate) {
					$segment[] = $row;
				}
			}

			if (count($segment) >= 5) {
				usort($segment, fn($a, $b) => strtotime($a['date']) <=> strtotime($b['date'])); // ASC
				$segments[$year] = $segment;
			}
		}
		return $segments;
	}
}

if (!function_exists('calc_max_drawdown')) {
	function calc_max_drawdown(array $segments): float {
		$drawdowns = [];
		foreach ($segments as $rows) {
			$peak = $rows[0]['close'];
			$maxDD = 0.0;
			foreach ($rows as $row) {
				$price = $row['close'];
				if ($price > $peak) $peak = $price;
				$dd = ($price - $peak) / $peak;
				if ($dd < $maxDD) $maxDD = $dd;
			}
			$drawdowns[] = $maxDD;
		}
		return count($drawdowns) ? round(100 * array_sum($drawdowns) / count($drawdowns), 2) : 0.0;
	}
}

if (!function_exists('calc_volatility')) {
	function calc_volatility(array $segments): float {
		$returns = [];
		foreach ($segments as $rows) {
			for ($i = 1; $i < count($rows); $i++) {
				$prev = $rows[$i-1]['close'];
				$curr = $rows[$i]['close'];
				if ($prev > 0) $returns[] = ($curr - $prev) / $prev;
			}
		}
		if (count($returns) < 2) return 0.0;
		$mean = array_sum($returns) / count($returns);
		$variance = array_sum(array_map(fn($r) => pow($r - $mean, 2), $returns)) / (count($returns) - 1);
		return round(sqrt($variance) * 100, 2);
	}
}

if (!function_exists('calc_avg_return')) {
	function calc_avg_return(array $segments): float {
		$returns = [];
		foreach ($segments as $rows) {
			$first = $rows[0]['close'];
			$last  = $rows[count($rows)-1]['close'];
			if ($first > 0) $returns[] = ($last - $first) / $first * 100.0;
		}
		return count($returns) ? round(array_sum($returns) / count($returns), 2) : 0.0;
	}
}

if (!function_exists('calc_hit_ratio')) {
	function calc_hit_ratio(array $segments): float {
		$positives = 0; $total = 0;
		foreach ($segments as $rows) {
			$first = $rows[0]['close'];
			$last  = $rows[count($rows)-1]['close'];
			if ($first > 0) { $total++; if ($last > $first) $positives++; }
		}
		return $total ? round($positives / $total * 100, 2) : 0.0;
	}
}

if (!function_exists('smoothSeries')) {
	function smoothSeries($data, $window = 5) {
		$smoothed = [];
		$count = count($data);
		for ($i = 0; $i < $count; $i++) {
			$sum = 0; $n = 0;
			for ($j = -$window; $j <= $window; $j++) {
				$idx = $i + $j;
				if ($idx >= 0 && $idx < $count && is_numeric($data[$idx])) {
					$sum += $data[$idx]; $n++;
				}
			}
			$smoothed[] = $n > 0 ? round($sum / $n, 2) : null;
		}
		return $smoothed;
	}
}

if (!function_exists('get_best_entry_exit_day')) {
	function get_best_entry_exit_day(array $segments): array {
		$dailyReturns = [];
		foreach ($segments as $rows) {
			$count = count($rows);
			for ($i = 0; $i < $count - 1; $i++) {
				for ($j = $i + 1; $j < $count; $j++) {
					$start = $rows[$i];
					$end   = $rows[$j];
					if ($start['close'] > 0) {
						$ret = ($end['close'] - $start['close']) / $start['close'] * 100.0;
						$key = $start['date'].' - '.$end['date'];
						$dailyReturns[$key][] = $ret;
					}
				}
			}
		}
		if (empty($dailyReturns)) return [];
		$avgReturns = array_map(fn($r) => array_sum($r) / count($r), $dailyReturns);
		arsort($avgReturns);
		$bestCombo = key($avgReturns);
		return ['period' => $bestCombo, 'avgReturn' => round($avgReturns[$bestCombo], 2)];
	}
}

if (!function_exists('simulate_trades')) {
	function simulate_trades(array $segments): array {
		$longReturns = []; $shortReturns = [];
		foreach ($segments as $rows) {
			$first = $rows[0]['close'];
			$last  = $rows[count($rows)-1]['close'];
			if ($first > 0) {
				$longReturns[]  = ($last - $first) / $first * 100.0;
				$shortReturns[] = - end($longReturns);
			}
		}
		$cntL = count($longReturns) ?: 1;
		$cntS = count($shortReturns) ?: 1;
		return [
			'longAvg'  => round(array_sum($longReturns)  / $cntL, 2),
			'shortAvg' => round(array_sum($shortReturns) / $cntS, 2),
		];
	}
}

/**
 * Erwartet ascending-sortierte Closes: [['date'=>'YYYY-MM-DD','close'=>float], ...]
 * direction: 'long' (Monatsrendite > 0 ist Gewinn) oder 'short' (Monatsrendite < 0 ist Gewinn)
 * Rückgabe:
 * [
 *   'per_month' => [
 *      1 => ['wins'=>int,'total'=>int,'rate'=>float, 'years'=>[2016,2017,...]],
 *      ...
 *   ],
 *   'best' => ['month'=>int|null, 'rate_percent'=>int|null],
 * ]
 */
function bewertung_saisonalitaet_100(string $symbol, array $opts = []): array {
	$months           = isset($opts['months']) && $opts['months'] ? array_map('intval',$opts['months']) : range(1,12);
	$thresholdPercent = isset($opts['threshold_percent']) ? (int)$opts['threshold_percent'] : 90;
	$minYears         = isset($opts['min_years']) ? (int)$opts['min_years'] : 5;
	$direction        = $opts['direction'] ?? 'long';
	$yearsBack        = isset($opts['max_years_back']) ? (int)$opts['max_years_back'] : 15;
 
	// Saison-Daten (nutzt bereits compute_seasonality)
	$season = compute_seasonality($symbol, ['scope'=>'last','years'=>$yearsBack]);
	$historicalDesc = $season['historical'] ?? [];
	if (empty($historicalDesc)) {
	  return ['name'=>$symbol,'exchange'=>'','score_percent'=>0,'score_label'=>'--','data'=>[]];
	}
 
	$monthly = build_monthly_distribution_from_historical($historicalDesc);
 
	// Richtung anwenden: short = Vorzeichen invertieren für Trefferlogik
	if ($direction === 'short') {
	  foreach ($monthly as $m => &$rets) $rets = array_map(fn($r)=>-$r, $rets);
	  unset($rets);
	}
 
	$stats = build_monthly_stats($monthly); // enthält 'hitRate' und 'count'
	$best = 0; $bestM = null; $qualified = []; $yearsByMonth = [];
 
	foreach ($months as $m) {
	  $st = $stats[$m] ?? null;
	  if (!$st) continue;
	  $yearsByMonth[$m] = (int)$st['count'];
	  $hit = (int)round($st['hitRate']); // 0..100
	  if ($st['count'] >= $minYears && $hit >= $thresholdPercent) $qualified[] = $m;
	  if ($st['count'] >= $minYears && $hit > $best) { $best = $hit; $bestM = $m; }
	}
 
	// --- Name & Exchange robust aus compute_seasonality (und Profil) befüllen ---
	$name     = '';
	$exchange = '';
	
	// 1) aus compute_seasonality nehmen
	if (!empty($season['name']))      $name     = trim((string)$season['name']);
	if (!empty($season['exchange']))  $exchange = trim((string)$season['exchange']);
	if ($name === ''     && !empty($season['meta']['name']))     $name     = trim((string)$season['meta']['name']);
	if ($exchange === '' && !empty($season['meta']['exchange'])) $exchange = trim((string)$season['meta']['exchange']);
	
	// evtl. Profil steckt in meta/profile oder profile
	$prof = null;
	if (isset($season['meta']['profile']) && is_array($season['meta']['profile'])) {
		$prof = $season['meta']['profile'];
	} elseif (isset($season['profile']) && is_array($season['profile'])) {
		$prof = $season['profile'];
	}
	if (is_array($prof)) {
		// einige Clients liefern das Profil als Array-Objekt, andere als Liste [0=>...]
		$p = (isset($prof['symbol']) || isset($prof['ticker'])) ? $prof : ((isset($prof[0]) && is_array($prof[0])) ? $prof[0] : null);
		if (is_array($p)) {
			if ($name === '' && !empty($p['companyName']))           $name = (string)$p['companyName'];
			if ($exchange === '' && !empty($p['exchangeShortName'])) $exchange = (string)$p['exchangeShortName'];
			if ($exchange === '' && !empty($p['primaryExchange']))   $exchange = (string)$p['primaryExchange'];
			if ($exchange === '' && !empty($p['exchange']))          $exchange = (string)$p['exchange'];
		}
	}
	
	// 2) Fallback: einmaliges Profil via fmp_multi (falls noch leer)
	if (($name === '' || $exchange === '') && function_exists('fmp_multi')) {
		try {
			$m = fmp_multi('profile', [$symbol]); // Map ODER Liste – wir fangen beides ab
			$p = $m[$symbol] ?? (is_array($m) ? reset($m) : null);
			if (is_array($p)) {
				if ($name === '' && !empty($p['companyName']))           $name = (string)$p['companyName'];
				if ($exchange === '' && !empty($p['exchangeShortName'])) $exchange = (string)$p['exchangeShortName'];
				if ($exchange === '' && !empty($p['primaryExchange']))   $exchange = (string)$p['primaryExchange'];
				if ($exchange === '' && !empty($p['exchange']))          $exchange = (string)$p['exchange'];
			}
		} catch (\Throwable $ignore) {}
	}
	
	// nie den Ticker als Namen zurückgeben
	if (strtoupper($name) === strtoupper($symbol)) $name = '';
	// --- /Name & Exchange ---
 
	return [
	  'name'          => $name,
	  'exchange'      => $exchange,
	  'score'         => (int)$best,
	  'score_percent' => (int)$best,
	  'score_label'   => $bestM ? (strtoupper(date('M', mktime(0,0,0,$bestM,1))).' '.$best.'%') : '--',
	  'data'          => [
		'threshold_percent' => $thresholdPercent,
		'months_selected'   => $months,
		'min_years'         => $minYears,
		'direction'         => $direction,
		'years_by_month'    => $yearsByMonth,
		'winrates_percent'  => array_map(fn($s)=>isset($s['hitRate'])?(int)round($s['hitRate']):null, $stats),
		'qualified_months'  => $qualified,
	  ],
	];
 }