/* Game1 entry point built with Webpack Encore */
import './styles/game1.css';

let sequenceFinished = false;
let stillPlayingSound = true;
let navigatingAway = false;

function goTo(url) {
  navigatingAway = true;
  window.location.href = url;
}

function subscribeToMercure(mercurePublicUrl, topic, myScreen, wonUrl, lostUrl) {
  try {
    const url = mercurePublicUrl + '?topic=' + encodeURIComponent(topic);
    const es = new EventSource(url);

    es.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        console.log('[Mercure][game1] Update:', data);

        if (data && !Array.isArray(data) && data.type === 'game_finished') {
          const destination = data.status === 'won' ? wonUrl : lostUrl;
          if (destination) {
            goTo(destination);
          }
          return;
        }

        // data is [sendTo, message, messageType?] - messageType defaults to 'mainframe' (green)
        if (Array.isArray(data) && data.length >= 2) {
          const sendTo = parseInt(data[0]);
          // Filter: 0 means everyone, otherwise must match myScreen
          if (sendTo !== 0 && sendTo !== parseInt(myScreen)) {
            console.log('[Mercure][game1] Message not for this player, skipping.');
            return;
          }

          const messageContainer = document.getElementById('message-container');
          if (messageContainer) {
            appendResultMessage(messageContainer, data[1], data[2] || 'mainframe');
            window.scrollTo(0, document.body.scrollHeight);
            if(stillPlayingSound)
            playSound();
            console.log('[Mercure][game1] sequenceFinished status:', sequenceFinished);
            if (sequenceFinished) {
              flashRed();
            }
          }
        }
      } catch (e) {
        console.log('[Mercure][game1] Raw event:', event.data);
      }
    };

    es.onerror = (err) => {
      console.warn('[Mercure][game1] EventSource error:', err);
    };

    console.log('[Mercure][game1] Subscribed to', url);
  } catch (e) {
    console.error('[Mercure][game1] Failed to subscribe:', e);
  }
}

function playSound() {
  const sound = document.getElementById('message-sound');
  if (sound) {
    sound.currentTime = 0;
    sound.play().catch(e => console.warn('[Audio] Playback failed:', e));
  }
}

function flashRed() {
  console.log('[Game1] Triggering flashRed');
  const body = document.body;
  body.classList.remove('flash-red');
  void body.offsetWidth; // Trigger reflow to restart animation
  body.classList.add('flash-red');

  // Also remove it after animation finishes so it's clean for inspection
  setTimeout(() => {
    body.classList.remove('flash-red');
    console.log('[Game1] Removed flash-red class');
  }, 150);
}

let lockRevealTimer = null;
let lockExpireTimer = null;
let lockCountdownTimer = null;
let currentLockedAt = null;

function lockMessageClass(messageType) {
  if (messageType === 'virus') return 'message-virus';
  if (messageType === 'mainframe') return 'message-mainframe';
  return '';
}

function appendResultMessage(container, text, messageType) {
  const msgEl = document.createElement('div');
  msgEl.className = ('message ' + lockMessageClass(messageType)).trim();
  msgEl.textContent = text;
  container.appendChild(msgEl);
}

function setInputDisabled(disabled) {
  const inputField = document.getElementById('input-message');
  if (inputField) {
    inputField.disabled = disabled;
  }
}

function clearLockTimers() {
  if (lockRevealTimer) { clearTimeout(lockRevealTimer); lockRevealTimer = null; }
  if (lockExpireTimer) { clearTimeout(lockExpireTimer); lockExpireTimer = null; }
  if (lockCountdownTimer) { clearInterval(lockCountdownTimer); lockCountdownTimer = null; }
}

function clearLock() {
  clearLockTimers();
  currentLockedAt = null;
  document.body.classList.remove('locked');
  const banner = document.getElementById('lock-banner');
  if (banner) banner.style.display = 'none';
  setInputDisabled(false);
}

function updateLockCountdown(unlockAtMs) {
  const countdownEl = document.getElementById('lock-countdown');
  if (!countdownEl) return;
  const remaining = Math.max(0, Math.ceil((unlockAtMs - Date.now()) / 1000));
  countdownEl.textContent = remaining + 's';
}

async function fetchLockReveal(apiEchoUrl, messageContainer) {
  if (!apiEchoUrl) return;
  try {
    const response = await fetchJson(apiEchoUrl, {
      method: 'POST',
      body: { message: '', ts: new Date().toISOString() },
    });
    const result = response && response.result;
    if (result && Array.isArray(result.result)) {
      result.result.forEach(text => appendResultMessage(messageContainer, text, result.messageType));
      window.scrollTo(0, document.body.scrollHeight);
    }
    if (result && result.locked === false) {
      clearLock();
      return;
    }
    // Code has been revealed (or already was), let the player try /unlock
    setInputDisabled(false);
  } catch (e) {
    console.error('[Game1] Failed to fetch lock reveal:', e);
  }
}

function applyLock(lockData, apiEchoUrl, messageContainer) {
  if (currentLockedAt === lockData.lockedAt) {
    return; // already tracking this lock, avoid re-fetching/duplicating messages
  }
  currentLockedAt = lockData.lockedAt;

  clearLockTimers();

  const banner = document.getElementById('lock-banner');
  if (banner) banner.style.display = 'flex';
  document.body.classList.add('locked');

  const revealAtMs = lockData.revealAt * 1000;
  const unlockAtMs = lockData.unlockAt * 1000;
  const now = Date.now();

  if (now < revealAtMs) {
    setInputDisabled(true);
    lockRevealTimer = setTimeout(() => fetchLockReveal(apiEchoUrl, messageContainer), revealAtMs - now);
  } else {
    fetchLockReveal(apiEchoUrl, messageContainer);
  }

  lockExpireTimer = setTimeout(() => clearLock(), Math.max(0, unlockAtMs - now));

  updateLockCountdown(unlockAtMs);
  lockCountdownTimer = setInterval(() => {
    updateLockCountdown(unlockAtMs);
    if (Date.now() >= unlockAtMs) {
      clearInterval(lockCountdownTimer);
      lockCountdownTimer = null;
    }
  }, 1000);
}

let filesRemovalTimer = null;
let scheduledFilesRemovalDeadline = null;

async function pingFilesRemovalDeadline(apiEchoUrl) {
  if (!apiEchoUrl) return;
  try {
    // A no-op message is enough to make the server evaluate the deadline server-side;
    // the actual restore notice (if any) arrives for everyone via the Mercure broadcast.
    await fetchJson(apiEchoUrl, {
      method: 'POST',
      body: { message: '', ts: new Date().toISOString() },
    });
  } catch (e) {
    console.error('[Game1] Failed to ping files-removal deadline:', e);
  }
}

function scheduleFilesRemovalCheck(deadline, apiEchoUrl) {
  if (!deadline || scheduledFilesRemovalDeadline === deadline) {
    return; // nothing to (re)schedule
  }
  scheduledFilesRemovalDeadline = deadline;

  if (filesRemovalTimer) {
    clearTimeout(filesRemovalTimer);
    filesRemovalTimer = null;
  }

  const delay = Math.max(0, deadline * 1000 - Date.now());
  filesRemovalTimer = setTimeout(() => {
    filesRemovalTimer = null;
    scheduledFilesRemovalDeadline = null;
    pingFilesRemovalDeadline(apiEchoUrl);
  }, delay);
}

async function fetchJson(url, options = {}) {
  const opts = { ...options };
  const headers = new Headers(opts.headers || {});
  headers.set('Accept', 'application/json');
  if (opts.body !== undefined && typeof opts.body !== 'string') {
    headers.set('Content-Type', 'application/json');
    opts.body = JSON.stringify(opts.body);
  }
  // Useful convention for server-side checks
  if (!headers.has('X-Requested-With')) {
    headers.set('X-Requested-With', 'XMLHttpRequest');
  }
  opts.headers = headers;
  const res = await fetch(url, opts);
  const text = await res.text();
  let data;
  try { data = text ? JSON.parse(text) : null; } catch (e) { data = text; }
  if (!res.ok) {
    const err = new Error('HTTP ' + res.status + ' ' + res.statusText);
    console.error('[API][game1]', err, data);
    throw err;
  }
  return data;
}

document.addEventListener('DOMContentLoaded', async () => {
  // Simple boot log so you can verify it in the browser console
  // and confirm this specific bundle is loaded on the Game Hub page.
  console.log('Game1 bundle loaded');

  // Example: add a CSS class to <body> so page-specific styles can apply
  document.body.classList.add('game1-page');

  // Look for config injected by Twig in the page
  const cfgEl = document.getElementById('mercure-config');

  // Prevent/warn on page reload, except for our own win/lose redirects
  window.addEventListener('beforeunload', (event) => {
    if (navigatingAway) {
      return;
    }
    // Standard way to trigger the browser's confirmation dialog
    event.preventDefault();
    // Included for compatibility with older browsers
    event.returnValue = '';
  });

  if (!cfgEl) {
    console.warn('[Mercure][game1] #mercure-config element not found on page');
    return;
  }

  const mercurePublicUrl = cfgEl.dataset.mercurePublicUrl;
  const topic = cfgEl.dataset.topic;
  const screen = cfgEl.dataset.screen;
  const apiPingUrl = cfgEl.dataset.apiPingUrl;
  const apiEchoUrl = cfgEl.dataset.apiEchoUrl;
  const apiCheckFinishedUrl = cfgEl.dataset.apiCheckFinishedUrl;
  const lostUrl = cfgEl.dataset.lostUrl;
  const wonUrl = cfgEl.dataset.wonUrl;
  const lockLockedAt = cfgEl.dataset.lockLockedAt;
  const lockRevealAt = cfgEl.dataset.lockRevealAt;
  const lockUnlockAt = cfgEl.dataset.lockUnlockAt;
  const filesRemovalDeadline = cfgEl.dataset.filesRemovalDeadline;

  // Resume the auto-restore timer after a page refresh, if a window is already running
  if (filesRemovalDeadline) {
    scheduleFilesRemovalCheck(parseInt(filesRemovalDeadline, 10), apiEchoUrl);
  }

  if (mercurePublicUrl && topic) {
    subscribeToMercure(mercurePublicUrl, topic, screen, wonUrl, lostUrl);
  } else {
    console.warn('[Mercure][game1] Missing data attributes on #mercure-config');
  }

  // Timer logic
  const timerEl = document.getElementById('game-timer');
  if (timerEl && timerEl.dataset.endTime) {
    const endTime = parseInt(timerEl.dataset.endTime) * 1000;

    const updateTimer = async () => {
      const now = Date.now();
      const diff = endTime - now;

      if (diff <= 0) {
        timerEl.textContent = '00:00:00';

        // Timer reached zero, check with server
        if (apiCheckFinishedUrl && lostUrl) {
          try {
            const response = await fetchJson(apiCheckFinishedUrl, { method: 'POST' });
            if (response && response.finished) {
              goTo(response.status === 'won' && wonUrl ? wonUrl : lostUrl);
              return; // Stop the timer loop
            }
          } catch (e) {
            console.error('[API][game1] Failed to check finished status:', e);
          }
        }

        // Even if check failed or not finished, stop the loop if diff <= 0
        // (though technically if the server says not finished, we might want to keep checking,
        // but 00:00:00 usually means it's over).
        return;
      }

      const hours = Math.floor(diff / (1000 * 60 * 60));
      const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = Math.floor((diff % (1000 * 60)) / 1000);

      const hDisplay = hours.toString().padStart(2, '0');
      const mDisplay = minutes.toString().padStart(2, '0');
      const sDisplay = seconds.toString().padStart(2, '0');

      timerEl.textContent = `${hDisplay}:${mDisplay}:${sDisplay}`;
      setTimeout(updateTimer, 1000);
    };

    updateTimer();
  }

  // Demo API calls
  try {
    if (apiPingUrl) {
      const ping = await fetchJson(apiPingUrl);
      console.log('[API][game1] ping →', ping);
    } else {
      console.warn('[API][game1] data-api-ping-url missing');
    }

    if (apiEchoUrl) {
      const echo = await fetchJson(apiEchoUrl, {
        method: 'POST',
        body: { message: 'from game1.js', ts: new Date().toISOString() },
      });
      console.log('[API][game1] echo →', echo);
    } else {
      console.warn('[API][game1] data-api-echo-url missing');
    }
  } catch (e) {
    console.error('[API][game1] Request failed:', e);
  }

  // Add messages to message-container
  const messageContainer = document.getElementById('message-container');
  if (messageContainer) {
    let messages = [
        ['System initializing...', 500],
        ['Connection established.', 200],
        ['Welcome agent to the mainframe.', 1000],
        ['Scanning...', 3000],
        ['Virus detected.', 500],
        ['Starting Mainframe help modus...', 2000],
        ['Help modus activated.', 500],
        ['Blocking virus activated', 0]
    ];

    let currentMessageIndex = 0;

    const printNextMessage = () => {
      if (currentMessageIndex < messages.length) {

        const msg = messages[currentMessageIndex];
        const msgEl = document.createElement('div');

        let extraClass = '';
        if(msg[2])
          extraClass = msg[2];

        msgEl.className = 'message ' + extraClass;
        msgEl.textContent = msg[0];
        messageContainer.appendChild(msgEl);
        window.scrollTo(0, document.body.scrollHeight);

        playSound();

        currentMessageIndex++;
        setTimeout(printNextMessage, msg[1]);
        if (sequenceFinished) {
          flashRed();
        }
      } else {
        // After it has printed a set of messages, it has to start a timer of 2 seconds
        console.log('[Game1] All messages printed. Starting 2s timer to expand message-container height...');
        setTimeout(() => {
          messageContainer.style.height = '400vh';
          const inputField = document.getElementById('input-message');
          inputField.disabled = false;

          // Add event listener for Enter key
          inputField.addEventListener('keypress', async (e) => {
            if (e.key === 'Enter') {
              stillPlayingSound = false;
              sequenceFinished = false;
              const message = inputField.value.trim();

              const msgEl = document.createElement('div');
              msgEl.className = 'message';
              msgEl.textContent = message;
              messageContainer.appendChild(msgEl);

              if (message && apiEchoUrl) {
                inputField.value = '';
                try {
                  const response = await fetchJson(apiEchoUrl, {
                    method: 'POST',
                    body: { message, ts: new Date().toISOString() },
                  });
                  console.log('[API][game1] message sent →', response);
                  if (response && response.result && Array.isArray(response.result.result)) {
                    response.result.result.forEach(text => appendResultMessage(messageContainer, text, response.result.messageType));
                    window.scrollTo(0, document.body.scrollHeight);
                  }
                  if (response && response.result) {
                    if (response.result.gameWon === true && wonUrl) {
                      goTo(wonUrl);
                      return;
                    }

                    if (response.result.locked === true) {
                      applyLock({
                        lockedAt: response.result.lockedAt,
                        revealAt: response.result.revealAt,
                        unlockAt: response.result.unlockAt,
                      }, apiEchoUrl, messageContainer);
                    } else if (response.result.locked === false) {
                      clearLock();
                    }

                    if (response.result.filesRemovalDeadline) {
                      scheduleFilesRemovalCheck(response.result.filesRemovalDeadline, apiEchoUrl);
                    }
                  }
                } catch (err) {
                  console.error('[API][game1] Failed to send message:', err);
                }
              }
            }
          });

          console.log('[Game1] message-container height changed to 400vh and input enabled');
          sequenceFinished = true;
          console.log('[Game1] sequenceFinished is now TRUE');

          // Restore an in-progress lock after a page refresh
          if (lockUnlockAt && parseInt(lockUnlockAt, 10) * 1000 > Date.now()) {
            applyLock({
              lockedAt: parseInt(lockLockedAt, 10),
              revealAt: parseInt(lockRevealAt, 10),
              unlockAt: parseInt(lockUnlockAt, 10),
            }, apiEchoUrl, messageContainer);
          }
        }, 2000);
      }
    };

    printNextMessage();
  }
});
