<?php
require_once __DIR__ . '/engine/engine.php';
require_once __DIR__ . '/engine/api-handling/api_helpers.php';
$requires_search = true;
$requires_chartjs = true;
$requires_dataTables = true;
require_once __DIR__ . '/views/header.php';
require_once __DIR__ . '/views/navigation.php';

$tickerFiles = [];
foreach (glob(__DIR__ . '/execute/tickers/ticker-listen/*.json') as $filepath) {
	$basename = basename($filepath);
	$count = 0;

	$json = @file_get_contents($filepath);
	if ($json) {
		$data = json_decode($json, true);
		if (is_array($data)) {
			$count = count($data);
		}
	}

	$tickerFiles[] = ['file' => $basename, 'count' => $count];
}
?>

<main class="flex-grow-1">
	<div class="container my-5">
		<div class="row">
			<div class="col-12">
				<h1>Aktien-Scanner</h1>
				<div class="card mt-4">
					<div class="card-body">
						<h5 class="card-title">Ticker-Scan starten</h5>
						
						<form id="scanForm">
							<div class="row g-3">
								<div class="col-md-3">
									<label for="source_file" class="form-label">Tickerliste wählen</label>
									<select class="form-select" name="source_file" id="source_file" required>
										<option value="" disabled selected>Bitte auswählen …</option>
										<?php foreach ($tickerFiles as $entry): ?>
											<option value="<?= htmlspecialchars($entry['file']) ?>">
												<?= htmlspecialchars($entry['file']) ?> (<?= $entry['count'] ?>)
											</option>
										<?php endforeach; ?>
									</select>
								</div>
								
								<div class="col-md-3">
								  <label for="start_chunk" class="form-label">Start-Chunk</label>
								  <input type="number" class="form-control" name="start_chunk" id="start_chunk" value="1" min="1">
								</div>
						
								<div class="col-md-3">
									<label for="method" class="form-label">Bewertungsmethode</label>
									<select class="form-select" name="method" id="method" onchange="toggleMethodFields()">
										<option value="fundamentale-kriterien" selected>Fundamentale Kriterien</option>
										<option value="oversold">Oversold (technisch)</option>
										<option value="saisonalitaet">Saisonalität</option>
										<option value="saisonalitaet100">Saisonalität 100 (Monats-Winrate)</option>
										<option value="minervini">Minervini Kriterien</option>
										<option value="strategic-profiles">Strategische Profile</option>
										<option value="chart-whale-patterns">Chart Whale Patterns</option>
									</select>
								</div>
								
								<div class="col-md-3 method-field" data-method="fundamentale-kriterien">
									<label for="method" class="form-label">%-Schwelle</label>
									<input type="number" class="form-control" name="min_score_percent" min="0" max="100" value="50">
								</div>
								
								<div class="col-md-3 method-field" data-method="oversold">
									<label for="oversold_direction" class="form-label">Signalrichtung</label>
									<select class="form-select" name="oversold_direction" id="oversold_direction">
										<option value="buy" selected>Oversold</option>
										<option value="sell">Overbought</option>
									</select>
								</div>
								
								<div class="col-md-3 method-field" data-method="saisonalitaet">
									<label for="saison_months" class="form-label">Zeitraum (Monate)</label>
									<input type="number" class="form-control" name="saison_months" id="saison_months" value="3" min="1" max="12">
								</div>
								
								<div class="col-md-3 method-field" data-method="saisonalitaet100">
									<label class="form-label">Schwelle</label>
									<select class="form-select" name="threshold_percent">
										<option value="100" selected>100%</option>
										<option value="95">≥ 95%</option>
										<option value="90">≥ 90%</option>
										<option value="85">≥ 85%</option>
										<option value="80">≥ 80%</option>
										<option value="75">≥ 75%</option>
										<option value="50">≥ 50%</option>
										<option value="25">≥ 25%</option>
									</select>
								</div>
								
								<div class="col-md-3 method-field" data-method="minervini">
									<label for="score_min" class="form-label">Minimale Punktzahl (von 14):</label>
									<select class="form-select" name="score_min">
										<?php for ($i = 5; $i <= 14; $i++) echo "<option value='$i'" . ($i == 10 ? " selected" : "") . ">$i Punkte</option>"; ?>
									</select>
								</div>
								
								<div class="col-md-3 method-field" data-method="strategic-profiles">
									<label class="form-label">Profil</label>
									<select class="form-select" name="strategic-profile">
										<option value="high_performer" selected>High-Performer (Growth+Momentum)</option>
										<option value="solide">Solide Investments (Quality/Defensive)</option>
										<option value="ertragreich">Ertragreiche Investments (Value/GARP)</option>
									</select>
								</div>
								<div class="col-md-3 method-field" data-method="strategic-profiles">
									<label class="form-label">%-Schwelle</label>
									<select class="form-select" name="min_score_percent">
										<option value="100" selected>100%</option>
										<option value="95">≥ 95%</option>
										<option value="90">≥ 90%</option>
										<option value="85">≥ 85%</option>
										<option value="80">≥ 80%</option>
										<option value="75">≥ 75%</option>
										<option value="50">≥ 50%</option>
										<option value="25">≥ 25%</option>
									</select>
								</div>
								<div class="col-md-3 method-field" data-method="strategic-profiles">
									<label class="form-check form-switch">
										<input class="form-check-input" type="checkbox" name="premium_flags">
										<span class="form-check-label">FMP Premium-Flags</span>
									</label>
								</div>
								
								<div class="col-md-3 method-field" data-method="chart-whale-patterns">
									<label class="form-label">Score-Schwelle</label>
									<select class="form-select" name="min_score_whales">
										<option value="100" selected>100</option>
										<option value="95">≥ 95</option>
										<option value="90">≥ 90</option>
										<option value="85">≥ 85</option>
										<option value="80">≥ 80</option>
										<option value="75">≥ 75</option>
										<option value="50">≥ 50</option>
										<option value="25">≥ 25</option>
									</select>
								</div>
								
							</div>
						
							<div class="mt-4">
								<button type="submit" class="btn btn-dark w-100" id="startScanBtn">Scan starten</button>
							</div>
							<div class="mt-2">
								<button type="button" id="resumeScanBtn" class="btn btn-outline-secondary w-100" style="display: none;">Scan fortsetzen</button>
							</div>
							<div class="mt-2" id="cancelScanArea" style="display: none;">
								<button type="button" class="btn btn-outline-danger w-100" id="abortScanBtn">Scan abbrechen</button>
							</div>
						</form>
						
						<div class="mt-4" id="loadingArea" style="display: none;">
							<div class="d-flex align-items-center">
								<div class="spinner-border spinner-border-sm text-black me-2" role="status"></div>
								<span>Scan läuft …</span>
								<div id="scanTimer" class="ms-3 small text-muted">Laufzeit: 0 Sekunden</div>
							</div>
						</div>
						
						<div class="mt-3" id="scanProgressContainer" style="display: none;">
							<div class="d-flex justify-content-between align-items-center mb-1">
								<span>Fortschritt:</span>
								<span id="progressText">Chunk wird gesendet...</span>
							</div>
							<div class="progress mt-0" style="height: 18px;" id="scanProgressBarWrapper">
								<div id="scanProgressBar" class="progress-bar progress-bar-animated bg-black" role="progressbar" style="width: 0%"></div>
							</div>
						</div>
						<div id="scanChunkLog" class="mt-2 small text-muted">
							<div id="statusLine"></div>
							<div id="errorList" class="mt-2"></div>
						</div>
						
						<div class="mt-4" id="scanSummary" style="display: none;">
							<div class="alert alert-success" role="alert"></div>
						</div>
						
						<div class="table-responsive mt-4" style="display: none;" id="scanTableWrapper">
							<table class="table table-striped mb-0" id="scanTable">
								<thead class="table-dark">
									<tr>
										<th>Status</th>
										<th>Ticker</th>
										<th>Börse</th>
										<th>Score</th>
										<th>Name</th>
										<th>Ergebnis</th>
									</tr>
								</thead>
								<tbody></tbody>
							</table>
						</div>
						
					</div>
				</div>
				
				<div class="card mt-3">
					<div class="card-body">
						<div class="d-flex justify-content-between align-items-center mb-3">
							<h5 class="card-title mb-0 d-flex align-items-center gap-2 flex-wrap">
								<span>Gespeicherte Ticker</span>
								<span id="resultsCount" class="badge bg-light text-dark rounded-pill">0</span>
							</h5>
							<small id="scanIdDisplay" class="text-muted" style="display:none;">Scan-ID: -</small>
							<div>
								<div class="dropdown mb-3 me-2 d-inline-block">
									<button class="btn btn-outline-success btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
										Export als JSON
									</button>
									<ul class="dropdown-menu">
										<li><a class="dropdown-item" href="execute/aktien-scanner/export_results.php" download>Alle Methoden</a></li>
										<li><a class="dropdown-item" href="execute/aktien-scanner/export_results.php?method=fundamentale-kriterien" download>Nur Fundamentale Kriterien</a></li>
										<li><a class="dropdown-item" href="execute/aktien-scanner/export_results.php?method=oversold" download>Nur Oversold</a></li>
										<li><a class="dropdown-item" href="execute/aktien-scanner/export_results.php?method=saisonalitaet" download>Nur Saisonalität</a></li>
										<li><a class="dropdown-item" href="execute/aktien-scanner/export_results.php?method=saisonalitaet100" download>Nur Saisonalität 100</a></li>
										<li><a class="dropdown-item" href="execute/aktien-scanner/export_results.php?method=minervini" download>Nur Minervini</a></li>
									</ul>
								</div>
								<button class="btn btn-sm btn-outline-danger d-inline-block" id="deleteAllBtn">Alle löschen</button>
							</div>
						</div>
						<div class="table-responsive" id="resultsTableWrapper">
							<table class="table table-striped mb-0" id="resultsTable">
								<thead class="table-dark">
								<tr>
									<th>Ticker</th>
									<th>Name</th>
									<th>Börsenplatz</th>
									<th>Score</th>
									<th>Methode</th>
									<th>Datum</th>
									<th></th>
								</tr>
								</thead>
								<tbody></tbody>
							</table>
						</div>
					</div>
				</div>
				
			</div>
		</div>
	
	</div>
</main>

<script>
	function toggleMethodFields() {
		const method = document.getElementById("method").value;
		const allFields = document.querySelectorAll(".method-field");
		
		allFields.forEach(field => {
			const methods = field.dataset.method.split(",");
			field.style.display = (methods.includes(method) || methods.includes("default")) ? "block" : "none";
			const controls = field.querySelectorAll('input,select,textarea,button');
			const active = (methods.includes(method) || methods.includes("default"));
			controls.forEach(el => el.disabled = !active);
		});
	}
	
	document.addEventListener("DOMContentLoaded", toggleMethodFields);
	document.getElementById("method").addEventListener("change", toggleMethodFields);
</script>

<script>
	document.addEventListener("DOMContentLoaded", function () {
		const tableElement = $('#resultsTable');
		let dataTable;

		// === DataTables initialisieren ===
		dataTable = tableElement.DataTable({
			serverSide: true,
			processing: true,
			ajax: {
				url: 'execute/aktien-scanner/load_scan_results.php',
				type: 'GET',
				data: function (d) {
					d.scan_id = window.currentScanId || '';
				}
			},
			columns: [
				{ data: "ticker", title: "Ticker" },
				{ data: "name", title: "Name" },
				{ data: "exchange", title: "Börsenplatz" },
				{ data: "score", title: "Score" },
				{ data: "method", title: "Methode" },
				{ data: "created_at", title: "Datum" },
				{
					data: "id",
					orderable: false,
					render: function (data, type, row) {
						return `<button class="btn btn-link text-danger p-0" onclick="deleteResult(${data})"><small>löschen</small></button>`;
					}
				}
			],
			order: [[3, 'desc']],
			pageLength: 50,
			lengthMenu: [[25, 50, 100, -1], [25, 50, 100, "Alle"]],
			stripeClasses: ['table-light', 'table-white'],
			language: {
				url: "assets/js/data-tables_de-DE.json"
			}
		});

		window.loadResults = function () {
			if (dataTable) {
				dataTable.ajax.reload(null, false);
		
				// Treffer-Zähler global abrufen (ALLE Einträge in scan_results)
				fetch('execute/aktien-scanner/load_scan_results.php?draw=1&start=0&length=1')
					.then(res => res.json())
					.then(data => {
						const total = data.recordsTotalGlobal ?? 0;
						const badge = document.getElementById("resultsCount");
						if (badge) badge.textContent = total;
					})
					.catch(err => console.error("Fehler beim Abrufen der Trefferanzahl:", err));
			}
		};
		window.loadResults();
	});
</script>

<script>
	function deleteResult(id) {
		if (!confirm("Eintrag wirklich löschen?")) return;
		fetch("execute/aktien-scanner/delete_result.php", {
			method: "POST",
			body: new URLSearchParams({ id })
		}).then(() => loadResults()); // <- nutzt window.loadResults
	}

	document.getElementById("deleteAllBtn").addEventListener("click", function () {
		if (!confirm("Wirklich alle Treffer löschen?")) return;
		fetch("execute/aktien-scanner/delete_all_results.php", { method: "POST" })
			.then(() => loadResults());
	});
</script>

<script>
  // === Scan-Konfiguration ===================
  let SCAN_CONFIG = {};
  function setScanConfig(method) {
	switch (method) {
	  case 'fundamentale-kriterien':
		SCAN_CONFIG = { chunkSize: 12, delayPerRequestMs: 290, maxChunkDelayMs: 7000 }; break;
	  case 'oversold':
		SCAN_CONFIG = { chunkSize: 300, delayPerRequestMs: 100, maxChunkDelayMs: 1000 }; break;
	  case 'saisonalitaet':
		SCAN_CONFIG = { chunkSize: 300, delayPerRequestMs: 100, maxChunkDelayMs: 1000 }; break;
	  case 'saisonalitaet100':
		SCAN_CONFIG = { chunkSize: 300, delayPerRequestMs: 100, maxChunkDelayMs: 1000 }; break;
	  case 'minervini':
		SCAN_CONFIG = { chunkSize: 300, delayPerRequestMs: 100, maxChunkDelayMs: 1000 }; break;
	  case 'strategic-profiles':
	  	SCAN_CONFIG = { chunkSize: 11, delayPerRequestMs: 290, maxChunkDelayMs: 7000 }; break;
	   case 'chart-whale-patterns':
		SCAN_CONFIG = { chunkSize: 300, delayPerRequestMs: 100, maxChunkDelayMs: 1000 }; break;
	  default:
		SCAN_CONFIG = { chunkSize: 300, delayPerRequestMs: 100, maxChunkDelayMs: 1000 };
	}
  }

  // =====================================================
  const scanForm   = document.getElementById('scanForm');
  const startBtn   = document.getElementById('startScanBtn');
  const abortBtn   = document.getElementById('abortScanBtn');
  const cancelArea = document.getElementById('cancelScanArea');
  const seenRows   = new Set();
  const resumeBtn  = document.getElementById('resumeScanBtn');
  const sourceFileEl = document.getElementById('source_file');

  let isAborted = false;
  let scanTimerInterval = null;
  let totalHits = 0;
  let scannedCount = 0;
  
  let userTouchedStartChunk = false;
  const startChunkEl = document.getElementById('start_chunk');
  startChunkEl?.addEventListener('input', () => { userTouchedStartChunk = true; });

  // 1) Default-Methode wählen, falls noch keine gesetzt ist
  (function ensureDefaultMethodSelected() {
	const checked = document.querySelector('input[name="method"]:checked');
	if (!checked) {
	  const firstRadio = document.querySelector('input[name="method"]');
	  if (firstRadio) firstRadio.checked = true;
	}
	const sel = document.querySelector('select[name="method"]');
	if (sel && !sel.value && sel.options.length) sel.value = sel.options[0].value;
  })();

  // 2) Start-Chunk beim Laden aus localStorage vorschlagen
  (function initResumeStartChunkOnLoad() {
	try {
	  const startChunkEl = document.getElementById('start_chunk');
	  if (!startChunkEl || !sourceFileEl) return;
  
	  const getMethod = () => {
		const checked = document.querySelector('input[name="method"]:checked');
		if (checked) return checked.value;
		const sel = document.querySelector('select[name="method"]');
		return sel ? sel.value : '';
	  };
  
	  const sourceFile = sourceFileEl.value;
	  const method = getMethod();
	  if (!sourceFile || !method) return;
  
	  startChunkEl.removeAttribute('max'); // max erst später setzen
  
	  const resumeKey = `scanner:resume:${sourceFile}:${method}`;
	  const lastDone = parseInt(localStorage.getItem(resumeKey) || '0', 10);
  
	  // Nur setzen, wenn es wirklich Fortschritt gibt
	  if (!Number.isNaN(lastDone) && lastDone > 0) {
		startChunkEl.value = String(lastDone + 1);
	  }
	} catch (e) {
	  console.warn("Resume-StartChunk-Init fehlgeschlagen:", e);
	}
  })();

  // === Resume-Helpers =========================================================
  function getMethodValue() {
	const checked = document.querySelector('input[name="method"]:checked');
	if (checked) return checked.value;
	const sel = document.querySelector('select[name="method"]');
	return sel ? sel.value : '';
  }
  function getResumeKeys(sourceFile, method) {
	return {
	  lastKey: `scanner:resume:${sourceFile}:${method}`,
	  metaKey: `scanner:resumeMeta:${sourceFile}:${method}`
	};
  }
  function readJSON(key) {
	try { return JSON.parse(localStorage.getItem(key) || '{}'); } catch { return {}; }
  }

  function updateResumeBtnVisibility() {
	if (!resumeBtn || !sourceFileEl) return;
	const sourceFile = sourceFileEl.value;
	const method = getMethodValue();
	if (!sourceFile || !method) { resumeBtn.style.display = 'none'; return; }
  
	const { lastKey, metaKey } = getResumeKeys(sourceFile, method);
	const lastDone = parseInt(localStorage.getItem(lastKey) || '0', 10);
	const meta = readJSON(metaKey);
	const total = parseInt(meta.totalChunks || '0', 10);
	const completed = !!meta.completed;
  
	const hasProgress = lastDone > 0; // es gab schon Fortschritt
	const hasWorkLeft = total > 0 && lastDone < total; // noch nicht alles fertig
  
	if (!hasProgress || !hasWorkLeft || completed) {
	  resumeBtn.style.display = 'none';
	} else {
	  resumeBtn.style.display = 'inline-block';
	  resumeBtn.title = `Fortsetzen ab Chunk ${lastDone + 1} von ${total}`;
	}
  }

  document.addEventListener('DOMContentLoaded', updateResumeBtnVisibility);

  // Klickhandler: start_chunk setzen + Scan starten
  if (resumeBtn) {
	resumeBtn.addEventListener('click', () => {
	  const status = document.getElementById('statusLine');
	  if (status) status.textContent = '';
	  const err = document.getElementById('errorList');
	  if (err) err.innerHTML = '';	
	  
	  const sourceFile = sourceFileEl ? sourceFileEl.value : '';
	  const method = getMethodValue();
	  if (!sourceFile || !method) return;
  
	  const { lastKey, metaKey } = getResumeKeys(sourceFile, method);
	  const lastDone = parseInt(localStorage.getItem(lastKey) || '0', 10);
	  const meta = readJSON(metaKey);
	  const total = parseInt(meta.totalChunks || '0', 10);
  
	  if (!total || lastDone >= total || meta.completed) {
		updateResumeBtnVisibility();
		return;
	  }
  
	  const nextChunk = Math.min(lastDone + 1, total);
	  const startChunkEl = document.getElementById('start_chunk');
	  if (startChunkEl) startChunkEl.value = String(nextChunk);
  
	  if (scanForm && typeof scanForm.requestSubmit === 'function') {
		scanForm.requestSubmit();
	  } else if (scanForm) {
		scanForm.submit();
	  }
	});
  }
  
  // ---- Debounce, Cache & Abort ----
  function debounce(fn, wait = 250) {
	let t;
	return (...args) => {
	  clearTimeout(t);
	  t = setTimeout(() => fn(...args), wait);
	};
  }
  
  const debouncedPrecompute = debounce(precomputeMaxChunks, 250);
  
  const PRECOMP_CACHE = new Map(); // key: `${sourceFile}|${method}|${chunkSize}` -> { totalChunks, ts }
  let precompAbortController = null;
  
  // ---------- Chunk-Max vorab berechnen ----------
  async function precomputeMaxChunks() {
	const startChunkEl = document.getElementById('start_chunk');
	if (!startChunkEl || !sourceFileEl) return;
  
	const sourceFile = sourceFileEl.value;
	const method = getMethodValue();
	if (!sourceFile || !method) return;
  
	// aktuelle ChunkSize zur Methode laden
	setScanConfig(method);
	const chunkSize = SCAN_CONFIG.chunkSize;
	const cacheKey = `${sourceFile}|${method}|${chunkSize}`;
  
	// --- Cache-Hit? Schnell setzen & raus ---
	if (PRECOMP_CACHE.has(cacheKey)) {
	  const cached = PRECOMP_CACHE.get(cacheKey);
	  const totalChunks = cached.totalChunks;
	  startChunkEl.setAttribute('max', String(totalChunks));
	  if ((!startChunkEl.value || Number(startChunkEl.value) < 1 || Number(startChunkEl.value) > totalChunks) && !userTouchedStartChunk) {
		startChunkEl.value = '1';
	  }
	  // Meta updaten
	  const { metaKey } = getResumeKeys(sourceFile, method);
	  const meta = readJSON(metaKey);
	  localStorage.setItem(metaKey, JSON.stringify({
		...meta,
		totalChunks,
		chunkSize,
		completed: false,
		ts: Date.now()
	  }));
	  updateResumeBtnVisibility?.();
	  return;
	}
  
	// --- laufenden Request abbrechen, falls vorhanden ---
	if (precompAbortController) {
	  precompAbortController.abort();
	}
	precompAbortController = new AbortController();
  
	try {
	  const res = await fetch(`execute/tickers/ticker-listen/${sourceFile}`, { signal: precompAbortController.signal });
	  const tickers = await res.json();
	  const totalChunks = Math.max(1, Math.ceil(tickers.length / chunkSize));
  
	  // Ergebnis cachen
	  PRECOMP_CACHE.set(cacheKey, { totalChunks, ts: Date.now() });
  
	  // max setzen & ggf. korrigieren
	  startChunkEl.setAttribute('max', String(totalChunks));
	  if (!startChunkEl.value || Number(startChunkEl.value) < 1 || Number(startChunkEl.value) > totalChunks) {
		startChunkEl.value = '1';
	  }
  
	  // Meta im LocalStorage aktualisieren
	  const { metaKey } = getResumeKeys(sourceFile, method);
	  const meta = readJSON(metaKey);
	  localStorage.setItem(metaKey, JSON.stringify({
		...meta,
		totalChunks,
		chunkSize,
		completed: false,
		ts: Date.now()
	  }));
  
	  updateResumeBtnVisibility?.();
	} catch (e) {
	  if (e.name === 'AbortError') {
		// ignorieren: es kam bereits eine neuere Anfrage
		return;
	  }
	  console.warn('precomputeMaxChunks() fehlgeschlagen:', e);
	} finally {
	  // Controller nur löschen, wenn es unserer war
	  precompAbortController = null;
	}
  }
  
  // Beim Laden: nur ausführen, wenn schon Datei & Methode gewählt sind
  document.addEventListener('DOMContentLoaded', () => {
	const hasSource = !!(document.querySelector('[name="source_file"]')?.value);
	const methodChecked = document.querySelector('input[name="method"]:checked');
	const methodSelected = document.querySelector('select[name="method"]')?.value;
	const hasMethod = !!(methodChecked ? methodChecked.value : methodSelected);
	if (hasSource && hasMethod) debouncedPrecompute();
  });
  
  // ===================== SCAN START =====================
  function logStatus(msg) {
	let status = document.getElementById('statusLine');
	if (!status) {
	  const wrap = document.getElementById('scanChunkLog');
	  status = document.createElement('div'); status.id = 'statusLine';
	  wrap.appendChild(status);
	}
	status.textContent = msg;
  }
  function logError(msg) {
	let list = document.getElementById('errorList');
	if (!list) {
	  const wrap = document.getElementById('scanChunkLog');
	  list = document.createElement('div'); list.id = 'errorList';
	  wrap.appendChild(list);
	}
	const row = document.createElement('div'); row.className = 'text-danger';
	row.textContent = msg; list.appendChild(row);
  }
  
  scanForm.onsubmit = async (e) => {
	e.preventDefault();
	const _s = document.getElementById('statusLine'); if (_s) _s.textContent = '';
	const _e = document.getElementById('errorList'); if (_e) _e.innerHTML = '';
	isAborted = false;
	startBtn.disabled = true;
	cancelArea.style.display = 'block';

	document.getElementById("loadingArea").style.display = "block";
	document.querySelector("#scanTable tbody").innerHTML = "";
	seenRows.clear();

	abortBtn.style.display = "block";
	abortBtn.disabled = false;
	abortBtn.textContent = "Scan abbrechen";

	const formData = new FormData(scanForm);
	const sourceFile = formData.get('source_file') ?? "";
	const method = formData.get('method');
	setScanConfig(method);

	if (!sourceFile) {
	  alert("Bitte eine Tickerliste auswählen!");
	  startBtn.disabled = false;
	  return;
	}

	// 1. Scan vorbereiten
	const initRes = await fetch('execute/aktien-scanner/scan_init.php', {
	  method: 'POST',
	  body: formData,
	  credentials: 'same-origin'
	});
	const initText = await initRes.text();
	let initData;
	try { initData = JSON.parse(initText); }
	catch (err) {
	  console.error("Fehler beim Parsen der Antwort aus scan_init.php:", err, initText);
	  alert("Fehler beim Initialisieren des Scans.");
	  startBtn.disabled = false;
	  cancelArea.style.display = 'none';
	  return;
	}
	if (!initData.scan_id) {
	  alert("Fehler beim Starten des Scans.");
	  startBtn.disabled = false;
	  cancelArea.style.display = 'none';
	  return;
	}
	const scanId = initData.scan_id;
	window.currentScanId = scanId;

	const scanIdBadge = document.getElementById("scanIdDisplay");
	if (scanIdBadge) {
	  scanIdBadge.style.display = "inline-block";
	  scanIdBadge.textContent = `Scan-ID: ${scanId}`;
	}
	if (typeof loadResults === "function") loadResults();

	// 2. Ticker laden
	const response = await fetch(`execute/tickers/ticker-listen/${sourceFile}`);
	const tickers = await response.json();

	// 4. Chunk-Verarbeitung
	const chunkSize = SCAN_CONFIG.chunkSize;
	const chunks = [];
	for (let i = 0; i < tickers.length; i += chunkSize) {
	  const chunk = tickers.slice(i, i + chunkSize);
	  if (Array.isArray(chunk) && chunk.length > 0) chunks.push(chunk);
	}
	if (chunks.length === 0) {
	  alert("Tickerliste ist leer oder konnte nicht verarbeitet werden.");
	  startBtn.disabled = false;
	  cancelArea.style.display = 'none';
	  return;
	}

	// Resume-Meta speichern (noch nicht completed)
	try {
	  const { metaKey } = getResumeKeys(sourceFile, method);
	  localStorage.setItem(metaKey, JSON.stringify({
		totalChunks: chunks.length,
		chunkSize: SCAN_CONFIG.chunkSize, // <— hinzufügen
		completed: false,
		ts: Date.now()
	  }));
	  const startChunkEl = document.getElementById('start_chunk');
	  if (startChunkEl) startChunkEl.setAttribute('max', String(chunks.length));
	  updateResumeBtnVisibility();
	} catch {}

	const progressBar  = document.getElementById("scanProgressBar");
	const progressText = document.getElementById("progressText");

	// --- Fortsetzen ab bestimmtem Chunk ---
	const startChunkInput = formData.get('start_chunk') || '1';
	let startChunk = parseInt(startChunkInput, 10);
	if (Number.isNaN(startChunk) || startChunk < 1) startChunk = 1;

	const startIndex = Math.max(0, Math.min(chunks.length - 1, startChunk - 1));
	if (startIndex > 0) {
	  const initPercent = Math.round((startIndex / chunks.length) * 100);
	  if (progressBar) {
		progressBar.style.width = `${initPercent}%`;
		progressBar.textContent = `${initPercent}%`;
	  }
	  if (progressText) {
		progressText.textContent = `Fortsetzen ab Chunk ${startChunk} von ${chunks.length}`;
	  }
	}

	const scanStartTime = Date.now();
	const scanTimer = document.getElementById("scanTimer");
	const formatDuration = (seconds) => {
	  const h = Math.floor(seconds / 3600);
	  const m = Math.floor((seconds % 3600) / 60);
	  const s = Math.floor(seconds % 60);
	  const parts = [];
	  if (h > 0) parts.push(`${h} h`);
	  if (m > 0 || h > 0) parts.push(`${m} min`);
	  parts.push(`${s} s`);
	  return parts.join(' ');
	};
	const updateTimer = () => {
	  const elapsedSec = Math.max((Date.now() - scanStartTime) / 1000, 1);
	  const speed = scannedCount / elapsedSec;
	  const remaining = tickers.length - scannedCount;
	  const etaSec = speed > 0 ? remaining / speed : 0;
	  let text = `Laufzeit: ${formatDuration(elapsedSec)}`;
	  text += ` | ${speed.toFixed(1)} Ticker/s`;
	  if (scannedCount > 0 && speed > 0) text += ` | noch ca. ${formatDuration(etaSec)}`;
	  if (scanTimer) scanTimer.textContent = text;
	};
	updateTimer();
	scanTimerInterval = setInterval(updateTimer, 1000);
	document.getElementById("scanProgressContainer").style.display = "block";

	for (let i = startIndex; i < chunks.length; i++) {
	  if (isAborted) break;

	  const currentChunk = Array.isArray(chunks[i]) ? chunks[i] : [chunks[i]];
	  const isValid = currentChunk.length > 0 && currentChunk.every(t =>
		(typeof t === "string" && t.trim()) ||
		(typeof t === "object" && (t.symbol || t.Code))
	  );
	  if (!isValid) {
		console.warn(`Überspringe ungültigen Chunk ${i + 1}/${chunks.length}:`, currentChunk);
		continue;
	  }

	  let chunkString;
	  try { chunkString = JSON.stringify(currentChunk); }
	  catch (err) { console.error("Fehler beim Serialisieren von Chunk:", currentChunk, err); continue; }

	  const payload = new FormData();
	  payload.append('source_file', sourceFile);
	  payload.append('method', formData.get('method'));
	  payload.append('methode', formData.get('method'));
	  payload.append('min_score_percent', formData.get('min_score_percent') ?? 50);
	  payload.append('oversold_direction', formData.get('oversold_direction') ?? 'buy');
	  payload.append('saison_months', formData.get('saison_months') ?? 3);
	  payload.append('score_min', formData.get('score_min') ?? 10);
	  payload.append('scan_id', scanId);
	  payload.append('chunkIndex', i + 1);
	  payload.append('chunkTotal', chunks.length);
	  payload.append('tickers_json', chunkString);
	  
	  if (formData.get('method') === 'saisonalitaet100') {
		// Monate optional: nur anhängen, wenn ein months[]-UI existiert
		const months = formData.getAll('months[]');
		months.forEach(m => payload.append('months[]', m));
	  
		// Schwelle/Filter
		const thr = formData.get('threshold_percent') || 100;
		payload.append('threshold_percent', thr);
	  
		// Qualität & Richtung (falls keine Felder vorhanden sind, greifen die Defaults)
		payload.append('min_years', formData.get('min_years') || 7);
		payload.append('direction', formData.get('season_direction') || 'long');
		payload.append('max_years_back', formData.get('max_years_back') || 20);
	  
		// WICHTIG: zentrale Filter-Schwelle an den Server spiegeln
		payload.set('min_score_percent', thr);
	  }
	  
	  if (formData.get('method') === 'chart-whale-patterns') {
		payload.append('min_score_whales', formData.get('min_score_whales') ?? '80');
	  }

	  try {
		const res = await fetch('execute/aktien-scanner/scan_start.php', {
		  method: 'POST',
		  body: payload,
		  credentials: 'same-origin'
		});
		// Optional: Fehler sichtbarer machen
		if (!res.ok) {
		  const errTxt = await res.text().catch(() => '');
		  throw new Error(`HTTP ${res.status}: ${res.statusText} — ${errTxt.slice(0, 200)}`);
		}

		// Fortschritt
		const percentGlobal = Math.round(((i + 1) / chunks.length) * 100);
		if (progressBar) {
		  progressBar.style.width = `${percentGlobal}%`;
		  progressBar.textContent = `${percentGlobal}%`;
		}
		if (progressText) {
		  progressText.textContent = `✓ Chunk ${i + 1} von ${chunks.length} verarbeitet`;
		}

		if (i + 1 === chunks.length) {
		  clearInterval(scanTimerInterval);
		  if (progressBar) {
			progressBar.classList.remove("progress-bar-animated");
			progressBar.classList.add("bg-success");
		  }
		}

		// Antwort (falls du debuggen willst)
		const text = await res.text();
		console.log(`Antwort für Chunk ${i + 1}/${chunks.length}:\n`, text);

		// Treffer live anzeigen
		try {
		  const resultRes = await fetch(`execute/aktien-scanner/load_scan_results.php?scan_id=${scanId}&draw=1&start=0&length=1000`);
		  const resultJson = await resultRes.json();
		  const entries = resultJson.data || [];
		  const scanTableBody = document.querySelector("#scanTable tbody");
		  entries.forEach(entry => {
			const rowId = `row-${entry.ticker}-${entry.method}`;
			if (seenRows.has(rowId)) return;
			seenRows.add(rowId);

			const scanTableWrapper = document.getElementById("scanTableWrapper");
			if (scanTableWrapper && scanTableWrapper.style.display === "none") {
			  scanTableWrapper.style.display = "block";
			}

			const row = document.createElement("tr");
			row.id = rowId;
			row.style.opacity = 0;
			row.style.transition = "opacity 0.4s ease";
			row.innerHTML = `
			  <td>✅</td>
			  <td>${entry.ticker}</td>
			  <td>${entry.exchange}</td>
			  <td>${entry.score}</td>
			  <td>${entry.name}</td>
			  <td>${entry.method}</td>
			`;
			scanTableBody.appendChild(row);
			requestAnimationFrame(() => row.style.opacity = 1);
			totalHits++;
		  });
		} catch (err) {
		  console.error("Fehler beim Anzeigen der Scan-Treffer in #scanTable:", err);
		}

		// DataTables reload
		window.currentScanId = scanId;
		if (typeof loadResults === "function") {
		  document.getElementById("resultsTableWrapper").style.display = "block";
		  loadResults();
		}

		// Chunk-Log
		logStatus(`Chunk ${i + 1}/${chunks.length} verarbeitet (${currentChunk.length} Ticker)`);

		// Delay
		const requestsInChunk = currentChunk.length * 3;
		const delay = Math.min(requestsInChunk * SCAN_CONFIG.delayPerRequestMs, SCAN_CONFIG.maxChunkDelayMs);

		// Fortschritt für Resume speichern
		try {
		  const { lastKey } = getResumeKeys(sourceFile, formData.get('method'));
		  localStorage.setItem(lastKey, String(i + 1));
		} catch {}

		scannedCount += currentChunk.length;
		await new Promise(r => setTimeout(r, delay));
	  } catch (err) {
		console.error(`Fehler beim Verarbeiten von Chunk ${i + 1}:`, err);
		logError(`Fehler in Chunk ${i + 1}/${chunks.length}: ${err?.message || 'Unbekannter Fehler'}`);
	  }
	}

	clearInterval(scanTimerInterval);
	startBtn.disabled = false;
	cancelArea.style.display = 'none';
	document.getElementById("loadingArea").style.display = "none";
	document.getElementById("scanProgressContainer").style.display = "none";
	document.getElementById("scanSummary").style.display = "block";
	logStatus("Scan abgeschlossen.");

	// Reset Progressbar
	if (document.getElementById("scanProgressBar")) {
	  const pb = document.getElementById("scanProgressBar");
	  pb.style.width = "0%";
	  pb.textContent = "0%";
	}

	// Meta als completed markieren & Button verstecken
	try {
	  const { metaKey } = getResumeKeys(sourceFile, formData.get('method'));
	  localStorage.setItem(metaKey, JSON.stringify({
		totalChunks: (typeof chunks !== 'undefined' ? chunks.length : 0),
		completed: true,
		ts: Date.now()
	  }));
	  updateResumeBtnVisibility();
	} catch {}

	// Zusammenfassung
	try {
	  const resultRes = await fetch(`execute/aktien-scanner/load_scan_results.php?scan_id=${scanId}&draw=1&start=0&length=10000`);
	  const resultJson = await resultRes.json();
	  const entries = resultJson.data || [];
	  const hitCount = entries.length;
	  const ratio = tickers.length > 0 ? Math.round((hitCount / tickers.length) * 100) : 0;
	  const alertEl = document.querySelector("#scanSummary .alert");
	  if (alertEl) {
		alertEl.className = "alert alert-success";
		alertEl.innerHTML = `Scan abgeschlossen mit <strong>${tickers.length} überprüften Titeln</strong>, <strong>${hitCount} Treffern (${ratio}%)</strong>.`;
	  }
	} catch (err) {
	  console.error("Fehler beim Abrufen der finalen Trefferanzahl:", err);
	}
  };

  // ===================== SCAN ABBRECHEN =====================
  abortBtn.onclick = () => {
	isAborted = true;
	cancelArea.style.display = 'none';
	startBtn.disabled = false;
	clearInterval(scanTimerInterval);
	document.getElementById("scanProgressContainer").style.display = "none";
	document.getElementById("loadingArea").style.display = "none";
	logStatus("Scan abgebrochen.");

	const pb = document.getElementById("scanProgressBar");
	const pt = document.getElementById("progressText");
	if (pt) pt.textContent = "Scan abgebrochen";
	if (pb) {
	  pb.classList.remove("progress-bar-animated");
	  pb.classList.add("bg-warning");
	  pb.style.width = "0%";
	  pb.textContent = "0%";
	}

	// Unvollständig markieren -> Resume-Button anzeigen (falls Arbeit übrig)
	try {
	  const methodNow = getMethodValue();
	  const { metaKey } = getResumeKeys(sourceFileEl ? sourceFileEl.value : '', methodNow);
	  const meta = readJSON(metaKey);
	  localStorage.setItem(metaKey, JSON.stringify({
		...meta,
		completed: false,
		ts: Date.now()
	  }));
	  updateResumeBtnVisibility();
	} catch {}
  };
</script>

<?php require_once __DIR__ . '/views/footer.php'; ?>