/**
 * main.js — HER Home Enhancement and Renovation
 *
 * Sections:
 *   1. Navigation — mobile hamburger (focus trap, Escape, ARIA)
 *   2. Navigation — compact + CTA visibility on scroll
 *   3. Scroll-triggered fade-up animation (IntersectionObserver)
 *   4. Quote form — validation, character counter, submission
 */

(function () {
  'use strict';

  /* ============================================================
     1. Navigation — Mobile Hamburger
  ============================================================ */

  var navToggle      = document.querySelector('.nav__toggle');
  var navOverlay     = document.querySelector('.nav__overlay');
  var navOverlayClose = document.querySelector('.nav__overlay-close');

  /**
   * Returns an array of focusable elements within a container,
   * in DOM order, skipping anything with tabindex="-1".
   */
  function getFocusable(container) {
    return Array.from(
      container.querySelectorAll(
        'a[href], button:not([disabled]), input:not([disabled]), ' +
        'select:not([disabled]), textarea:not([disabled]), ' +
        '[tabindex]:not([tabindex="-1"])'
      )
    );
  }

  function openNav() {
    if (!navOverlay || !navToggle) return;
    navOverlay.removeAttribute('hidden');
    navToggle.setAttribute('aria-expanded', 'true');
    navToggle.setAttribute('aria-label', 'Close menu');
    document.body.classList.add('nav-open');
    // Move focus into overlay — first focusable item
    var focusable = getFocusable(navOverlay);
    if (focusable.length) focusable[0].focus();
  }

  function closeNav() {
    if (!navOverlay || !navToggle) return;
    navOverlay.setAttribute('hidden', '');
    navToggle.setAttribute('aria-expanded', 'false');
    navToggle.setAttribute('aria-label', 'Open menu');
    document.body.classList.remove('nav-open');
    navToggle.focus();
  }

  if (navToggle) {
    navToggle.addEventListener('click', function () {
      if (navToggle.getAttribute('aria-expanded') === 'true') {
        closeNav();
      } else {
        openNav();
      }
    });
  }

  if (navOverlayClose) {
    navOverlayClose.addEventListener('click', closeNav);
  }

  if (navOverlay) {
    // Close when a nav link inside the overlay is clicked
    navOverlay.addEventListener('click', function (e) {
      if (e.target && e.target.tagName === 'A') {
        closeNav();
      }
    });
  }

  // Keyboard handling: focus trap (Tab/Shift-Tab) and Escape close
  document.addEventListener('keydown', function (e) {
    if (!navOverlay || navOverlay.hasAttribute('hidden')) return;

    if (e.key === 'Escape') {
      closeNav();
      return;
    }

    if (e.key === 'Tab') {
      var focusable = getFocusable(navOverlay);
      if (!focusable.length) {
        e.preventDefault();
        return;
      }
      var first = focusable[0];
      var last  = focusable[focusable.length - 1];
      if (e.shiftKey) {
        // Shift-Tab on first → wrap to last
        if (document.activeElement === first) {
          e.preventDefault();
          last.focus();
        }
      } else {
        // Tab on last → wrap to first
        if (document.activeElement === last) {
          e.preventDefault();
          first.focus();
        }
      }
    }
  });


  /* ============================================================
     2. Navigation — Compact on Scroll
  ============================================================ */

  var navEl  = document.querySelector('.nav');
  // #nav-cta starts with style="display:none" in HTML; we manage via style.display
  var navCta = document.getElementById('nav-cta');

  function handleNavScroll() {
    var y = window.scrollY;

    if (navEl) {
      if (y > 80) {
        navEl.classList.add('nav--compact');
      } else {
        navEl.classList.remove('nav--compact');
      }
    }

    if (navCta) {
      // Remove the inline display:none to show; restore it to hide
      navCta.style.display = y > 400 ? '' : 'none';
    }
  }

  window.addEventListener('scroll', handleNavScroll, { passive: true });
  // Apply immediately so state is correct before any scroll occurs
  handleNavScroll();


  /* ============================================================
     3. Scroll-triggered Fade-Up Animation
  ============================================================ */

  (function initFadeUp() {
    // Respect user's motion preference — CSS already handles the fallback
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    if (!('IntersectionObserver' in window)) return;

    var fadeEls = document.querySelectorAll('.fade-up');
    if (!fadeEls.length) return;

    var observer = new IntersectionObserver(function (entries) {
      entries.forEach(function (entry) {
        if (entry.isIntersecting) {
          entry.target.classList.add('is-visible');
          observer.unobserve(entry.target); // one-shot — never toggle back
        }
      });
    }, { threshold: 0.15 });

    fadeEls.forEach(function (el) {
      observer.observe(el);
    });
  }());


  /* ============================================================
     4. Quote Form
  ============================================================ */

  (function initQuoteForm() {

    /** POST destination — replace with your API Gateway invoke URL */
    var FORM_ENDPOINT = 'REPLACE_WITH_API_GATEWAY_URL';

    var form = document.getElementById('quote-form');
    if (!form) return; // form not present on this page — bail out

    // ── Element references ────────────────────────────────────

    var honeypot    = form.querySelector('input[name="website"]');
    var errorBanner = form.querySelector('.form-error-banner');
    var successPanel = form.querySelector('.form-success');
    var submitBtn   = form.querySelector('.form__submit');
    var charCountEl = document.getElementById('description-char-count');
    var descriptionEl = document.getElementById('description');

    var FIELD_IDS = ['name', 'address', 'phone', 'email', 'description'];

    // Save original submit button label so we can restore it after an error
    var originalBtnText = submitBtn ? (submitBtn.textContent.trim() || 'Send Request') : 'Send Request';

    // Ensure the error banner is focusable (needed for focus management)
    if (errorBanner && !errorBanner.hasAttribute('tabindex')) {
      errorBanner.setAttribute('tabindex', '-1');
    }

    // ── Validation rules ──────────────────────────────────────

    var RULES = {
      name: function (val) {
        return val.trim().length >= 2
          ? null
          : 'Please enter your name.';
      },
      address: function (val) {
        return val.trim().length >= 3
          ? null
          : 'Please enter the service address or city.';
      },
      phone: function (val) {
        return /^[+\d][\d\s\-().]{6,}$/.test(val.trim())
          ? null
          : 'Please enter a valid phone number.';
      },
      email: function (val) {
        var v = val.trim();
        return (v.indexOf('@') !== -1 && v.indexOf('.') !== -1)
          ? null
          : 'Please enter a valid email address.';
      },
      description: function (val) {
        return val.trim().length >= 10
          ? null
          : 'Please describe your project (at least 10 characters).';
      }
    };

    // ── Helper: show / clear individual field errors ──────────

    /**
     * Mark a field as invalid and display its error message.
     * @param {string} fieldId - The field's id attribute value
     * @param {string} message - The error text to display
     */
    function showError(fieldId, message) {
      var field   = document.getElementById(fieldId);
      var errorEl = document.getElementById(fieldId + '-error');
      if (field)   field.setAttribute('aria-invalid', 'true');
      if (errorEl) {
        errorEl.textContent = message;
        errorEl.removeAttribute('hidden');
      }
    }

    /**
     * Mark a field as valid and hide its error message.
     * @param {string} fieldId - The field's id attribute value
     */
    function clearError(fieldId) {
      var field   = document.getElementById(fieldId);
      var errorEl = document.getElementById(fieldId + '-error');
      if (field)   field.setAttribute('aria-invalid', 'false');
      if (errorEl) {
        errorEl.textContent = '';
        errorEl.setAttribute('hidden', '');
      }
    }

    // ── Helper: validate one field ────────────────────────────

    /**
     * Run validation for a single field. Shows or clears its error.
     * @param {string} fieldId
     * @returns {boolean} true if valid
     */
    function validateField(fieldId) {
      var field = document.getElementById(fieldId);
      if (!field) return true; // element absent — treat as valid
      var rule = RULES[fieldId];
      if (!rule) return true;
      var errorMsg = rule(field.value);
      if (errorMsg) {
        showError(fieldId, errorMsg);
        return false;
      }
      clearError(fieldId);
      return true;
    }

    // ── Helper: validate all fields ───────────────────────────

    /**
     * Run validation on every form field. Does NOT short-circuit —
     * all fields are checked so every error is visible at once.
     * @returns {boolean} true only if every field is valid
     */
    function validateAll() {
      var allValid = true;
      FIELD_IDS.forEach(function (id) {
        if (!validateField(id)) allValid = false;
      });
      return allValid;
    }

    // ── Blur-time validation ──────────────────────────────────

    FIELD_IDS.forEach(function (id) {
      var field = document.getElementById(id);
      if (field) {
        field.addEventListener('blur', function () {
          validateField(id);
        });
      }
    });

    // ── Character counter ─────────────────────────────────────

    if (descriptionEl && charCountEl) {
      // Set initial display
      charCountEl.textContent = '0 / 2000';

      descriptionEl.addEventListener('input', function () {
        var len = descriptionEl.value.length;
        charCountEl.textContent = len + ' / 2000';
        // classList.toggle(cls, force) is supported in all modern browsers
        charCountEl.classList.toggle('warn', len > 1800);
      });
    }

    // ── Banner helpers ────────────────────────────────────────

    /**
     * Display the error banner with a given message and move focus to it.
     * @param {string} message
     */
    function showBannerError(message) {
      if (!errorBanner) return;
      // Update the inner <p> if present to preserve markup; else use textContent
      var bannerP = errorBanner.querySelector('p');
      if (bannerP) {
        bannerP.textContent = message;
      } else {
        errorBanner.textContent = message;
      }
      errorBanner.removeAttribute('hidden');
      errorBanner.focus();
    }

    function hideBanner() {
      if (!errorBanner) return;
      errorBanner.setAttribute('hidden', '');
      var bannerP = errorBanner.querySelector('p');
      if (bannerP) {
        bannerP.textContent = '';
      } else {
        errorBanner.textContent = '';
      }
    }

    // ── Success display ───────────────────────────────────────

    /**
     * Hide all form content except the success panel, then show the
     * success panel and move focus to its title.
     * (.form-success lives inside <form> in the HTML, so we cannot
     *  simply hide the <form> element — instead we hide its siblings.)
     */
    function showSuccess() {
      // Hide every direct child of the form except .form-success
      Array.from(form.children).forEach(function (child) {
        if (!child.classList.contains('form-success')) {
          child.setAttribute('hidden', '');
        }
      });

      if (successPanel) {
        successPanel.removeAttribute('hidden');
        var title = successPanel.querySelector('.form-success__title');
        if (title) {
          // Ensure the heading is programmatically focusable
          if (!title.hasAttribute('tabindex')) {
            title.setAttribute('tabindex', '-1');
          }
          title.focus();
        }
      }
    }

    // ── Form state helpers ────────────────────────────────────

    var allInputs; // captured at submit time, used in onError

    function disableForm() {
      allInputs = form.querySelectorAll('input, textarea, select, button');
      allInputs.forEach(function (el) { el.disabled = true; });
      if (submitBtn) submitBtn.textContent = 'Sending\u2026'; // "Sending…"
      form.setAttribute('aria-busy', 'true');
    }

    function enableForm() {
      if (allInputs) {
        allInputs.forEach(function (el) { el.disabled = false; });
      }
      if (submitBtn) submitBtn.textContent = originalBtnText;
      form.removeAttribute('aria-busy');
    }

    // ── Submit handler ────────────────────────────────────────

    form.addEventListener('submit', function (e) {
      e.preventDefault();

      // Reset error banner from any previous attempt
      hideBanner();

      // 1. Validate — show all errors simultaneously
      var isValid = validateAll();
      if (!isValid) {
        showBannerError('Please correct the errors above before submitting.');
        return;
      }

      // 2. Honeypot check — silently fake success so bots see no difference
      if (honeypot && honeypot.value) {
        showSuccess();
        return;
      }

      // 3. Lock the form while the request is in flight
      disableForm();

      // 4. Build the JSON payload
      var payload = {
        name:        (document.getElementById('name')        || { value: '' }).value,
        address:     (document.getElementById('address')     || { value: '' }).value,
        phone:       (document.getElementById('phone')       || { value: '' }).value,
        email:       (document.getElementById('email')       || { value: '' }).value,
        description: (document.getElementById('description') || { value: '' }).value,
        honeypot:    honeypot ? honeypot.value : ''
      };

      // 5. 10-second hard timeout via AbortController
      var controller = new AbortController();
      var abortTimer = setTimeout(function () {
        controller.abort();
      }, 10000);

      // 6. POST to the API
      fetch(FORM_ENDPOINT, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
        signal: controller.signal
      })
      .then(function (response) {
        // Treat any non-2xx HTTP status as an error
        if (!response.ok) {
          throw new Error('HTTP error ' + response.status);
        }
        return response.json();
      })
      .then(function (data) {
        clearTimeout(abortTimer);
        // Some API Gateway patterns return { ok: false } with HTTP 200
        if (data && data.ok === false) {
          throw new Error(data.message || 'Server returned an error.');
        }
        // 7. Success path
        form.removeAttribute('aria-busy'); // clear before showSuccess hides children
        showSuccess();
      })
      .catch(function () {
        clearTimeout(abortTimer);
        // 8. Error path — re-enable form and surface the error
        enableForm();
        showBannerError(
          'Something went wrong sending your request. ' +
          'Please try again, or contact Diana directly.'
        );
      });
    });

  }()); // end initQuoteForm

}()); // end top-level IIFE
