// connectR landing page: connect/disconnect controls that talk to the
// Service Worker (which owns the iroh session).
//
// Deep links: #alias=test.app&password=1234 prefills the form and connects
// automatically. Fragments are never sent to the web server.

const aliasEl = document.getElementById('alias');
const passwordEl = document.getElementById('password');
const connectBtn = document.getElementById('connect-btn');
const disconnectBtn = document.getElementById('disconnect-btn');
const formEl = document.getElementById('connect-form');
const sessionPanel = document.getElementById('session-panel');
const sessionInfo = document.getElementById('session-info');
const statusEl = document.getElementById('status');
const shareBtn = document.getElementById('share-btn');
const shareModal = document.getElementById('share-modal');
const modalCloseX = document.getElementById('modal-close-x');

function setStatus(text, kind) {
  statusEl.textContent = text;
  statusEl.className = 'status ' + (kind || 'info');
}

function postToSW(sw, msg) {
  return new Promise((resolve) => {
    const channel = new MessageChannel();
    channel.port1.onmessage = (e) => resolve(e.data);
    sw.postMessage(msg, [channel.port2]);
  });
}

// Bound a promise so the UI can never hang on "Connecting…" forever.
function withTimeout(promise, ms, label) {
  return Promise.race([
    promise,
    new Promise((_, reject) => setTimeout(() => reject(new Error(label)), ms)),
  ]);
}

async function getActiveSW() {
  const reg = await withTimeout(navigator.serviceWorker.ready, 30000, 'Service worker not ready');
  return reg.active;
}

function showConnected(endpointId) {
  formEl.hidden = true;
  sessionPanel.hidden = false;
  sessionInfo.textContent = 'Connected. The root of this site now serves the remote service (node ' + endpointId + ').';
  setStatus('Connected — opening the remote service…', 'ok');
  window.location.href = './';
}

function showForm() {
  formEl.hidden = false;
  sessionPanel.hidden = true;
}

// Parse #alias=...&password=... (percent-decoded).
function parseFragment() {
  const out = {};
  const hash = location.hash.replace(/^#/, '');
  if (!hash) return out;
  for (const pair of hash.split('&')) {
    const eq = pair.indexOf('=');
    if (eq < 0) continue;
    const key = pair.slice(0, eq);
    const value = decodeURIComponent(pair.slice(eq + 1));
    out[key] = value;
  }
  return out;
}

// Register the Service Worker immediately (this script runs at the end of
// <body>, so the DOM is ready) — the tunnel is available as early as possible.
async function initSW() {
  if (!('serviceWorker' in navigator)) {
    setStatus('This browser does not support Service Workers — connectR needs a modern browser.', 'error');
    return null;
  }
  if (!window.isSecureContext) {
    setStatus('Service Workers require a secure context (HTTPS or localhost). Serve this page over HTTPS for remote use.', 'error');
    return null;
  }
  try {
    // 'none' avoids caching sw.js and its imported glue between deploys.
    await navigator.serviceWorker.register('sw.js', { updateViaCache: 'none' });
    const sw = await getActiveSW();
    const res = await postToSW(sw, { type: 'status' });
    if (res.type === 'connected') {
      sessionPanel.hidden = false;
      formEl.hidden = true;
      sessionInfo.textContent =
        'A session is already active (node ' + res.endpointId + '). ' +
        'The site root serves the remote service. Disconnect to return here.';
    }
    return sw;
  } catch (e) {
    setStatus('Service worker setup failed: ' + e, 'error');
    return null;
  }
}

// Deep-link handling: prefill and auto-connect once the SW is ready.
const frag = parseFragment();
if (frag.alias) aliasEl.value = frag.alias;
if (frag.password) passwordEl.value = frag.password;

initSW().then(() => {
  window.__connectrReady = true;
  if (frag.alias && frag.password) {
    setTimeout(() => connect(), 100);
  }
});

formEl.addEventListener('submit', (e) => {
  e.preventDefault();
  connect();
});

async function connect() {
  const alias = aliasEl.value.trim();
  const password = passwordEl.value;
  if (!alias || !password) {
    setStatus('Enter an alias and a password.', 'error');
    return;
  }
  setStatus('Connecting to "' + alias + '"…', 'info');
  connectBtn.disabled = true;
  try {
    const sw = await getActiveSW();
    // Give the dial its full retry window (several timed attempts), then
    // surface a clean message instead of a raw error.
    const res = await withTimeout(
      postToSW(sw, { type: 'connect', alias, password }),
      90000,
      'timed out'
    );
    if (res.type === 'connected') {
      showConnected(res.endpointId);
    } else if (res.type === 'error') {
      let clean = String(res.error || 'unknown error').replace(/^Error:\s*/, '');
      if (!/could not connect/i.test(clean)) {
        clean = 'Could not connect to "' + alias + '": ' + clean;
      }
      setStatus(clean, 'error');
      connectBtn.disabled = false;
    }
  } catch (e) {
    setStatus(
      'Could not connect to "' + alias + '" — the daemon is probably offline, or the alias/password is wrong.',
      'error'
    );
    connectBtn.disabled = false;
  }
}

disconnectBtn.addEventListener('click', async () => {
  setStatus('Disconnecting…', 'info');
  try {
    const sw = await getActiveSW();
    await postToSW(sw, { type: 'close' });
  } finally {
    showForm();
    setStatus('Disconnected. This site is no longer proxied.', 'ok');
  }
});

// Share modal
shareBtn.addEventListener('click', () => {
  shareModal.hidden = false;
});

modalCloseX.addEventListener('click', () => {
  shareModal.hidden = true;
});

shareModal.addEventListener('click', (e) => {
  if (e.target === shareModal) shareModal.hidden = true;
});
