<?php

/**
 * Logic:
 *  1) We track a "lastKnownNonzeroDelay" for each schedule's runs.
 *  2) If a schedule finishes behind (some run's real_time suggests a positive delay),
 *     we carry that leftover delay into the next schedule — unless the next schedule
 *     has a non‐null time_planned, in which case we reset the delay to 0.
 *  3) We do NOT modify the schedule's time_planned fields or forcibly shift anything.
 */
function getScheduleAndRunsByCode(
	$conn,
	$competitionCode,
	$requestGroupsOnly = false,
	$nextGroupsCount   = null,
	$includeScores     = false
) {
	$response = [];

	// 1) Competition ID
	$competitionId = fetchCompetitionId($conn, $competitionCode);
	if (!$competitionId) {
		return ["error" => "No competition found with code '$competitionCode'."];
	}

	// 2) Settings
	$settings = fetchSettingsForCompetition($conn, $competitionCode);
	$runtime        = $settings['runtime']          ?? 180;
	$timebetween    = $settings['time_between']     ?? 240;
	$numberOfJudges = $settings['number_of_judges'] ?? 3;

	// 3) Schedules
	$scheduleRows = fetchSchedules($conn, $competitionId);

	$lastScheduleEndTime   = 0;
	$previousScheduleDelay = 0;
	$foundActiveSchedule   = false;

	foreach ($scheduleRows as &$scheduleRow) {
		// -- (A) If this schedule has a non‐null time_planned => reset carryover delay to 0
		//         per "If time_planned exist put the delay on 0."
		if ($scheduleRow["time_planned"] !== null) {
			$previousScheduleDelay = 0;
		}

		// -- (B) Insert break between schedules (only if not groups)
		if (!$requestGroupsOnly && $lastScheduleEndTime > 0) {
			$minStart = $lastScheduleEndTime + $timebetween;
			if ($scheduleRow["time_planned"] === null || $scheduleRow["time_planned"] < $minStart) {
				$scheduleRow["time_planned"] = $minStart;
			}
		}
		$scheduleRow["number_of_judges"] = $numberOfJudges;

		// -- (C) Fetch officials
		$scheduleRow["officials"] = getOfficialsForSchedule(
			$conn,
			$competitionId,
			$scheduleRow['event'],
			$scheduleRow['category']
		);

		// -- (D) Fetch runs
		$allRuns = getScheduleRuns($conn, $scheduleRow["schedule_id"]);
		if ($includeScores) {
			attachAverageScores($conn, $allRuns);
		}

		// Determine if schedule is fully complete => all runs status != 0
		$scheduleFullyComplete = isScheduleComplete($allRuns);

		// We'll do per-run planning if not just group IDs
		if (!$requestGroupsOnly) {
			$currentPlannedTime   = (int)$scheduleRow["time_planned"];
			$previousGroup        = null;
			$lastKnownNonzeroDelay = $previousScheduleDelay; // start with leftover from last schedule

			$previousStatus = null;
			foreach ($allRuns as $idx => &$runRow) {
				// If group changed => add timebetween
				if ($idx > 0 && $runRow['group'] != $previousGroup) {
					$currentPlannedTime += $timebetween;
				}
				$runRow["calculated_planned_time"] = $currentPlannedTime;
				$currentPlannedTime += $runtime;
				$previousGroup = $runRow['group'];

				// compute run-level delay
				$plan = (int)($runRow["calculated_planned_time"] ?? 0);
				$real = (int)$runRow["real_time"] ?: 0;
				$delay= 0;
				if ($real>0 && $plan>0) {
					$delay = $real - $plan;
					if ($delay != 0) {
						$lastKnownNonzeroDelay = $delay;
					}
				} else {
					// No real_time => keep leftover from last schedule or prior run
					$delay = $lastKnownNonzeroDelay;
				}
				$runRow["delay"] = $delay;

				// Mark active run?
				if (
					!$foundActiveSchedule &&
					!$scheduleFullyComplete &&
					(int)$runRow['status']===0 &&
					$previousStatus >= 1
				) {
					$runRow["active"] = 1;
					$foundActiveSchedule = true;
				} else {
					$runRow["active"] = 0;
				}
				$previousStatus = $runRow['status'];
			}
			unset($runRow);

			// If STILL no active run & not complete => set first as active
			if (!$foundActiveSchedule && !$scheduleFullyComplete && !empty($allRuns)) {
				$allRuns[0]["active"] = 1;
				$foundActiveSchedule  = true;
			}

			// Possibly filter next groups
			if ($nextGroupsCount!==null && $foundActiveSchedule) {
				$allRuns = filterNextGroups($allRuns, $nextGroupsCount);
			}

			// The schedule end => we set lastScheduleEndTime
			$lastScheduleEndTime = max($lastScheduleEndTime, $currentPlannedTime);

			// If we ended with a nonzero delay => pass it to the next schedule
			// (meaning we had some real_time > planned_time)
			if ($lastKnownNonzeroDelay != 0) {
				$previousScheduleDelay = $lastKnownNonzeroDelay;
			}
		} else {
			// groups only => skip run planning
			$scheduleRow["groups"] = extractUniqueGroups($allRuns);
		}

		$scheduleRow["runs"] = $allRuns;
		$response[]          = $scheduleRow;

		if ($foundActiveSchedule && $nextGroupsCount!==null) {
			break;
		}
	}
	unset($scheduleRow);

	return $response;
}


// ------------------------------------------------------------------
// HELPER FUNCTIONS
// ------------------------------------------------------------------

function fetchCompetitionId($conn, $code) {
	$q = "SELECT id FROM competitions WHERE code = ?";
	$stmt= $conn->prepare($q);
	$stmt->bind_param("s", $code);
	$stmt->execute();
	$res = $stmt->get_result();
	return $res->fetch_assoc()['id'] ?? null;
}

function fetchSettingsForCompetition($conn, $competitionCode) {
	$q = "
		SELECT runtime, judges AS number_of_judges, time_between
		FROM settings
		WHERE competition_code = ?
		LIMIT 1
	";
	$stmt = $conn->prepare($q);
	$stmt->bind_param("s", $competitionCode);
	$stmt->execute();
	$res = $stmt->get_result();
	return $res->fetch_assoc() ?? [];
}

function fetchSchedules($conn, $competitionId) {
	$q= "
		SELECT
			s.id AS schedule_id,
			s.comp,
			s.event,
			e.event_name AS event_name,
			s.category,
			s.heatsystem,
			s.`order`,
			s.time_planned,
			c.name AS category_name,
			h.round,
			h.name AS heatsystem_name,
			h.capacity,
			h.runs,
			h.system,
			o.name AS competition_name
		FROM schedule s
		JOIN heatsystem h   ON s.heatsystem = h.id
		JOIN categories c   ON s.category   = c.id
		JOIN events e       ON s.event      = e.id
		JOIN competitions o ON e.competition_code = o.code
		WHERE s.comp = ?
		ORDER BY s.`order` ASC
	";
	$stmt= $conn->prepare($q);
	$stmt->bind_param("i", $competitionId);
	$stmt->execute();
	$res= $stmt->get_result();
	return $res->fetch_all(MYSQLI_ASSOC);
}

function getOfficialsForSchedule($conn, $competitionId, $eventId, $catId) {
	$q= "
		SELECT
			op.official,
			op.judge,
			CONCAT(o.first_name, ' ', o.last_name) AS official_name,
			o.iwwf_id
		FROM officials_panel op
		JOIN officials o ON op.official = o.id
		WHERE op.competition = ?
		  AND op.event       = ?
		  AND op.category    = ?
	";
	$stmt= $conn->prepare($q);
	$stmt->bind_param("iii", $competitionId, $eventId, $catId);
	$stmt->execute();
	$res= $stmt->get_result();

	$list=[];
	while($row=$res->fetch_assoc()){
		$list[]=[
			"official_id"  =>$row['official'],
			"judge_number" =>$row['judge'],
			"official_name"=>$row['official_name'],
			"iwwf_id"      =>$row['iwwf_id']
		];
	}
	return $list;
}

function getScheduleRuns($conn, $scheduleId) {
	$q = "
		SELECT
			r.id AS run_id,
			r.schedule,
			r.`group`,
			r.run,
			r.`order`,
			r.athlete,
			CONCAT(a.first_name, ' ', a.last_name) AS athlete_name,
			a.bib,
			a.country,
			a.year_of_birth,
			r.status,
			r.real_time
		FROM runs r
		LEFT JOIN athletes a ON r.athlete = a.id
		WHERE r.schedule = ?
		ORDER BY r.schedule, r.`group`, r.run, r.`order` ASC
	";
	$stmt = $conn->prepare($q);
	$stmt->bind_param("i", $scheduleId);
	$stmt->execute();
	$res = $stmt->get_result();
	return $res->fetch_all(MYSQLI_ASSOC);
}

function attachAverageScores($conn, array &$allRuns) {
	if (empty($allRuns)) return;
	$runIds = array_column($allRuns, 'run_id');
	if (empty($runIds)) return;

	$inList = implode(',', array_map('intval',$runIds));
	$scoresQ= "SELECT run, judge, score FROM scores WHERE run IN ($inList)";
	$scoresRes = $conn->query($scoresQ);
	if (!$scoresRes)return;

	$avgMap=[];
	while($row=$scoresRes->fetch_assoc()){
		$rid=(int)$row['run'];
		$val=(float)$row['score'];
		if(!isset($avgMap[$rid])){
			$avgMap[$rid]=[0.0,0];
		}
		$avgMap[$rid][0]+=$val;
		$avgMap[$rid][1]+=1;
	}
	$scoresRes->free();

	foreach ($allRuns as &$ar) {
		$rid=(int)$ar['run_id'];
		if(isset($avgMap[$rid])){
			list($sum,$count)=$avgMap[$rid];
			$ar['average_score']=($count>0)? $sum/$count:0.0;
		}else{
			$ar['average_score']=0.0;
		}
	}
	unset($ar);
}

function isScheduleComplete(array $allRuns){
	foreach($allRuns as $rr){
		if((int)$rr['status']===0){
			return false;
		}
	}
	return true;
}

function filterNextGroups(array $allRuns,int $count){
	// find the first active => that group
	$activeGroup=null;
	foreach($allRuns as $r){
		if(!empty($r['active'])){
			$activeGroup=$r['group'];
			break;
		}
	}
	if(!$activeGroup)return $allRuns;

	$groupIds=[];
	foreach($allRuns as $r){
		if(!in_array($r['group'],$groupIds)){
			$groupIds[]=$r['group'];
		}
	}
	sort($groupIds);

	$activeIdx=array_search($activeGroup,$groupIds);
	$slice=array_slice($groupIds,$activeIdx+1,$count);

	$filtered=[];
	foreach($allRuns as $r){
		if(in_array($r['group'],$slice)){
			$filtered[]=$r;
		}
	}
	return $filtered;
}

function extractUniqueGroups(array $runs){
	$g=[];
	foreach($runs as $rr){
		if(!in_array($rr['group'],$g)){
			$g[]=$rr['group'];
		}
	}
	return$g;
}


// MAIN
if (isset($_GET["competitionCode"])) {
	$competitionCode=$_GET["competitionCode"];

	$requestGroupsOnly = isset($_GET["groups"]);
	$nextGroupsCount   = null;
	if(isset($_GET["next"])){
		$tmp=(int)$_GET["next"];
		if($tmp>0){
			$nextGroupsCount=$tmp;
		}
	}
	$includeScores=(isset($_GET["scores"]) && $_GET["scores"]==="true");

	$data= getScheduleAndRunsByCode(
		$conn,
		$competitionCode,
		$requestGroupsOnly,
		$nextGroupsCount,
		$includeScores
	);

	echo json_encode([
		"competitionCode"=>$competitionCode,
		"data"=>$data
	]);
}else{
	echo json_encode(["error"=>"Parameter 'competitionCode' is missing"]);
}