// ===== TOTP AUTHENTICATION GATE =====
(function() {
  function updateTotpLogo() {
    const card = document.querySelector('.totp-card');
    if (!card) return;
    const bg = getComputedStyle(card).backgroundColor;
    const m = bg.match(/\d+/g);
    if (!m) return;
    const lum = (0.299 * +m[0] + 0.587 * +m[1] + 0.114 * +m[2]) / 255;
    const dark = card.querySelector('.totp-logo-dark');
    const light = card.querySelector('.totp-logo-light');
    if (dark) dark.style.display = lum > 0.5 ? 'none' : '';
    if (light) light.style.display = lum > 0.5 ? '' : 'none';
  }

  function showTotpOverlay() {
    const overlay = document.getElementById('totp-overlay');
    if (overlay) {
      overlay.classList.add('show');
      setTimeout(updateTotpLogo, 50);
      const firstInput = overlay.querySelector('.totp-digits input');
      if (firstInput) setTimeout(() => firstInput.focus(), 100);
    }
    // Refresh the "Lost access?" recovery link visibility based on server state.
    // Hides itself if TOTP is healthy; shows if unreadable/corrupt. The user
    // can still click it even when healthy — but the panel will explain there's
    // no recovery needed. Cheaper than gating it.
    if (typeof window._refreshRecoveryLink === 'function') {
      window._refreshRecoveryLink();
    }
  }

  function hideTotpOverlay() {
    const overlay = document.getElementById('totp-overlay');
    if (overlay) overlay.classList.remove('show');
  }

  function buildSsoHandoffTarget(redirect, token) {
    const parsed = new URL(redirect, window.location.origin);
    if (parsed.origin === window.location.origin) return parsed.toString();

    const suffix = SITE.tld.startsWith('.') ? SITE.tld : `.${SITE.tld}`;
    const isPrivateHost = parsed.hostname === suffix.slice(1) || parsed.hostname.endsWith(suffix);
    if (parsed.protocol !== 'https:' || !isPrivateHost) return null;
    if (!token) return parsed.toString();

    const returnPath = `${parsed.pathname}${parsed.search}${parsed.hash}`;
    parsed.pathname = '/dashcaddy-sso';
    parsed.search = '';
    parsed.hash = '';
    parsed.searchParams.set('token', token);
    parsed.searchParams.set('return', returnPath);
    return parsed.toString();
  }

  // Setup digit input UX
  const container = document.getElementById('totp-digits');
  if (container) {
    const inputs = container.querySelectorAll('input');
    inputs.forEach((input, idx) => {
      input.addEventListener('input', (e) => {
        const val = e.target.value.replace(/\D/g, '');
        e.target.value = val.slice(0, 1);
        if (val && idx < inputs.length - 1) inputs[idx + 1].focus();
        const code = Array.from(inputs).map(i => i.value).join('');
        if (code.length === 6) submitTotpCode(code);
      });

      input.addEventListener('keydown', (e) => {
        if (e.key === 'Backspace' && !e.target.value && idx > 0) {
          inputs[idx - 1].focus();
          inputs[idx - 1].value = '';
        }
      });

      input.addEventListener('paste', (e) => {
        e.preventDefault();
        const pasted = (e.clipboardData.getData('text') || '').replace(/\D/g, '');
        if (pasted.length >= 6) {
          inputs.forEach((inp, i) => { inp.value = pasted[i] || ''; });
          inputs[5].focus();
          submitTotpCode(pasted.slice(0, 6));
        }
      });
    });
  }

  async function submitTotpCode(code) {
    const errorEl = document.getElementById('totp-error');
    errorEl.textContent = 'Verifying...';
    errorEl.className = 'totp-error verifying';

    const redirect = safeSessionGet('totp_redirect');
    let serviceId = null;
    if (redirect) {
      try {
        const parsed = new URL(redirect, window.location.origin);
        const suffix = SITE.tld.startsWith('.') ? SITE.tld : `.${SITE.tld}`;
        const candidate = parsed.hostname.slice(0, -suffix.length);
        if (parsed.hostname.endsWith(suffix) && /^[a-z0-9][a-z0-9-]*$/.test(candidate)) serviceId = candidate;
      } catch (_) { /* invalid redirect is handled by the normal auth flow */ }
    }

    try {
      const res = await secureFetch('/api/v1/totp/verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code, serviceId })
      });
      const data = await res.json();

      if (data.success) {
        errorEl.textContent = '';
        // Update cached CSRF token from TOTP response (server rotates it on login)
        if (data.csrfToken) {
          csrfToken = data.csrfToken;
        }
        hideTotpOverlay();
        // Check if redirected here from another service
        if (redirect) {
          try { sessionStorage.removeItem('totp_redirect'); } catch (_) {}
          // .sami is an unregistered TLD, so browsers silently drop the
          // Domain=.sami session cookie on any OTHER *.sami subdomain (they
          // treat "sami" as the effective public suffix, same protection
          // that blocks a Domain=.com supercookie). The target service can't
          // see our session cookie no matter how it's built, so instead we
          // hand it a one-time token in the URL; its login page exchanges
          // that for its own host-only cookie via /auth/sso-exchange.
          const target = buildSsoHandoffTarget(redirect, data.ssoToken);
          if (!target) return;
          window.location.href = target;
          return;
        }
        // Initialize dashboard
        if (typeof window.initializeDashboard === 'function') {
          window.initializeDashboard();
        }
        // Resume live updates after mid-session re-auth. The auth-loss
        // handlers latched polling + SSE off when the session expired
        // (grid.js sets _dcAuthLost, live-events.js latches the stream
        // closed); a fresh login must clear both and reconnect, or the
        // dashboard stays frozen on stale data until a manual F5.
        window._dcAuthLost = false;
        if (typeof window._sseResume === 'function') window._sseResume();
        else if (typeof window._sseReconnect === 'function') window._sseReconnect();
        if (typeof window.refreshAll === 'function') window.refreshAll();
      } else {
        errorEl.textContent = data.error || 'Invalid code';
        errorEl.className = 'totp-error';
        const inputs = document.querySelectorAll('#totp-digits input');
        inputs.forEach(i => { i.value = ''; });
        inputs[0]?.focus();
      }
    } catch (e) {
      errorEl.textContent = 'Connection error';
      errorEl.className = 'totp-error';
    }
  }

  // Handle ?auth=required redirect from Caddy SSO.
  // DC-049: if the auth-gate module will handle this (multi-provider
  // installs), skip our TOTP-only branch entirely. Single-provider
  // TOTP-only installs continue to use this path because auth-gate.js
  // delegates back to window._showTotpOverlay().
  const __dc_049_skip = !!(window.__dc_049_handled);
  if (!__dc_049_skip && urlParams.get('auth') === 'required') {
    const returnUrl = urlParams.get('return');
    if (returnUrl) {
      // Validate redirect URL: must be same-origin or hostname must end with our TLD
      // (prevents open redirect via includes() bypass like evil.com?q=.sami)
      try {
        const parsed = new URL(returnUrl, window.location.origin);
        const hostname = parsed.hostname;
        const isSameOrigin = parsed.origin === window.location.origin;
        const tldSuffix = SITE.tld.startsWith('.') ? SITE.tld : '.' + SITE.tld;
        const isOurTld = hostname.endsWith(tldSuffix) || hostname === tldSuffix.substring(1);
        if (isSameOrigin || isOurTld) {
          safeSessionSet('totp_redirect', returnUrl);
        }
      } catch (_) {
        // Invalid URL — reject redirect
      }
    }
    // Clean URL
    window.history.replaceState({}, '', window.location.pathname);
  }

  window._showTotpOverlay = showTotpOverlay;
})();
