document.addEventListener("DOMContentLoaded", function () {

  /************************************************************
   * 0) Retrieve bearer token from localStorage user
   ************************************************************/
  const storedUser = JSON.parse(localStorage.getItem("user")) || {};
  const bearerToken = storedUser.bearer || null;
  if (!bearerToken) {
	console.error("No bearer token found in localStorage!");
	localStorage.removeItem("user");
	window.location.href = "index.html";
	return;
  }

  /**
   * Build fetch headers with Bearer token
   */
  function getAuthHeaders() {
	return {
	  "Content-Type": "application/json",
	  "Authorization": `Bearer ${bearerToken}`,
	};
  }

  /**
   * If response is not OK => sign out
   */
  function handleNotOkResponse(response) {
	console.error(
	  `API response not OK (${response.status} ${response.statusText}). Logging out...`
	);
	alert("Session expired or invalid. Signing out...");
	localStorage.removeItem("user");
	window.location.href = "index.html";
  }

  /************************************************************
   * Insert CSS
   ************************************************************/
  const style = document.createElement("style");
  style.textContent = `
	.athlete-name {
	  margin: 0;
	  font-size: 1rem;
	  text-align: center;
	}
	.table td {
	  vertical-align: middle;
	}
	.score-input {
	  text-align: center;
	  width: 100px;
	}
	.badge {
	  display: inline-block;
	  padding: 0.2rem 0.4rem;
	  font-size: 0.8rem;
	  border-radius: 5px;
	  font-weight: bold;
	}
	.card-header {
	  display: flex;
	  justify-content: space-between;
	  align-items: center;
	  gap: 0.5rem;
	}
	.card-footer {
	  display: flex;
	  flex-direction: column;
	  gap: 0.5rem;
	  align-items: center;
	  justify-content: center;
	}
	.card-title {
	  flex: 1;
	  text-align: center;
	}
	.btn-group {
	  display: flex;
	  justify-content: space-between;
	  gap: 0.5rem;
	  margin-bottom: 0.5rem;
	}
	.is-warning {
	  border: 2px solid #ffc107 !important;
	}
	.is-danger {
	  border: 2px solid #dc3545 !important;
	}
	.is-invalid {
	  border: 2px solid #dc3545 !important;
	  background-color: #f8d7da;
	}
	#protest-modal {
	  position: fixed;
	  top: 0;
	  left: 0;
	  width: 100%;
	  height: 100%;
	  background: rgba(0,0,0,0.5);
	  display: none;
	  z-index: 9999;
	}
	#protest-modal .modal-content {
	  background: #fff;
	  padding: 1rem;
	  margin: 10% auto;
	  max-width: 400px;
	  border-radius: 5px;
	}
	#protest-modal .modal-content h4 {
	  margin-top: 0;
	}
	#protest-modal .modal-content .modal-actions {
	  display: flex;
	  justify-content: flex-end;
	  gap: 0.5rem;
	  margin-top: 1rem;
	}
	#danger-message {
	  font-weight: bold;
	  color: #dc3545;
	}
	.scoring-action-alert {
	  margin-top: 0.5rem;
	  padding: 0.5rem;
	  border: 1px solid;
	  border-radius: 5px;
	  width: 100%;
	  text-align: center;
	}
	.scoring-action-alert h5 {
	  margin: 0 0 0.5rem 0;
	  font-size: 1rem;
	  font-weight: bold;
	}
	.scoring-action-alert button {
	  margin: 0 0.25rem;
	}
	.judge-badges-row {
	  display: flex;
	  justify-content: center;
	  gap: 0.5rem;
	  margin-bottom: 0.5rem;
	}
	.btn-selected {
	  outline: 2px solid #333;
	}
	.badge-placement {
	  background-color: #ffc107;
	  color: #000;
	  margin-left: 6px;
	}
  `;
  document.head.appendChild(style);

  /************************************************************
   * Helper: Check if any text-based input is focused
   ************************************************************/
  function isAnyInputFocused() {
	const active = document.activeElement;
	return active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA");
  }

  /************************************************************
   * 2) Internal state variables
   ************************************************************/
  let activeRunIndex = 0;
  let allRuns = [];
  let scoreDifferenceThreshold = 25;
  let placeDifferenceThreshold = 2;
  let overrulingThreshold = 0;
  let lastDataCache = null;
  let userOverrodeIndex = false;
  let lastRunIds = null;
  let pendingFocusIndex = null;

  /************************************************************
   * 2A) Grab container & parse data-section
   ************************************************************/
  const scoringContainer = document.getElementById("kt_app_content_container_scoring");
  const dataSection = parseInt(scoringContainer.getAttribute("data-section"), 10);

  /************************************************************
   * 3) Retrieve competition/user from localStorage
   ************************************************************/
  const storedCompetition = JSON.parse(localStorage.getItem("activeCompetition")) || {};
  if (!storedCompetition.code) {
	console.error("No active competition found in localStorage!");
	return;
  }
  const competitionCode = storedCompetition.code;
  const competitionName = storedCompetition.name || "Unknown Competition";

  const localUser = storedUser;
  const officialId = localUser.id || null;
  const iwwfId = localUser.iwwf_id || null;

  /************************************************************
   * 4) Prepare "Request Protest" modal
   ************************************************************/
  const protestModalHTML = `
	<div id="protest-modal">
	  <div class="modal-content">
		<h4>Request Protest</h4>
		<textarea id="protest-remark" rows="3" placeholder="Enter your remark..."></textarea>
		<div class="modal-actions">
		  <button id="protest-cancel" class="btn btn-secondary">Cancel</button>
		  <button id="protest-submit" class="btn btn-primary">Submit</button>
		</div>
	  </div>
	</div>
  `;
  document.body.insertAdjacentHTML("beforeend", protestModalHTML);

  const protestModal = document.getElementById("protest-modal");
  const protestRemark = document.getElementById("protest-remark");
  const protestCancel = document.getElementById("protest-cancel");
  const protestSubmit = document.getElementById("protest-submit");
  let protestRunId = null;

  protestCancel.addEventListener("click", function () {
	protestModal.style.display = "none";
  });

  protestSubmit.addEventListener("click", function () {
	const remarkText = protestRemark.value.trim();
	if (!remarkText) {
	  alert("Please enter a remark for the protest.");
	  return;
	}
	if (!protestRunId || !officialId) {
	  alert("Missing run or official info!");
	  return;
	}
	const payload = {
	  officialId: iwwfId,
	  competitionCode: competitionCode,
	  runId: protestRunId,
	  remark: remarkText,
	};
	fetch(`${API_BASE_URL}?api=protests`, {
	  method: "POST",
	  headers: getAuthHeaders(),
	  body: JSON.stringify(payload),
	})
	  .then((res) => {
		if (!res.ok) {
		  handleNotOkResponse(res);
		  return null;
		}
		return res.json();
	  })
	  .then((data) => {
		if (!data) return;
		if (!data.success) {
		  alert("Error submitting protest: " + (data.error || "Unknown error"));
		  return;
		}
		protestModal.style.display = "none";
		fetchSettingsAndThenSchedule();
	  })
	  .catch((err) => {
		console.error("Error submitting protest:", err);
		alert("Error submitting protest");
		protestModal.style.display = "none";
	  });
  });

  /************************************************************
   * 5) fetchSettingsAndThenSchedule => load thresholds, then schedule
   ************************************************************/
  fetchSettingsAndThenSchedule();
  function fetchSettingsAndThenSchedule() {
	const url = `${API_BASE_URL}?api=settings&competitionCode=${encodeURIComponent(competitionCode)}`;
	fetch(url, {
	  method: "GET",
	  headers: getAuthHeaders(),
	})
	  .then((res) => {
		if (!res.ok) {
		  handleNotOkResponse(res);
		  return null;
		}
		return res.json();
	  })
	  .then((json) => {
		if (!json) return;
		if (!json.success || !json.settings) {
		  console.warn("No settings found or error fetching settings:", json?.error);
		} else {
		  scoreDifferenceThreshold = parseInt(json.settings.score_difference) || 25;
		  placeDifferenceThreshold = parseInt(json.settings.place_difference) || 2;
		  overrulingThreshold = parseInt(json.settings.overruling) || 0;
		}
		fetchScheduleData();
	  })
	  .catch((err) => {
		console.error("Error fetching settings:", err);
		scoreDifferenceThreshold = 25;
		placeDifferenceThreshold = 2;
		overrulingThreshold = 0;
		fetchScheduleData();
	  });
  }

  /************************************************************
   * 6) fetchScheduleData => flatten runs => maybe re-render
   *      Also dynamically check if the active athlete changed.
   ************************************************************/
  function fetchScheduleData() {
	const scheduleUrl = `${API_BASE_URL}?api=schedule&competitionCode=${encodeURIComponent(
	  competitionCode
	)}&public=false&scores=true`.replace(/\s+/g, "");
	fetch(scheduleUrl, { method: "GET", headers: getAuthHeaders() })
	  .then((res) => {
		if (!res.ok) {
		  handleNotOkResponse(res);
		  return null;
		}
		return res.json();
	  })
	  .then((data) => {
		if (!data) return;
		if (!Array.isArray(data.data)) {
		  console.error("Unexpected API response from schedule.");
		  return;
		}
		let relevantData = data.data;
		const newAllRuns = relevantData.flatMap((schedule) =>
		  schedule.runs.map((run) => ({
			...run,
			number_of_judges: schedule.number_of_judges,
			officials: schedule.officials,
			groupName: `${schedule.category_name || "Unknown Cat"} ${schedule.heatsystem_name || "Unknown Round"} Group ${run.group}`,
			athleteName: run.athlete_name || "Unknown Athlete",
			last_run_timestamp: schedule.last_run_timestamp,
		  }))
		);
		if (newAllRuns.length === 0) {
		  console.error("No runs available to display for scoring.");
		  return;
		}
		const newRunIds = newAllRuns.map((r) => r.run_id).join(",");
		let bigChange = false;
		if (lastRunIds === null) {
		  bigChange = true;
		} else if (lastRunIds !== newRunIds) {
		  bigChange = true;
		}
		lastRunIds = newRunIds;
		let serverActiveIndex = newAllRuns.findIndex((r) => r.active === 1);
		if (serverActiveIndex < 0) serverActiveIndex = 0;
		allRuns = newAllRuns;
		// Only update activeRunIndex from server if the user has not manually navigated.
		if (!userOverrodeIndex && serverActiveIndex !== activeRunIndex) {
		  activeRunIndex = serverActiveIndex;
		  renderCard(allRuns[activeRunIndex]);
		  return;
		}
		const newDataJSON = JSON.stringify(allRuns);
		if (lastDataCache !== newDataJSON) {
		  lastDataCache = newDataJSON;
		  renderCard(allRuns[activeRunIndex]);
		}
	  })
	  .catch((error) => console.error("Error fetching schedule data:", error));
  }

  /************************************************************
   * Auto-refresh every 2 seconds ONLY if no input is focused
   ************************************************************/
  setInterval(() => {
	if (isAnyInputFocused()) return;
	fetchSettingsAndThenSchedule();
  }, 2000);

  /************************************************************
   * 7) buildJudgeBadgesHTML (only if dataSection=1)
   ************************************************************/
  function buildJudgeBadgesHTML(numberOfJudges, scores) {
	if (dataSection !== 1) {
	  return "";
	}
	let html = `<div class="judge-badges-row">`;
	for (let i = 1; i <= numberOfJudges; i++) {
	  const foundScore = scores.find((s) => s.judge === i);
	  let badgeClass = "bg-secondary";
	  if (foundScore) {
		switch (foundScore.status) {
		  case 0:
			badgeClass = "bg-info";
			break;
		  case 1:
			badgeClass = "bg-success";
			break;
		  case 2:
			badgeClass = "bg-warning";
			break;
		  case 3:
			badgeClass = "bg-primary";
			break;
		  case 4:
			badgeClass = "bg-danger";
			break;
		  default:
			badgeClass = "bg-secondary";
		}
	  }
	  html += `<span class="badge ${badgeClass}">J${i}</span>`;
	}
	html += `</div>`;
	return html;
  }

  /************************************************************
   * Helper: showConfirmationPrompt
   ************************************************************/
  function showConfirmationPrompt(buttonEl, selectedStatus, runId, judgeNumber) {
	const btnGroup = buttonEl.parentNode;
	const existingAlert = btnGroup.parentNode.querySelector(".scoring-action-alert");
	if (existingAlert) {
	  existingAlert.remove();
	}
	let bgColor = "#fff3cd";
	let bdColor = "#ffeeba";
	if (buttonEl.classList.contains("btn-warning")) {
	  bgColor = "#fff3cd";
	  bdColor = "#ffeeba";
	} else if (buttonEl.classList.contains("btn-success")) {
	  bgColor = "#d4edda";
	  bdColor = "#c3e6cb";
	} else if (buttonEl.classList.contains("btn-danger")) {
	  bgColor = "#f8d7da";
	  bdColor = "#f5c6cb";
	} else if (buttonEl.classList.contains("btn-primary")) {
	  bgColor = "#d1ecf1";
	  bdColor = "#bee5eb";
	}
	const alertDiv = document.createElement("div");
	alertDiv.classList.add("scoring-action-alert");
	alertDiv.style.backgroundColor = bgColor;
	alertDiv.style.borderColor = bdColor;
	alertDiv.innerHTML = `
	  <h5>Are you sure?</h5>
	  <p>This will set the status to <strong>${statusLabel(selectedStatus)}</strong>.</p>
	  <button type="button" class="btn btn-secondary" id="action-cancel">Cancel</button>
	  <button type="button" class="btn btn-primary" id="action-confirm">Confirm</button>
	`;
	btnGroup.insertAdjacentElement("afterend", alertDiv);
	alertDiv.querySelector("#action-cancel").addEventListener("click", () => {
	  alertDiv.remove();
	});
	alertDiv.querySelector("#action-confirm").addEventListener("click", () => {
	  updateScoreStatus(runId, judgeNumber, selectedStatus)
		.then(() => {
		  alertDiv.remove();
		  fetchSettingsAndThenSchedule();
		})
		.catch((err) => console.error(err));
	});
  }

  /************************************************************
   * 8) Render the scoring card
   ************************************************************/
  function renderCard(run) {
	if (!run) {
	  console.error("Invalid run data. Cannot render card.");
	  return;
	}
	scoringContainer.innerHTML = "";
	const card = document.createElement("div");
	card.classList.add("card", "mb-3", "mobile-optimized-card");
	const activeAthleteIndex = allRuns.findIndex(r => r.active === 1);
	// For forward navigation, do not move past the active athlete.
	const nextDisabled = (activeRunIndex === allRuns.length - 1 || activeRunIndex >= activeAthleteIndex) ? "disabled" : "";
	const badgesPlaceholder = dataSection === 1 ? `<div id="judgeBadgesRow"></div>` : "";
	let footerButtonsHTML = "";
	if (run.status === 0) {
	  let disabledFRS = "";
	  let disabledDNS = "";
	  let disabledDSQ = "";
	  if (dataSection === 1) {
		const localJudgeOfficial = run.officials.find(o => o.iwwf_id === iwwfId);
		if (!localJudgeOfficial || localJudgeOfficial.judge_number !== 1) {
		  disabledFRS = "disabled";
		  disabledDNS = "disabled";
		  disabledDSQ = "disabled";
		}
	  }
	  const isFRSEnabled = run.run === 2 ? "" : "disabled";
	  footerButtonsHTML = `
		${badgesPlaceholder}
		<div class="btn-group">
		  <button id="publishScores"
				  class="btn btn-success scoring-action-btn"
				  data-status="1">Publish</button>
		  <button class="btn btn-warning scoring-action-btn"
				  data-status="2"
				  ${isFRSEnabled} ${disabledFRS}>FRS</button>
		  <button class="btn btn-primary scoring-action-btn"
				  data-status="3"
				  ${disabledDNS}>DNS</button>
		  <button class="btn btn-danger scoring-action-btn"
				  data-status="4"
				  ${disabledDSQ}>DSQ</button>
		</div>
		<div style="text-align:center; width: 100%;">
		  <span id="danger-message"></span>
		</div>
	  `;
	} else if (run.status !== 0) {
	  footerButtonsHTML = `
		${badgesPlaceholder}
		<div class="btn-group">
		  <button class="btn btn-danger" id="requestProtestBtn">Request Protest</button>
		</div>
		<div style="text-align:center; width: 100%;">
		  <span id="danger-message"></span>
		</div>
	  `;
	} else {
	  footerButtonsHTML = `
		${badgesPlaceholder}
		<div style="text-align:center; width: 100%;">
		  <p>Number of Judges: <strong>${run.number_of_judges || 0}</strong></p>
		  <p>Number of Scores: <strong><span id="scoresCountSpan">0</span></strong></p>
		  <span id="danger-message"></span>
		</div>
	  `;
	}

	card.innerHTML = `
	  <div class="card-header">
		<button class="btn btn-light"
				id="prevAthlete"
				${activeRunIndex === 0 ? "disabled" : ""}>←</button>
		<h3 class="flex-column align-items-start card-title">
		  <span class="fw-bold text-dark card-label">${run.groupName}</span>
		  <span class="fw-semibold text-muted card-label mt-1 fs-7">
			${run.athleteName} - Run ${run.run}
		  </span>
		</h3>
		<button class="btn btn-light"
				id="nextAthlete"
				${nextDisabled}>→</button>
	  </div>
	  <div class="card-body">
		<table class="table table-hover table-sm">
		  <tbody id="judgeScoresTable"></tbody>
		</table>
	  </div>
	  <div class="card-footer" style="width: 100%;">
		${footerButtonsHTML}
	  </div>
	`;
	scoringContainer.appendChild(card);
	fetchExistingScores(run.run_id, dataSection);
	document.getElementById("prevAthlete")?.addEventListener("click", () => navigateAthlete(-1));
	document.getElementById("nextAthlete")?.addEventListener("click", () => navigateAthlete(1));

	if (run.status === 0) {
	  const actionButtons = document.querySelectorAll(".scoring-action-btn");
	  actionButtons.forEach((btn) => {
		btn.addEventListener("click", function () {
		  const selectedStatus = parseInt(this.getAttribute("data-status"), 10);
		  let judgeNumber = null;
		  if (dataSection === 1) {
			const localJudgeOfficial = run.officials.find((o) => o.iwwf_id === iwwfId);
			if (localJudgeOfficial && localJudgeOfficial.judge_number) {
			  judgeNumber = localJudgeOfficial.judge_number;
			}
		  }
		  showConfirmationPrompt(this, selectedStatus, run.run_id, judgeNumber);
		});
	  });
	}

	if (run.status !== 0) {
	  const requestProtestBtn = document.getElementById("requestProtestBtn");
	  if (requestProtestBtn) {
		checkIfProtestOpenForRun(run.run_id)
		  .then((isProtestOpen) => {
			if (isProtestOpen) {
			  requestProtestBtn.textContent = "Protest Requested";
			  requestProtestBtn.disabled = true;
			} else {
			  requestProtestBtn.addEventListener("click", function () {
				protestRunId = run.run_id;
				protestRemark.value = "";
				protestModal.style.display = "block";
			  });
			}
		  })
		  .catch((err) => console.error("Error checking protest for run:", err));
	  }
	}
  }

  /************************************************************
   * 9) Navigation
   * When navigating backwards, do NOT override the user's chosen active run.
   ************************************************************/
  function navigateAthlete(direction) {
	const activeAthleteIndex = allRuns.findIndex(r => r.active === 1);
	const newIndex = activeRunIndex + direction;
	// For forward navigation, do not move past the active athlete.
	if (direction > 0 && activeRunIndex >= activeAthleteIndex) {
	  return;
	}
	if (newIndex >= 0 && newIndex < allRuns.length) {
	  activeRunIndex = newIndex;
	  userOverrodeIndex = true;
	  renderCard(allRuns[activeRunIndex]);
	}
  }

  /************************************************************
   * 10) Existing Scores / Judging
   ************************************************************/
  function fetchExistingScores(runId, dataSection) {
	const scoresUrl = `${API_BASE_URL}?api=scores&runId=${runId}`;
	fetch(scoresUrl, {
	  method: "GET",
	  headers: getAuthHeaders(),
	})
	  .then((res) => {
		if (!res.ok) {
		  handleNotOkResponse(res);
		  return null;
		}
		return res.json();
	  })
	  .then((scoreData) => {
		if (!scoreData) return;
		if (!scoreData.success || !Array.isArray(scoreData.scores)) {
		  throw new Error(scoreData.error || "Unexpected response format from scores.php");
		}
		const scoresCountSpan = document.getElementById("scoresCountSpan");
		if (scoresCountSpan) {
		  scoresCountSpan.textContent = scoreData.scores.length;
		}
		const judgeBadgesRowEl = document.getElementById("judgeBadgesRow");
		if (dataSection === 1 && judgeBadgesRowEl) {
		  const thisRun = allRuns.find((r) => r.run_id === runId);
		  if (thisRun && thisRun.number_of_judges > 0) {
			const dynamicBadgesHTML = buildJudgeBadgesHTML(thisRun.number_of_judges, scoreData.scores);
			judgeBadgesRowEl.innerHTML = dynamicBadgesHTML;
		  }
		}
		const thisRun = allRuns.find((r) => r.run_id === runId);
		if (!thisRun || !Array.isArray(thisRun.officials)) {
		  console.log("No .officials for this run => skipping placement retrieval");
		  fillTableWithRows(scoreData.scores, runId, dataSection, {});
		  return;
		}
		const officialJudgePromises = thisRun.officials.map((official) => {
		  const judgeIwwfId = official.iwwf_id;
		  if (!judgeIwwfId) return Promise.resolve(null);
		  const resultsUrl = `${API_BASE_URL}?api=results&competitionCode=${encodeURIComponent(competitionCode)}&public=false&judgeId=${judgeIwwfId}`;
		  return fetch(resultsUrl, {
			method: "GET",
			headers: getAuthHeaders(),
		  })
			.then((r) => {
			  if (!r.ok) {
				handleNotOkResponse(r);
				return null;
			  }
			  return r.json();
			})
			.then((resultsData) => {
			  if (!resultsData) return null;
			  if (!resultsData.data || !resultsData.data.active_athlete) {
				return null;
			  }
			  const foundPlacement = resultsData.data.active_athlete.placement;
			  if (foundPlacement === undefined) {
				return null;
			  }
			  return {
				judgeNumber: official.judge_number,
				placement: foundPlacement,
			  };
			})
			.catch((err) => {
			  console.warn("Error calling results for judge:", judgeIwwfId, err);
			  return null;
			});
		});
		Promise.all(officialJudgePromises).then((placementArray) => {
		  const placementMap = {};
		  placementArray.forEach((item) => {
			if (item && item.judgeNumber !== undefined) {
			  placementMap[item.judgeNumber] = item.placement;
			}
		  });
		  fillTableWithRows(scoreData.scores, runId, dataSection, placementMap).finally(() => {
			if (pendingFocusIndex !== null) {
			  const newInputs = document.querySelectorAll(".score-input");
			  if (newInputs[pendingFocusIndex]) {
				if (!newInputs[pendingFocusIndex].value.trim()) {
				  newInputs[pendingFocusIndex].focus();
				}
			  }
			  pendingFocusIndex = null;
			}
		  });
		});
	  })
	  .catch((error) => {
		console.error("Error fetching scores:", error);
	  });
  }

  async function fillTableWithRows(scores, runId, dataSection, placementMap) {
	const tableEl = document.getElementById("judgeScoresTable");
	if (!tableEl) return;
	tableEl.innerHTML = await generateJudgeRows(scores, runId, dataSection, placementMap);
	addInputListeners(runId);
  }

  /************************************************************
   * 11) fetchActiveAthletePlacement
   ************************************************************/
  async function fetchActiveAthletePlacement(competitionCode, isPublic = false) {
	try {
	  let url = `${API_BASE_URL}?api=results&competitionCode=${encodeURIComponent(competitionCode)}`;
	  url += isPublic ? `&public=true` : `&public=false`;
	  const response = await fetch(url, {
		method: "GET",
		headers: getAuthHeaders(),
	  });
	  if (!response.ok) {
		handleNotOkResponse(response);
		return null;
	  }
	  const jsonData = await response.json();
	  if (!jsonData || !jsonData.data || !jsonData.data.active_athlete) {
		console.warn("No active athlete data found in results.");
		return null;
	  }
	  const activeAthlete = jsonData.data.active_athlete;
	  if (typeof activeAthlete.placement !== "number") {
		console.warn("Active athlete has no numeric placement.");
		return null;
	  }
	  return activeAthlete.placement;
	} catch (err) {
	  console.error("Error in fetchActiveAthletePlacement:", err);
	  return null;
	}
  }

  /**
   * Compute median of an array of numbers.
   */
  function computeMedian(arrayOfNumbers) {
	if (!Array.isArray(arrayOfNumbers) || arrayOfNumbers.length === 0) {
	  return 0;
	}
	const sorted = [...arrayOfNumbers].sort((a, b) => a - b);
	const midIndex = Math.floor(sorted.length / 2);
	if (sorted.length % 2 === 0) {
	  return (sorted[midIndex - 1] + sorted[midIndex]) / 2;
	} else {
	  return sorted[midIndex];
	}
  }

  /************************************************************
   *  --- Helper: getAllResultsScores ---
   *  Retrieves all scores from the results API.
   ************************************************************/
  async function getAllResultsScores(competitionCode) {
	const url = `${API_BASE_URL}?api=results&competitionCode=${encodeURIComponent(competitionCode)}&public=false`;
	try {
	  const response = await fetch(url, {
		method: "GET",
		headers: getAuthHeaders(),
	  });
	  if (!response.ok) {
		handleNotOkResponse(response);
		return [];
	  }
	  const jsonData = await response.json();
	  if (!jsonData || !jsonData.data) {
		console.warn("No data returned from results API");
		return [];
	  }
	  let scoresArray = [];
	  if (Array.isArray(jsonData.data.runs)) {
		jsonData.data.runs.forEach((athlete) => {
		  if (Array.isArray(athlete.runs)) {
			athlete.runs.forEach((run) => {
			  if (run.score !== null && run.score !== undefined) {
				scoresArray.push(run.score);
			  }
			});
		  }
		});
	  }
	  return scoresArray;
	} catch (err) {
	  console.error("Error fetching results scores:", err);
	  return [];
	}
  }

  /************************************************************
   * 12) generateJudgeRows: includes difference logic, overruling,
   *     and same score detection.
   ************************************************************/
  async function generateJudgeRows(scores, runId, dataSection, placementMap) {
	const run = allRuns.find((r) => r.run_id === runId);
	if (!run || !run.officials || run.officials.length === 0) {
	  console.error("No officials data available for this run.");
	  return "<tr><td colspan='3'>No judge data available</td></tr>";
	}

	const areInputsDisabledByRun = run.status !== 0 ? "disabled" : "";
	const loggedInJudgeIWWFId = storedUser?.iwwf_id;
	const filteredOfficials =
	  dataSection === 1
		? run.officials.filter((o) => o.iwwf_id === loggedInJudgeIWWFId)
		: run.officials.slice(0, run.number_of_judges);

	if (dataSection === 1 && filteredOfficials.length === 0) {
	  const c = document.getElementById("kt_app_content_container_scoring");
	  c.innerHTML = `<div class="alert alert-warning" role="alert">You are not assigned to judge at the moment.</div>`;
	  return "";
	}

	let allJudgesScored = true;
	filteredOfficials.forEach((off) => {
	  const existingScore = scores.find((s) => s.judge === off.judge_number);
	  const val = existingScore?.score ? parseFloat(existingScore.score) : 0;
	  if (val <= 0) {
		allJudgesScored = false;
	  }
	});

	const totalScores = scores.length;
	const totalJudges = run.number_of_judges;
	const allScoresAreIn = totalScores === totalJudges;
	let differenceBadgesAllowed = allScoresAreIn;
	let dangerDetected = false;
	let sameScoreDetected = false;
	const threshold = scoreDifferenceThreshold;

	let averageScore = 0;
	if (differenceBadgesAllowed) {
	  const sum = scores.reduce((acc, s) => acc + (s.score || 0), 0);
	  averageScore = sum / totalScores;
	}

	const overallPlace = differenceBadgesAllowed
	  ? await fetchActiveAthletePlacement(competitionCode, false)
	  : null;

	let medianPlacement = null;
	if (overrulingThreshold === 1) {
	  const allPlacements = run.officials
		.map((o) => {
		  const p = placementMap[o.judge_number];
		  return typeof p === "number" ? p : null;
		})
		.filter((pl) => pl !== null);
	  medianPlacement = computeMedian(allPlacements);
	  console.log("Median Placement for this run =>", medianPlacement);
	}

	let overrulingDetected = false;

	const rowsHTML = filteredOfficials
	  .map((official) => {
		const existingScore = scores.find((s) => s.judge === official.judge_number) || {};
		let numericVal = parseFloat(existingScore.score || 0);
		let extraDisabled = "";
		if (dataSection === 1 && existingScore.status !== undefined && existingScore.status !== 0) {
		  extraDisabled = "disabled";
		}
		const finalDisabledAttr = areInputsDisabledByRun || extraDisabled ? "disabled" : "";
		let difference = "";
		let badgeDiffClass = "bg-success";
		if (differenceBadgesAllowed && typeof existingScore.differenceFromAverage === "number") {
		  difference = existingScore.differenceFromAverage.toFixed(2);
		  const diffValue = parseFloat(difference);
		  if (Math.abs(diffValue) >= threshold) {
			badgeDiffClass = "bg-danger";
			dangerDetected = true;
		  } else if (Math.abs(diffValue) >= threshold / 2) {
			badgeDiffClass = "bg-warning";
		  }
		}
		const differenceBadge =
		  differenceBadgesAllowed && difference !== ""
			? `<span class="badge ${badgeDiffClass}">${difference}</span>`
			: "";
		let place = placementMap[official.judge_number];
		let placeHTML = "";
		let inputClass = "";
		if (allScoresAreIn && place) {
		  if (overallPlace && place !== null) {
			if (place === overallPlace) {
			  placeHTML = `<span class="badge bg-success">#${place}</span>`;
			} else {
			  const placeDiff = Math.abs(place - overallPlace);
			  if (placeDiff > placeDifferenceThreshold) {
				placeHTML = `<span class="badge bg-danger">#${place}</span>`;
				inputClass = "is-danger";
			  } else {
				placeHTML = `<span class="badge badge-placement">#${place}</span>`;
			  }
			}
		  } else {
			placeHTML = `<span class="badge badge-placement">#${place}</span>`;
		  }
		}
		if (
		  overrulingThreshold === 1 &&
		  medianPlacement !== null &&
		  place !== undefined &&
		  allScoresAreIn
		) {
		  if (medianPlacement !== overallPlace && medianPlacement !== place) {
			placeHTML = `<span class="badge bg-danger">#${place}</span>`;
			inputClass = "is-danger";
			dangerDetected = true;
			overrulingDetected = true;
		  }
		}
		
		// --- SAME SCORE DETECTION ---
		// Retrieve all results scores and check if the rounded average score appears at least twice.
		getAllResultsScores(competitionCode).then((resultsScores) => {
		  const roundedAverage = Number(averageScore.toFixed(2));
		  const count = resultsScores.filter(s => Number(s).toFixed(2) === roundedAverage.toFixed(2)).length;
		  if (count >= 2) {
			sameScoreDetected = true;
			dangerDetected = true;
			const publishBtn = document.getElementById("publishScores");
			const dangerMsg = document.getElementById("danger-message");
			if (publishBtn) publishBtn.disabled = true;
			if (dangerMsg) dangerMsg.textContent = "Same Score detected! You need to change the score!";
			console.log("Same score detected! You need to change the score!");
		  }
		});

		if (numericVal <= 0) {
		  inputClass = "is-invalid";
		} else if (differenceBadgesAllowed && averageScore > 0) {
		  const isWarning = Math.abs(numericVal - averageScore) > averageScore * 0.2;
		  if (isWarning) {
			inputClass = "is-warning";
		  }
		  if (badgeDiffClass === "bg-danger") {
			inputClass = "is-danger";
		  }
		}
		return `
		  <tr>
			<td class="text-start">
			  <span class="badge bg-dark text-white">J${official.judge_number}</span>
			  ${official.official_name}
			</td>
			<td>
			  <input
				type="number"
				class="form-control form-control-sm input-sm score-input ${inputClass}"
				value="${numericVal || ""}"
				data-judge="${official.judge_number}"
				data-run-id="${runId}"
				step="0.01"
				${finalDisabledAttr}
			  />
			</td>
			<td>
			  <div class="badge-container">
				${placeHTML}
				${differenceBadge}
			  </div>
			</td>
		  </tr>
		`;
	  })
	  .join("");

	const publishBtn = document.getElementById("publishScores");
	const dangerMsg = document.getElementById("danger-message");
	if (publishBtn) {
	  publishBtn.disabled = false;
	}
	if (dangerMsg) {
	  dangerMsg.textContent = "";
	}
	
	if (run?.status === 0 && publishBtn) {
	  if (run.number_of_judges === 1) {
		publishBtn.disabled = false;
		if (dangerMsg) {
		  dangerMsg.textContent = "";
		}
	  } else {
		if (!allJudgesScored || !allScoresAreIn || dangerDetected) {
		  publishBtn.disabled = true;
		  if (dangerDetected && dangerMsg && !overrulingDetected) {
			dangerMsg.textContent = `Large difference or place difference issue detected!`;
		  } else if (overrulingDetected) {
			dangerMsg.textContent = `Overruling detected!`;
		  }
		}
	  }
	}
	return rowsHTML;
  }

  /************************************************************
   * 13) Add input listeners
   ************************************************************/
  function addInputListeners(runId) {
	const inputs = document.querySelectorAll(".score-input");
	inputs.forEach((input, index) => {
	  input.addEventListener("keydown", (event) => {
		if (event.key === "Enter") {
		  event.preventDefault();
		  input.blur();
		}
	  });
	  input.addEventListener("blur", (event) => {
		let val = parseFloat(event.target.value);
		if (isNaN(val)) {
		  console.warn("Invalid numeric input:", event.target.value);
		  event.target.classList.add("is-invalid");
		} else {
		  const rounded = val.toFixed(2);
		  event.target.value = rounded;
		  const judgeId = parseInt(input.getAttribute("data-judge"), 10);
		  updateScore(runId, judgeId, parseFloat(rounded))
			.then((success) => {
			  if (success) {
				pendingFocusIndex = index + 1;
			  }
			})
			.catch((err) => {
			  console.error("Error updating score:", err);
			});
		}
	  });
	});
  }

  /************************************************************
   * 14) Updating a score - returns a Promise<boolean>
   ************************************************************/
  function updateScore(runId, judge, floatValue) {
	return new Promise((resolve, reject) => {
	  const numeric = parseFloat(floatValue);
	  console.log("Sending numeric score to API =>", numeric);
	  fetch(`${API_BASE_URL}?api=scores`, {
		method: "POST",
		headers: getAuthHeaders(),
		body: JSON.stringify({
		  run_id: runId,
		  data_section: dataSection,
		  scores: [
			{
			  judge,
			  score: numeric,
			},
		  ],
		}),
	  })
		.then((res) => {
		  if (!res.ok) {
			handleNotOkResponse(res);
			resolve(false);
			return null;
		  }
		  return res.json();
		})
		.then((data) => {
		  if (!data) {
			resolve(false);
			return;
		  }
		  if (!data.success) {
			console.error("Failed to update score:", data.error);
			resolve(false);
		  } else {
			console.log(`Score updated for Judge ${judge}: ${numeric}`);
			fetchExistingScores(runId, dataSection);
			resolve(true);
		  }
		})
		.catch((error) => {
		  console.error("Error updating score:", error);
		  reject(error);
		});
	});
  }

  /************************************************************
   * 15) Updating run status OR judge status
   *      After a successful status update, refresh schedule.
   ************************************************************/
  function updateScoreStatus(runId, judge, status) {
	return fetch(`${API_BASE_URL}?api=scores`, {
	  method: "POST",
	  headers: getAuthHeaders(),
	  body: JSON.stringify({
		run_id: runId,
		data_section: dataSection,
		judge,
		status,
	  }),
	})
	  .then((res) => {
		if (!res.ok) {
		  handleNotOkResponse(res);
		  return null;
		}
		return res.json();
	  })
	  .then((data) => {
		if (!data) return;
		if (!data.success) {
		  throw new Error(data.error || "Failed to update status.");
		}
		console.log(`Run ${runId} status updated to ${status}.`);
		fetchSettingsAndThenSchedule();
	  })
	  .catch((error) => {
		console.error("Error updating status:", error);
		throw error;
	  });
  }

  /************************************************************
   * 16) Check if protest is open
   ************************************************************/
  function checkIfProtestOpenForRun(runId) {
	const protestUrl = `${API_BASE_URL}?api=protests&competitionCode=${encodeURIComponent(competitionCode)}&runId=${runId}`;
	return fetch(protestUrl, {
	  method: "GET",
	  headers: getAuthHeaders(),
	})
	  .then((res) => {
		if (!res.ok) {
		  handleNotOkResponse(res);
		  return null;
		}
		return res.json();
	  })
	  .then((json) => {
		if (!json) return false;
		if (!json.success) {
		  console.warn("checkIfProtestOpenForRun error:", json.error);
		  return false;
		}
		const protests = json.data || [];
		const openOne = protests.find((p) => p.status === 0);
		return !!openOne;
	  })
	  .catch((err) => {
		console.error("Error checking protest:", err);
		return false;
	  });
  }

  /**
   * Convert numeric status to label.
   */
  function statusLabel(st) {
	switch (st) {
	  case 1:
		return "Published";
	  case 2:
		return "FRS";
	  case 3:
		return "DNS";
	  case 4:
		return "DSQ";
	  default:
		return "Unknown";
	}
  }

  /************************************************************
   * 9) Navigation
   * When navigating backwards, do NOT override the user's chosen active run.
   ************************************************************/
  function navigateAthlete(direction) {
	const activeAthleteIndex = allRuns.findIndex(r => r.active === 1);
	const newIndex = activeRunIndex + direction;
	// For forward navigation, do not move past the active athlete.
	if (direction > 0 && activeRunIndex >= activeAthleteIndex) {
	  return;
	}
	if (newIndex >= 0 && newIndex < allRuns.length) {
	  activeRunIndex = newIndex;
	  userOverrodeIndex = true;
	  renderCard(allRuns[activeRunIndex]);
	}
  }
});