<?php
// api_helpers_multi_fetch.php

/**
 * Führt mehrere parallele API-Requests aus (via curl_multi).
 * @param array $urls    ['alias' => 'https://api.url']
 * @param int   $timeout Timeout je Request in Sekunden
 * @return array         ['alias' => decoded_json_array]
 */
if (!function_exists('fetch_multi')) {
	function fetch_multi(array $urls, int $timeout = 10): array {
		if (empty($urls)) return [];

		$multiHandle = curl_multi_init();
		$curlHandles = [];
		$responses   = [];

		foreach ($urls as $alias => $url) {
			$ch = curl_init();
			curl_setopt_array($ch, [
				CURLOPT_URL            => $url,
				CURLOPT_RETURNTRANSFER => true,
				CURLOPT_FOLLOWLOCATION => true,
				CURLOPT_CONNECTTIMEOUT => $timeout,
				CURLOPT_TIMEOUT        => $timeout,
				CURLOPT_SSL_VERIFYPEER => true,
				CURLOPT_SSL_VERIFYHOST => 2,
			]);
			curl_multi_add_handle($multiHandle, $ch);
			$curlHandles[$alias] = $ch;
		}

		// Ausführen
		$active = null;
		do {
			$mrc = curl_multi_exec($multiHandle, $active);
			if ($mrc === CURLM_OK) {
				// Warten auf I/O, um Busy-Loop zu vermeiden
				curl_multi_select($multiHandle, 1.0);
			}
		} while ($active && $mrc === CURLM_OK);

		// Einsammeln
		foreach ($curlHandles as $alias => $ch) {
			$raw = curl_multi_getcontent($ch);
			$data = json_decode($raw, true);
			$responses[$alias] = is_array($data) ? $data : [];

			curl_multi_remove_handle($multiHandle, $ch);
			curl_close($ch);
		}

		curl_multi_close($multiHandle);
		return $responses;
	}
}

/**
 * Wrap um fetch_multi() mit Mini-Cache (APCu optional) & Retries (Exponential Backoff).
 * @param array $urls     ['alias' => 'https://api.url']
 * @param int   $timeout  Timeout je Request
 * @param int   $retries  Anzahl zusätzlicher Versuche bei leerer Antwort
 * @param int   $cacheTtl Sekunden Cache-Lebensdauer
 * @return array          ['alias' => decoded_json_array]
 */
if (!function_exists('fetch_multi_safe')) {
	function fetch_multi_safe(array $urls, int $timeout = 10, int $retries = 1, int $cacheTtl = 60): array {
		static $memCache = []; // pro PHP-Prozess
		$now = time();

		// Deduplizieren (gleiche URL nur einmal holen)
		$aliasToUrl   = [];
		$urlToAliases = [];
		foreach ($urls as $alias => $url) {
			$aliasToUrl[$alias] = $url;
			$urlToAliases[$url][] = $alias;
		}

		$results = [];
		$toFetch = [];

		// Cache prüfen (APCu bevorzugt, sonst in-memory)
		foreach ($urlToAliases as $url => $aliases) {
			$hit = null;

			if (function_exists('apcu_fetch')) {
				$apcKey = 'fmp:' . md5($url);
				$tmp = apcu_fetch($apcKey, $ok);
				if ($ok && is_array($tmp) && isset($tmp['ts']) && ($now - $tmp['ts'] <= $cacheTtl)) {
					$hit = $tmp;
				}
			}

			if (!$hit && isset($memCache[$url]) && ($now - $memCache[$url]['ts'] <= $cacheTtl)) {
				$hit = $memCache[$url];
			}

			if ($hit) {
				foreach ($aliases as $a) $results[$a] = $hit['data'];
			} else {
				$toFetch[$url] = $url;
			}
		}

		// Netz-Fetch (mit Retries)
		if ($toFetch) {
			$attempt = 0;
			$pending = $toFetch;
			$fetched = [];

			do {
				$attempt++;

				// fetch_multi erwartet alias=>url; wir nutzen hier die URL selbst als Alias
				$batch = [];
				foreach ($pending as $url) $batch[$url] = $url;

				$chunk = fetch_multi($batch, $timeout);

				foreach ($chunk as $urlKey => $data) {
					$fetched[$urlKey] = $data;
				}

				$needRetry = [];
				foreach ($pending as $url) {
					$ok = isset($fetched[$url]) && is_array($fetched[$url]) && $fetched[$url] !== [];
					if (!$ok) $needRetry[$url] = $url;
				}

				if ($needRetry && $attempt <= $retries) {
					// Exponential Backoff: 200ms, 400ms, 800ms, ...
					usleep(200000 * (1 << ($attempt - 1)));
					$pending = $needRetry;
				} else {
					break;
				}
			} while ($attempt <= $retries);

			// Cache füllen + auf ursprüngliche Aliase zurückmappen
			foreach ($toFetch as $url) {
				$payload = [
					'ts'   => $now,
					'data' => $fetched[$url] ?? [],
				];
				$memCache[$url] = $payload;

				if (function_exists('apcu_store')) {
					$apcKey = 'fmp:' . md5($url);
					@apcu_store($apcKey, $payload, $cacheTtl);
				}

				foreach ($urlToAliases[$url] as $alias) {
					$results[$alias] = $payload['data'];
				}
			}
		}

		return $results;
	}
}