const GAME_ID = "demo";
const REQUIRED_HOLD_SECONDS = 10;

let objectiveHoldTimer = null;
let objectiveHoldStartedAt = null;
let activeObjectiveButton = null;

function getApiBase() {
  return window.location.origin;
}

function getPlayerId() {
  return localStorage.getItem("qrTagGame.playerId");
}

function getUserId() {
  return localStorage.getItem("qrTagGame.userId") || localStorage.getItem("qrTagGame.playerId");
}

function setPlayer(player, badge, userId = "") {
  const stableUserId = userId || player.playerId;

  localStorage.setItem("qrTagGame.userId", stableUserId);
  localStorage.setItem("qrTagGame.playerId", player.playerId);
  localStorage.setItem("qrTagGame.handle", player.handle);
  localStorage.setItem("qrTagGame.teamId", player.teamId);

  if (badge?.badgeCode) {
    localStorage.setItem("qrTagGame.badgeCode", badge.badgeCode);
  }
}

function getStoredPlayerInfo() {
  return {
    userId: localStorage.getItem("qrTagGame.userId"),
    playerId: localStorage.getItem("qrTagGame.playerId"),
    handle: localStorage.getItem("qrTagGame.handle"),
    teamId: localStorage.getItem("qrTagGame.teamId"),
    badgeCode: localStorage.getItem("qrTagGame.badgeCode")
  };
}

function ensureUserIdInput(handleInput) {
  let userIdInput = document.getElementById("userId");

  if (userIdInput) {
    return userIdInput;
  }

  userIdInput = document.createElement("input");
  userIdInput.id = "userId";
  userIdInput.name = "userId";
  userIdInput.type = "text";
  userIdInput.placeholder = "User ID, example: u_geoff";
  userIdInput.autocomplete = "username";

  const label = document.createElement("label");
  label.htmlFor = "userId";
  label.textContent = "User ID";

  const parent = handleInput?.parentElement;

  if (parent && handleInput) {
    parent.insertBefore(label, handleInput);
    parent.insertBefore(userIdInput, handleInput);
  }

  return userIdInput;
}

async function postJson(url, body = {}) {
  const response = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body)
  });

  const text = await response.text();
  let data = null;

  try {
    data = text ? JSON.parse(text) : {};
  } catch {
    data = { raw: text };
  }

  if (!response.ok) {
    const errorMessage = data?.error || data?.message || data?.title || data?.detail || data?.raw || `Request failed with status ${response.status}.`;
    throw new Error(errorMessage);
  }

  return data;
}

async function getJson(url) {
  const response = await fetch(url);
  const data = await response.json();

  if (!response.ok) {
    const errorMessage = data?.error || data?.message || "Request failed.";
    throw new Error(errorMessage);
  }

  return data;
}

function setupJoinPage() {
  const handleInput = document.getElementById("handle");
  const userIdInput = ensureUserIdInput(handleInput);
  const teamSelect = document.getElementById("team");
  const joinButton = document.getElementById("joinButton");
  const startButton = document.getElementById("startButton");
  const status = document.getElementById("status");
  const badgeInfo = document.getElementById("badgeInfo");

  const stored = getStoredPlayerInfo();

  if (stored.userId) userIdInput.value = stored.userId;
  if (stored.handle) handleInput.value = stored.handle;
  if (stored.teamId) teamSelect.value = stored.teamId;

  if (stored.playerId) {
    status.innerHTML = `<strong>Current Player:</strong> ${escapeHtml(stored.handle || stored.playerId)}<br><strong>User ID:</strong> ${escapeHtml(stored.userId || stored.playerId)}<br><strong>Team:</strong> ${escapeHtml(stored.teamId || "unknown")}`;
  }

  if (stored.badgeCode) {
    badgeInfo.innerHTML = `
      <strong>Badge Code:</strong> ${escapeHtml(stored.badgeCode)}<br>
      <strong>Badge URL:</strong> <a href="/scan/${encodeURIComponent(stored.badgeCode)}">/scan/${escapeHtml(stored.badgeCode)}</a>
    `;
  }

  joinButton.addEventListener("click", async () => {
    const userId = userIdInput.value.trim();
    const handle = handleInput.value.trim();
    const teamId = teamSelect.value;

    if (!userId) {
      showStatus("Enter your user ID first.", "error");
      return;
    }

    if (!handle) {
      showStatus("Enter a player name first.", "error");
      return;
    }

    joinButton.disabled = true;
    showStatus("Joining game...");

    try {
      const result = await postJson(`${getApiBase()}/api/games/${GAME_ID}/join`, { userId, handle, teamId });
      setPlayer(result.player, result.badge, userId);

      status.innerHTML = `
        <strong>Joined!</strong><br>
        User ID: ${escapeHtml(userId)}<br>
        Player: ${escapeHtml(result.player.handle)}<br>
        Team: ${escapeHtml(result.player.teamId)}<br>
        Player ID saved in this browser.
      `;
      status.className = "status success";

      if (result.badge?.badgeCode) {
        badgeInfo.innerHTML = `
          <strong>Badge Code:</strong> ${escapeHtml(result.badge.badgeCode)}<br>
          <strong>Badge URL:</strong> <a href="/scan/${encodeURIComponent(result.badge.badgeCode)}">/scan/${escapeHtml(result.badge.badgeCode)}</a>
        `;
      }

      await loadObjectives();
    } catch (error) {
      showStatus(error.message, "error");
    } finally {
      joinButton.disabled = false;
    }
  });

  startButton.addEventListener("click", async () => {
    startButton.disabled = true;
    showStatus("Starting game...");

    try {
      const result = await postJson(`${getApiBase()}/api/games/${GAME_ID}/start`);
      showStatus(`${result.message} Mode: ${result.gameMode}. Status: ${result.status}`, "success");
      await loadObjectives();
    } catch (error) {
      showStatus(error.message, "error");
    } finally {
      startButton.disabled = false;
    }
  });

  loadObjectives();
}

async function loadObjectives() {
  const objectivesElement = document.getElementById("objectives");
  if (!objectivesElement) return;

  try {
    const scoreboard = await getJson(`${getApiBase()}/api/games/${GAME_ID}/scoreboard`);

    if (!scoreboard.objectives || scoreboard.objectives.length === 0) {
      objectivesElement.innerHTML = `<p class="muted">No visible objectives for this game mode.</p>`;
      return;
    }

    objectivesElement.innerHTML = scoreboard.objectives.map(objective => `
      <div class="objective-card">
        <div>
          <strong>${escapeHtml(objective.name)}</strong>
          <span>${escapeHtml(objective.objectiveType)}</span>
          <span>Owner: ${escapeHtml(objective.currentOwnerTeamId || "Neutral")}</span>
          ${objective.expiresAtUtc ? `<span>Expires: ${escapeHtml(new Date(objective.expiresAtUtc).toLocaleTimeString())}</span>` : ""}
        </div>

        <button
          class="capture-button"
          data-objective-id="${escapeHtml(objective.objectiveId)}"
          data-objective-name="${escapeHtml(objective.name)}"
          data-objective-latitude="${objective.latitude}"
          data-objective-longitude="${objective.longitude}">
          Hold to Capture
        </button>

        <div class="capture-progress" id="progress-${escapeHtml(objective.objectiveId)}"></div>
      </div>
    `).join("");

    setupCaptureButtons();
  } catch (error) {
    objectivesElement.innerHTML = `<div class="status error">Could not load objectives: ${escapeHtml(error.message)}</div>`;
  }
}

function setupCaptureButtons() {
  document.querySelectorAll(".capture-button").forEach(button => {
    button.addEventListener("pointerdown", () => beginObjectiveHold(button));
    button.addEventListener("pointerup", cancelObjectiveHold);
    button.addEventListener("pointerleave", cancelObjectiveHold);
    button.addEventListener("pointercancel", cancelObjectiveHold);
  });
}

function beginObjectiveHold(button) {
  const playerId = getPlayerId();
  if (!playerId) {
    showStatus("Join the game before capturing objectives.", "error");
    return;
  }

  if (objectiveHoldTimer) cancelObjectiveHold();

  activeObjectiveButton = button;
  objectiveHoldStartedAt = Date.now();

  const objectiveId = button.dataset.objectiveId;
  const progressElement = document.getElementById(`progress-${objectiveId}`);

  button.classList.add("holding");
  button.textContent = `Holding... ${REQUIRED_HOLD_SECONDS}`;

  if (progressElement) progressElement.textContent = `Keep holding for ${REQUIRED_HOLD_SECONDS} seconds.`;

  objectiveHoldTimer = window.setInterval(async () => {
    const elapsedSeconds = Math.floor((Date.now() - objectiveHoldStartedAt) / 1000);
    const remainingSeconds = Math.max(REQUIRED_HOLD_SECONDS - elapsedSeconds, 0);

    button.textContent = `Holding... ${remainingSeconds}`;
    if (progressElement) progressElement.textContent = `Keep holding: ${remainingSeconds} seconds left.`;

    if (elapsedSeconds >= REQUIRED_HOLD_SECONDS) {
      const heldSeconds = elapsedSeconds;
      clearObjectiveHoldStateOnly();

      button.disabled = true;
      button.textContent = "Capturing...";

      try {
        await captureObjective(button, heldSeconds);
      } finally {
        button.disabled = false;
        button.classList.remove("holding");
        button.textContent = "Hold to Capture";
      }
    }
  }, 250);
}

function cancelObjectiveHold() {
  if (!objectiveHoldTimer || !activeObjectiveButton) return;

  const button = activeObjectiveButton;
  const objectiveId = button.dataset.objectiveId;
  const progressElement = document.getElementById(`progress-${objectiveId}`);

  button.classList.remove("holding");
  button.textContent = "Hold to Capture";
  if (progressElement) progressElement.textContent = "Capture cancelled.";

  clearObjectiveHoldStateOnly();
}

function clearObjectiveHoldStateOnly() {
  if (objectiveHoldTimer) window.clearInterval(objectiveHoldTimer);
  objectiveHoldTimer = null;
  objectiveHoldStartedAt = null;
  activeObjectiveButton = null;
}

async function syncPlayerLocation(playerId, location) {
  if (!playerId || !location) {
    return null;
  }

  return await postJson(`${getApiBase()}/api/games/${GAME_ID}/players/${encodeURIComponent(playerId)}/location`, {
    latitude: location.latitude,
    longitude: location.longitude,
    accuracyMeters: location.accuracy
  });
}

async function captureObjective(button, heldSeconds) {
  const objectiveId = button.dataset.objectiveId;
  const objectiveName = button.dataset.objectiveName;
  const progressElement = document.getElementById(`progress-${objectiveId}`);
  const playerId = getPlayerId();
  const demoMode = document.getElementById("demoLocationMode")?.checked === true;

  try {
    let location;
    if (demoMode) {
      location = {
        latitude: Number(button.dataset.objectiveLatitude),
        longitude: Number(button.dataset.objectiveLongitude)
      };
    } else {
      location = await getCurrentLocation();
    }

    await syncPlayerLocation(playerId, location);

    const result = await postJson(`${getApiBase()}/api/games/${GAME_ID}/objectives/${objectiveId}/action`, {
      playerId,
      actionType: "capture",
      latitude: location.latitude,
      longitude: location.longitude,
      accuracyMeters: location.accuracy,
      heldSeconds
    });

    if (progressElement) {
      progressElement.innerHTML = `<strong>Captured!</strong><br>${escapeHtml(result.message)}<br>+${result.pointsAwarded} points`;
      progressElement.className = "capture-progress success-text";
    }

    showStatus(`${objectiveName} captured. +${result.pointsAwarded} points`, "success");
    await loadObjectives();
  } catch (error) {
    if (progressElement) {
      progressElement.innerHTML = `<strong>Capture failed</strong><br>${escapeHtml(error.message)}`;
      progressElement.className = "capture-progress error-text";
    }
    showStatus(error.message, "error");
  }
}

function getCurrentLocation() {
  return new Promise((resolve, reject) => {
    if (!navigator.geolocation) {
      reject(new Error("This browser does not support GPS location."));
      return;
    }

    navigator.geolocation.getCurrentPosition(
      position => resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy: position.coords.accuracy }),
      error => reject(new Error(getLocationErrorMessage(error))),
      { enableHighAccuracy: true, timeout: 15000, maximumAge: 0 }
    );
  });
}

function getLocationErrorMessage(error) {
  switch (error.code) {
    case error.PERMISSION_DENIED: return "Location permission was denied.";
    case error.POSITION_UNAVAILABLE: return "Location is currently unavailable.";
    case error.TIMEOUT: return "Location request timed out.";
    default: return "Could not get location.";
  }
}

function setupScanPage() {
  const scanResult = document.getElementById("scanResult");
  const scannerPlayerId = getPlayerId();
  const badgeCode = getBadgeCodeFromUrl();

  if (!scannerPlayerId) {
    scanResult.innerHTML = `<strong>You are not joined yet.</strong><br>Go back to the join page first.`;
    scanResult.className = "status error";
    return;
  }

  if (!badgeCode) {
    scanResult.textContent = "No badge code found in the URL.";
    scanResult.className = "status error";
    return;
  }

  scanPlayerBadge(badgeCode, scannerPlayerId, scanResult);
}

function getBadgeCodeFromUrl() {
  const parts = window.location.pathname.split("/").filter(Boolean);
  return parts.length >= 2 && parts[0].toLowerCase() === "scan" ? parts[1] : null;
}

async function scanPlayerBadge(badgeCode, scannerPlayerId, outputElement) {
  outputElement.textContent = `Scanning badge ${badgeCode}...`;
  outputElement.className = "status";

  try {
    const result = await postJson(`${getApiBase()}/api/games/${GAME_ID}/badges/${encodeURIComponent(badgeCode)}/scan`, { scannerPlayerId });
    outputElement.innerHTML = `<strong>Success!</strong><br>${escapeHtml(result.message)}<br>+${result.pointsAwarded} points`;
    outputElement.className = "status success";
  } catch (error) {
    outputElement.innerHTML = `<strong>Scan Failed</strong><br>${escapeHtml(error.message)}`;
    outputElement.className = "status error";
  }
}

function setupScoreboardPage() {
  loadScoreboard();
  setInterval(loadScoreboard, 2000);
}

async function loadScoreboard() {
  const gameTitle = document.getElementById("gameTitle");
  const gameStatus = document.getElementById("gameStatus");
  const teams = document.getElementById("teams");
  const objectivesBoard = document.getElementById("objectivesBoard");
  const badgesBoard = document.getElementById("badgesBoard");
  const events = document.getElementById("events");

  try {
    const scoreboard = await getJson(`${getApiBase()}/api/games/${GAME_ID}/scoreboard`);

    gameTitle.textContent = scoreboard.gameName;
    gameStatus.textContent = `Mode: ${scoreboard.gameMode || "not started"} | Status: ${scoreboard.status}`;

    teams.innerHTML = scoreboard.teams.map(team => `
      <div class="team-card">
        <h2>${escapeHtml(team.name)}</h2>
        <div class="score">${team.score}</div>
        <div class="muted">${team.capturedObjectives} captured objectives</div>
      </div>
    `).join("");

    objectivesBoard.innerHTML = scoreboard.objectives.length === 0
      ? `<p class="muted">No active objectives.</p>`
      : scoreboard.objectives.map(objective => `
        <div class="tag-card">
          <strong>${escapeHtml(objective.name)}</strong>
          <span>${escapeHtml(objective.objectiveId)}</span>
          <span>${escapeHtml(objective.objectiveType)}</span>
          <span>Owner: ${escapeHtml(objective.currentOwnerTeamId || "Neutral")}</span>
          <span>Claims: ${objective.claimCount}</span>
        </div>
      `).join("");

    badgesBoard.innerHTML = scoreboard.playerBadges.length === 0
      ? `<p class="muted">No player badges.</p>`
      : scoreboard.playerBadges.map(badge => `
        <div class="tag-card">
          <strong>${escapeHtml(badge.playerHandle)}</strong>
          <span>${escapeHtml(badge.badgeCode)}</span>
          <span>${badge.active ? "Active" : "Inactive"}</span>
        </div>
      `).join("");

    events.innerHTML = scoreboard.recentEvents.length === 0
      ? `<p class="muted">No events yet.</p>`
      : scoreboard.recentEvents.map(event => `
        <div class="event-card">
          <strong>${escapeHtml(event.type)}</strong>
          <span>${escapeHtml(event.message)}</span>
          <span class="muted">+${event.pointsAwarded || 0} points</span>
        </div>
      `).join("");
  } catch (error) {
    gameTitle.textContent = "Scoreboard Error";
    gameStatus.textContent = error.message;
  }
}

function showStatus(message, type = "") {
  const status = document.getElementById("status");
  if (!status) return;
  status.textContent = message;
  status.className = type ? `status ${type}` : "status";
}

function escapeHtml(value) {
  return String(value)
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#039;");
}
