---
interface Props {
  /** Array of strings to cycle through */
  words: string[];
  /** Typing speed in ms per character */
  typeSpeed?: number;
  /** Deleting speed in ms per character */
  deleteSpeed?: number;
  /** Pause after fully typed, in ms */
  pauseAfterType?: number;
  /** Pause after fully deleted, in ms */
  pauseAfterDelete?: number;
}

const {
  words,
  typeSpeed = 120,
  deleteSpeed = 70,
  pauseAfterType = 1800,
  pauseAfterDelete = 400,
} = Astro.props;

const id = `typing-${Math.random().toString(36).slice(2, 8)}`;
---

<span id={id} class="typing-effect" aria-label={words.join(', ')}>
  <span class="typing-text"></span><span class="typing-cursor" aria-hidden="true">|</span>
</span>

<style>
  .typing-effect {
    display: inline-block;
    white-space: nowrap;
    vertical-align: bottom;
  }

  .typing-cursor {
    display: inline-block;
    margin-left: 1px;
    animation: blink 0.75s step-end infinite;
    color: var(--color-brand-500, currentColor);
    font-weight: 300;
  }

  @keyframes blink {
    0%, 100% { opacity: 1; }
    50%       { opacity: 0; }
  }
</style>

<script define:vars={{ id, words, typeSpeed, deleteSpeed, pauseAfterType, pauseAfterDelete }}>
  function startTyping() {
    const root = document.getElementById(id);
    if (!root) return;
    const textEl = root.querySelector('.typing-text');

    // Lock the element width to the widest word so the layout never shifts
    const measurer = document.createElement('span');
    measurer.setAttribute('aria-hidden', 'true');
    measurer.style.cssText = 'visibility:hidden;position:absolute;white-space:nowrap;pointer-events:none;';
    const cs = getComputedStyle(root);
    measurer.style.font = cs.font;
    measurer.style.letterSpacing = cs.letterSpacing;
    document.body.appendChild(measurer);

    let maxWidth = 0;
    for (const word of words) {
      measurer.textContent = word + '|'; // include cursor character in measurement
      maxWidth = Math.max(maxWidth, measurer.offsetWidth);
    }
    document.body.removeChild(measurer);
    root.style.minWidth = maxWidth + 'px';

    let wordIndex = 0;
    let charIndex = 0;
    let isDeleting = false;
    let timer;

    function tick() {
      const current = words[wordIndex];

      if (isDeleting) {
        charIndex--;
        textEl.textContent = current.slice(0, charIndex);

        if (charIndex === 0) {
          isDeleting = false;
          wordIndex = (wordIndex + 1) % words.length;
          timer = setTimeout(tick, pauseAfterDelete);
          return;
        }
        timer = setTimeout(tick, deleteSpeed);
      } else {
        charIndex++;
        textEl.textContent = current.slice(0, charIndex);

        if (charIndex === current.length) {
          isDeleting = true;
          timer = setTimeout(tick, pauseAfterType);
          return;
        }
        timer = setTimeout(tick, typeSpeed);
      }
    }

    // Start after a short initial delay so the page paint settles
    timer = setTimeout(tick, 600);

    // Clean up pending timer when navigating away
    document.addEventListener('astro:before-swap', () => clearTimeout(timer), { once: true });
  }

  // Run on initial load and on every client-side navigation back to this page
  document.addEventListener('astro:page-load', startTyping);
</script>
