/* =========================================================
   PKO @ 50 : RSVP form logic
   ========================================================= */
(function () {
  'use strict';

  var CFG       = window.PKO || {};
  var STRINGS   = CFG.strings || {};
  var COUNTRIES = CFG.countries || [];
  var lang      = CFG.lang || 'fr';

  /* Expected national number length per country. Fallback is 6 to 15. */
  var LENGTHS = {
    TG: [8, 8],   BJ: [8, 10],  GH: [9, 9],   NG: [10, 11], CI: [8, 10],
    BF: [8, 8],   NE: [8, 8],   ML: [8, 8],   SN: [9, 9],   CM: [9, 9],
    GA: [7, 9],   CD: [9, 9],   CG: [9, 9],   FR: [9, 9],   BE: [8, 9],
    US: [10, 10], CA: [10, 10], GB: [9, 10],  DE: [9, 11],  ZA: [9, 9],
    KE: [9, 9],   MA: [9, 9],   TN: [8, 8],   DZ: [9, 9],   IT: [9, 10],
    ES: [9, 9],   PT: [9, 9],   CH: [9, 9],   NL: [9, 9],   IN: [10, 10]
  };

  var el = function (id) { return document.getElementById(id); };

  /* ---- Flag from ISO code ------------------------------------------------ */
  function flagOf(iso) {
    try {
      return String.fromCodePoint(
        0x1F1E6 + iso.charCodeAt(0) - 65,
        0x1F1E6 + iso.charCodeAt(1) - 65
      );
    } catch (err) {
      return iso;
    }
  }

  /* Some browsers on Windows show no flag glyphs. Detect and fall back. */
  var flagsSupported = (function () {
    try {
      var c = document.createElement('canvas');
      c.width = 24; c.height = 24;
      var x = c.getContext('2d');
      x.font = '18px sans-serif';
      x.fillText(flagOf('TG'), 0, 18);
      var a = c.toDataURL();
      x.clearRect(0, 0, 24, 24);
      x.fillText(flagOf('FR'), 0, 18);
      return a !== c.toDataURL();
    } catch (err) {
      return true;
    }
  })();

  function badge(iso) { return flagsSupported ? flagOf(iso) : iso; }

  /* ---- Fold accents so search works with or without them ----------------- */
  function fold(s) {
    return (s || '')
      .toString()
      .normalize('NFD')
      .replace(/[̀-ͯ]/g, '')
      .toLowerCase()
      .trim();
  }

  function t(key) {
    return (STRINGS[lang] && STRINGS[lang][key]) || key;
  }

  /* ---- Elements ---------------------------------------------------------- */
  var form        = el('rsvpForm');
  var nameInput   = el('fullName');
  var phoneInput  = el('phone');
  var attending   = el('attending');
  var submitBtn   = el('submitBtn');
  var dialBtn     = el('dialBtn');
  var dialFlag    = el('dialFlag');
  var dialCode    = el('dialCode');
  var sheet       = el('sheet');
  var backdrop    = el('sheetBackdrop');
  var searchInput = el('countrySearch');
  var listEl      = el('countryList');
  var emptyEl     = el('sheetEmpty');
  var stateForm   = el('stateForm');
  var stateOk     = el('stateSuccess');

  var current = null;

  /* ---- Language ---------------------------------------------------------- */
  function applyLang(next) {
    lang = next;
    document.documentElement.lang = t('html_lang');
    document.documentElement.setAttribute('data-lang', lang);
    el('fieldLang').value = lang;

    document.querySelectorAll('[data-i18n]').forEach(function (node) {
      node.textContent = t(node.getAttribute('data-i18n'));
    });

    document.querySelectorAll('[data-i18n-attr]').forEach(function (node) {
      node.getAttribute('data-i18n-attr').split('|').forEach(function (pair) {
        var bits = pair.split(':');
        node.setAttribute(bits[0], t(bits[1]));
      });
    });

    document.title = t('page_title');
    try { localStorage.setItem('pko_lang', lang); } catch (err) {}

    renderCountries(searchInput.value);
    if (current) { setCountry(current, true); }
    revalidate(true);
  }

  el('langToggle').addEventListener('click', function () {
    applyLang(lang === 'fr' ? 'en' : 'fr');
  });

  /* ---- Country picker ---------------------------------------------------- */
  function countryLabel(c) { return lang === 'fr' ? c[3] : c[2]; }

  function sorted() {
    return COUNTRIES.slice().sort(function (a, b) {
      return countryLabel(a).localeCompare(countryLabel(b), lang);
    });
  }

  function renderCountries(query) {
    var q = fold(query);
    var digits = q.replace(/[^0-9]/g, '');
    var rows = sorted().filter(function (c) {
      if (!q) { return true; }
      return fold(c[2]).indexOf(q) === 0 ||
             fold(c[3]).indexOf(q) === 0 ||
             fold(c[2]).indexOf(' ' + q) > -1 ||
             fold(c[3]).indexOf(' ' + q) > -1 ||
             fold(c[0]) === q ||
             (digits && c[1].replace('+', '').indexOf(digits) === 0);
    });

    listEl.innerHTML = '';
    rows.forEach(function (c) {
      var li  = document.createElement('li');
      var btn = document.createElement('button');
      btn.type = 'button';
      btn.setAttribute('role', 'option');
      btn.setAttribute('aria-selected', current && current[0] === c[0] ? 'true' : 'false');
      btn.innerHTML =
        '<span class="c-flag">' + badge(c[0]) + '</span>' +
        '<span class="c-name"></span>' +
        '<span class="c-dial">' + c[1] + '</span>';
      btn.querySelector('.c-name').textContent = countryLabel(c);
      btn.addEventListener('click', function () {
        setCountry(c);
        closeSheet();
        phoneInput.focus();
      });
      li.appendChild(btn);
      listEl.appendChild(li);
    });

    var none = rows.length === 0;
    emptyEl.hidden = !none;
    listEl.hidden = none;
  }

  function setCountry(c, silent) {
    current = c;
    dialFlag.textContent = badge(c[0]);
    dialCode.textContent = c[1];
    try { localStorage.setItem('pko_iso', c[0]); } catch (err) {}
    if (!silent) { validatePhone(phoneInput.value !== ''); }
  }

  function openSheet() {
    sheet.hidden = false;
    backdrop.hidden = false;
    document.body.classList.add('no-scroll');
    dialBtn.setAttribute('aria-expanded', 'true');
    requestAnimationFrame(function () {
      sheet.classList.add('open');
      backdrop.classList.add('open');
    });
    searchInput.value = '';
    renderCountries('');
    var active = listEl.querySelector('[aria-selected="true"]');
    if (active) { active.scrollIntoView({ block: 'center' }); }
    if (window.matchMedia('(min-width: 600px)').matches) {
      setTimeout(function () { searchInput.focus(); }, 220);
    }
  }

  function closeSheet() {
    sheet.classList.remove('open');
    backdrop.classList.remove('open');
    dialBtn.setAttribute('aria-expanded', 'false');
    document.body.classList.remove('no-scroll');
    setTimeout(function () {
      sheet.hidden = true;
      backdrop.hidden = true;
    }, 300);
  }

  dialBtn.addEventListener('click', openSheet);
  el('sheetClose').addEventListener('click', closeSheet);
  backdrop.addEventListener('click', closeSheet);
  searchInput.addEventListener('input', function () { renderCountries(this.value); });
  document.addEventListener('keydown', function (ev) {
    if (ev.key === 'Escape' && !sheet.hidden) { closeSheet(); }
  });

  /* ---- Validation -------------------------------------------------------- */
  function showError(wrapId, errId, message) {
    var wrap = el(wrapId);
    var err  = el(errId);
    if (message) {
      wrap.classList.add('is-error');
      wrap.classList.remove('is-valid');
      err.textContent = message;
      err.classList.add('show');
    } else {
      wrap.classList.remove('is-error');
      err.textContent = '';
      err.classList.remove('show');
    }
  }

  var NAME_RE = /^[\p{L}][\p{L}\s'’.-]{2,}$/u;

  function validateName(show) {
    var v = nameInput.value.trim().replace(/\s+/g, ' ');
    if (v.length < 3) {
      if (show) { showError('fieldNameWrap', 'nameErr', t('err_name')); }
      return false;
    }
    if (!NAME_RE.test(v)) {
      if (show) { showError('fieldNameWrap', 'nameErr', t('err_name_chars')); }
      return false;
    }
    showError('fieldNameWrap', 'nameErr', '');
    el('fieldNameWrap').classList.add('is-valid');
    return true;
  }

  function validatePhone(show) {
    var digits = phoneInput.value.replace(/[^0-9]/g, '').replace(/^0+/, '');
    var iso    = current ? current[0] : 'TG';
    var range  = LENGTHS[iso] || [6, 15];
    var ok     = digits.length >= range[0] && digits.length <= range[1];
    if (!ok) {
      if (show) { showError('fieldPhoneWrap', 'phoneErr', t('err_phone')); }
      return false;
    }
    showError('fieldPhoneWrap', 'phoneErr', '');
    el('fieldPhoneWrap').classList.add('is-valid');
    return true;
  }

  function validateAttend(show) {
    if (attending.value !== '0' && attending.value !== '1') {
      if (show) { showError('fieldAttendWrap', 'attendErr', t('err_attend')); }
      return false;
    }
    showError('fieldAttendWrap', 'attendErr', '');
    return true;
  }

  function revalidate(quiet) {
    var ok = validateName(false) && validatePhone(false) && validateAttend(false);
    submitBtn.disabled = !ok;
    if (!quiet) { return ok; }
    return ok;
  }

  nameInput.addEventListener('input', function () {
    if (el('fieldNameWrap').classList.contains('is-error')) { validateName(true); }
    revalidate(true);
  });
  nameInput.addEventListener('blur', function () {
    if (nameInput.value.trim()) { validateName(true); }
    revalidate(true);
  });

  phoneInput.addEventListener('input', function () {
    this.value = this.value.replace(/[^0-9+\s().-]/g, '');
    if (el('fieldPhoneWrap').classList.contains('is-error')) { validatePhone(true); }
    revalidate(true);
  });
  phoneInput.addEventListener('blur', function () {
    if (phoneInput.value.trim()) { validatePhone(true); }
    revalidate(true);
  });

  document.querySelectorAll('.choice').forEach(function (btn) {
    btn.addEventListener('click', function () {
      document.querySelectorAll('.choice').forEach(function (b) {
        b.setAttribute('aria-checked', 'false');
      });
      btn.setAttribute('aria-checked', 'true');
      attending.value = btn.getAttribute('data-value');
      validateAttend(true);
      revalidate(true);
    });
  });

  /* ---- Submit ------------------------------------------------------------ */
  function setLoading(on) {
    submitBtn.classList.toggle('is-loading', on);
    submitBtn.querySelector('.btn-label').textContent = on ? t('sending') : t('submit');
    submitBtn.disabled = on;
  }

  function showSuccess(data, name) {
    var yes = attending.value === '1';
    el('successTitle').textContent = t(yes ? 'ok_yes_title' : 'ok_no_title');
    el('successBody').textContent  = t(yes ? 'ok_yes_body' : 'ok_no_body').replace('%s', name);
    var note = el('successNote');
    if (data && data.updated) {
      note.textContent = t('ok_updated');
      note.hidden = false;
    } else {
      note.hidden = true;
    }
    stateForm.hidden = true;
    stateOk.hidden = false;
    el('formCard').scrollIntoView({ behavior: 'smooth', block: 'center' });
  }

  el('againBtn').addEventListener('click', function () {
    stateOk.hidden = true;
    stateForm.hidden = false;
    revalidate(true);
    el('formCard').scrollIntoView({ behavior: 'smooth', block: 'start' });
  });

  form.addEventListener('submit', function (ev) {
    ev.preventDefault();
    el('formErr').textContent = '';
    el('formErr').classList.remove('show');

    var okName   = validateName(true);
    var okPhone  = validatePhone(true);
    var okAttend = validateAttend(true);
    if (!okName || !okPhone || !okAttend) {
      var firstBad = document.querySelector('.field.is-error');
      if (firstBad) { firstBad.scrollIntoView({ behavior: 'smooth', block: 'center' }); }
      return;
    }

    var name = nameInput.value.trim().replace(/\s+/g, ' ');
    var body = new FormData();
    body.append('csrf', form.elements.csrf.value);
    body.append('full_name', name);
    body.append('country_iso', current[0]);
    body.append('country_code', current[1]);
    body.append('phone', phoneInput.value.replace(/[^0-9]/g, ''));
    body.append('attending', attending.value);
    body.append('lang', lang);
    body.append('website', form.elements.website.value);

    setLoading(true);

    fetch('api/submit.php', {
      method: 'POST',
      body: body,
      headers: { 'X-Requested-With': 'fetch' }
    })
      .then(function (res) {
        return res.json().catch(function () { throw new Error('bad-json'); });
      })
      .then(function (data) {
        setLoading(false);
        if (data && data.ok) {
          showSuccess(data, name);
        } else {
          var key = data && data.error === 'throttle' ? 'err_throttle' : 'err_server';
          var err = el('formErr');
          err.textContent = t(key);
          err.classList.add('show');
          revalidate(true);
        }
      })
      .catch(function () {
        setLoading(false);
        var err = el('formErr');
        err.textContent = t('err_network');
        err.classList.add('show');
        revalidate(true);
      });
  });

  /* ---- One gentle nudge towards the form --------------------------------- */
  /* After the poster has had a moment on screen the page glides down to the
     form. The movement is animated by hand rather than by the browser, because
     the native smooth scroll covers this distance in a few hundred milliseconds
     and reads as a jolt. It is abandoned the instant the guest does anything
     themselves, both while waiting and mid glide, so it never fights a person
     who is already reading. */

  var GLIDE_WATCH = ['wheel', 'touchstart', 'pointerdown', 'keydown'];

  /* Slow at both ends, quickest in the middle. */
  function easeInOutCubic(t) {
    return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
  }

  function glideTo(target, duration) {
    var start = window.pageYOffset;
    var distance = target - start;
    if (Math.abs(distance) < 2) { return; }

    var began = null;
    var stopped = false;
    var root = document.documentElement;

    /* The stylesheet asks for smooth scrolling, which would make the browser
       animate every single frame we set here and fight this animation. It is
       suspended for the duration and put back afterwards. */
    var previousBehaviour = root.style.scrollBehavior;
    root.style.scrollBehavior = 'auto';

    function stop() {
      stopped = true;
      root.style.scrollBehavior = previousBehaviour;
      GLIDE_WATCH.forEach(function (name) {
        window.removeEventListener(name, stop);
      });
    }

    GLIDE_WATCH.forEach(function (name) {
      window.addEventListener(name, stop, { passive: true });
    });

    function step(now) {
      if (stopped) { return; }
      if (began === null) { began = now; }

      var elapsed = now - began;
      var t = Math.min(1, elapsed / duration);

      window.scrollTo(0, start + distance * easeInOutCubic(t));

      if (t < 1) {
        requestAnimationFrame(step);
      } else {
        stop();
      }
    }

    requestAnimationFrame(step);
  }

  function autoScroll() {
    var delay = parseInt(CFG.autoScrollDelay, 10);
    var duration = parseInt(CFG.autoScrollDuration, 10) || 2200;

    if (!delay || delay < 0) { return; }
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { return; }
    if (window.pageYOffset > 10) { return; }

    var cancelled = false;
    var waiting = GLIDE_WATCH.concat(['scroll']);

    function cancel() {
      cancelled = true;
      waiting.forEach(function (name) {
        window.removeEventListener(name, cancel);
      });
    }

    waiting.forEach(function (name) {
      window.addEventListener(name, cancel, { passive: true });
    });

    setTimeout(function () {
      if (cancelled) { return; }
      cancel();

      /* Checked once more at the last moment: if the guest has moved the page
         at all by now, the nudge is not wanted. */
      if (window.pageYOffset > 10) { return; }
      if (stateOk && !stateOk.hidden) { return; }
      if (!sheet.hidden) { return; }

      var card = el('formCard');
      if (!card) { return; }

      var top = card.getBoundingClientRect().top + window.pageYOffset - 78;
      glideTo(Math.max(0, top), duration);
    }, delay);
  }

  /* ---- Boot -------------------------------------------------------------- */
  (function boot() {
    var saved = null;
    try { saved = localStorage.getItem('pko_lang'); } catch (err) {}
    if (!saved && !location.search.match(/lang=/)) {
      var nav = (navigator.language || 'fr').slice(0, 2).toLowerCase();
      saved = nav === 'en' ? 'en' : 'fr';
    }
    if (saved && saved !== lang && STRINGS[saved]) {
      applyLang(saved);
    } else {
      applyLang(lang);
    }

    var iso = null;
    try { iso = localStorage.getItem('pko_iso'); } catch (err) {}
    var pick = COUNTRIES.filter(function (c) { return c[0] === (iso || CFG.defaultIso); })[0]
            || COUNTRIES.filter(function (c) { return c[0] === 'TG'; })[0];
    setCountry(pick, true);
    renderCountries('');
    revalidate(true);
    autoScroll();
  })();

})();
