/* global React */
const { useState, useEffect, useRef } = React;

// Renders <picture> with WebP source + original as fallback for local images.
// External URLs (http/https) and unknown extensions pass through as plain <img>.
function Img({ src, ...rest }) {
  if (!src || /^(https?:)?\/\//.test(src) || !/\.(jpe?g|png)$/i.test(src)) {
    return <img src={src} {...rest} />;
  }
  const webp = src.replace(/\.(jpe?g|png)$/i, '.webp');
  return (
    <picture>
      <source srcSet={webp} type="image/webp" />
      <img src={src} {...rest} />
    </picture>
  );
}

// "+" cross icon for pill button
const XIco = () => (
  <span className="ico" aria-hidden>
    <svg viewBox="0 0 12 12" fill="none">
      <path d="M6 1v10M1 6h10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
    </svg>
  </span>
);

function useMagnetic(strength = 0.32, enabled = true, radius = 140) {
  const ref = useRef(null);
  useEffect(() => {
    if (!enabled) return;
    if (window.matchMedia('(pointer: coarse)').matches) return;
    const el = ref.current; if (!el) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2;
      const cy = r.top + r.height / 2;
      const dist = Math.hypot(e.clientX - cx, e.clientY - cy);
      if (dist > radius) { el.style.transform = ''; return; }
      const falloff = 1 - dist / radius;
      const dx = (e.clientX - cx) * strength * falloff;
      const dy = (e.clientY - cy) * strength * falloff;
      el.style.transform = `translate(${dx}px, ${dy}px)`;
    };
    const onLeave = () => { el.style.transform = ''; };
    document.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => { document.removeEventListener('mousemove', onMove); el.removeEventListener('mouseleave', onLeave); };
  }, [strength, enabled, radius]);
  return ref;
}

function Btn({ children, onClick, outline, magnet = true, lg = false, className = '' }) {
  const ref = useMagnetic(0.32, magnet);
  return (
    <span style={{ display: 'inline-block' }}>
      <button ref={ref} onClick={onClick} className={`btn magnet ${outline ? 'outline' : ''} ${lg ? 'lg' : ''} ${className}`}>
        <span>{children}{'\u00a0'}</span>
        <XIco />
      </button>
    </span>
  );
}

function Tag({ children }) { return <span className="tag">{children}</span>; }

function MagneticNavLink({ children, active, onClick }) {
  const ref = useMagnetic(0.24, true, 75);
  return <a ref={ref} className={`magnet-link ${active ? 'active' : ''}`} onClick={onClick}>{children}</a>;
}

function Magnetic({ children, as: T = 'a', className = '', strength = 0.22, radius = 65, onClick, href, ...props }) {
  const ref = useMagnetic(strength, true, radius);
  const isClickable = !!onClick && !href;
  const handleKey = isClickable ? (e) => {
    if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(e); }
  } : undefined;
  return (
    <T
      ref={ref}
      className={`magnet-link ${className}`.trim()}
      onClick={onClick}
      href={href}
      tabIndex={isClickable ? 0 : undefined}
      role={isClickable ? 'button' : undefined}
      onKeyDown={handleKey}
      {...props}
    >
      {children}
    </T>
  );
}

function StudioStatus() {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 60000);
    return () => clearInterval(id);
  }, []);
  const time = new Intl.DateTimeFormat('de-DE', {
    hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Berlin'
  }).format(now);
  const hour = parseInt(new Intl.DateTimeFormat('en-GB', {
    hour: '2-digit', hour12: false, timeZone: 'Europe/Berlin'
  }).format(now), 10);
  const weekday = new Intl.DateTimeFormat('en-GB', {
    weekday: 'short', timeZone: 'Europe/Berlin'
  }).format(now);
  const isWeekday = !['Sat', 'Sun'].includes(weekday);
  const isOpen = isWeekday && hour >= 9 && hour < 19;
  return (
    <div className={`studio-status ${isOpen ? 'on' : 'off'}`} aria-hidden>
      <span className="studio-status-dot" />
      <span className="studio-status-text">{isOpen ? 'Studio aktiv' : 'Off-Hours'} · Köln {time}</span>
    </div>
  );
}

function Header({ active, onNav }) {
  const items = [
    { id: 'home', label: 'home', href: '/' },
    { id: 'creative', label: 'about', href: '/about/' },
    { id: 'projects', label: 'cases', href: '/cases/' },
    { id: 'kontakt', label: 'kontakt', href: '/kontakt/' },
  ];
  const [menuOpen, setMenuOpen] = useState(false);
  useEffect(() => {
    document.body.classList.toggle('nav-open', menuOpen);
    return () => document.body.classList.remove('nav-open');
  }, [menuOpen]);
  const goAndClose = (id) => { setMenuOpen(false); onNav(id); };
  return (
    <>
      <header className="hdr">
        <div className="hdr-l">
          <Magnetic as="div" className="brand" onClick={() => { setMenuOpen(false); onNav('home'); }} strength={0.3} radius={90}>Cuvir<sup>®</sup></Magnetic>
          <StudioStatus />
        </div>
        <nav>{items.map(i => <MagneticNavLink key={i.id} active={active === i.id} onClick={() => onNav(i.id)}>{i.label}</MagneticNavLink>)}</nav>
        <Btn onClick={() => onNav('kontakt')}>Termin buchen </Btn>
        <button className={`hdr-burger ${menuOpen ? 'open' : ''}`} onClick={() => setMenuOpen(o => !o)} aria-label={menuOpen ? 'Menu schließen' : 'Menu öffnen'} aria-expanded={menuOpen}>
          <span /><span /><span />
        </button>
      </header>
      <div className={`mobile-menu ${menuOpen ? 'open' : ''}`} role="dialog" aria-hidden={!menuOpen}>
        <nav className="mobile-menu-nav">
          {items.map((i, idx) => (
            <button
              key={i.id}
              type="button"
              onClick={() => goAndClose(i.id)}
              className={`mobile-menu-item ${active === i.id ? 'active' : ''}`}
              style={{ '--mm-i': idx }}
            >{i.label}</button>
          ))}
        </nav>
        <div className="mobile-menu-foot">
          <button className="mobile-menu-close" onClick={() => setMenuOpen(false)} aria-label="Menu schließen">
            <span /><span />
          </button>
          <a href="/kontakt/" onClick={(e) => { e.preventDefault(); goAndClose('kontakt'); }} className="mobile-menu-cta">Termin buchen →</a>
        </div>
      </div>
    </>
  );
}

function Marquee({ items, paused, outline }) {
  const list = items.concat(items);
  return (
    <div className={`marquee ${paused ? 'paused' : ''}`}>
      <div className="marquee-track">
        {list.map((it, i) => (
          <span key={i} className={`mq-item ${outline && i % 2 === 1 ? 'mq-out' : ''}`}>{it}<span className="d" /></span>
        ))}
      </div>
    </div>
  );
}

function ScrollCounter({ to, label, num }) {
  const [val, setVal] = useState(0);
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    const target = parseFloat(String(to).replace(/[^\d.]/g, '')) || 0;
    const obs = new IntersectionObserver(([e]) => {
      if (!e.isIntersecting) return;
      const start = performance.now(); const dur = 1300;
      const tick = (t) => {
        const p = Math.min(1, (t - start) / dur);
        const eased = 1 - Math.pow(1 - p, 3);
        setVal(target * eased);
        if (p < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick); obs.disconnect();
    }, { threshold: 0.4 });
    obs.observe(el); return () => obs.disconnect();
  }, [to]);
  const display = (() => {
    const target = parseFloat(String(to).replace(/[^\d.]/g, '')) || 0;
    const isInt = Number.isInteger(target);
    const n = isInt ? Math.round(val) : val.toFixed(1);
    return String(to).replace(/[\d.]+/, n);
  })();
  return (
    <div className="kpi-mini" ref={ref}>
      <div className="img"></div>
      <div className="body">
        <div className="num-s">{num}</div>
        <div className="num-l">{display}</div>
        <div className="lbl">{label}</div>
      </div>
    </div>
  );
}

const SCRAMBLE_POOL = '/\\<>{}[]+=#$%&*?ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

function setupScramble(root) {
  const chars = [];
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
  const nodes = []; let n;
  while ((n = walker.nextNode())) if (n.nodeValue && n.nodeValue.trim()) nodes.push(n);
  nodes.forEach(node => {
    const frag = document.createDocumentFragment();
    [...node.nodeValue].forEach(c => {
      if (c === ' ') { frag.appendChild(document.createTextNode(' ')); return; }
      const span = document.createElement('span');
      span.className = 'scr-c';
      span.dataset.final = c;
      span.textContent = c;
      chars.push(span);
      frag.appendChild(span);
    });
    node.parentNode.replaceChild(frag, node);
  });
  chars.forEach(c => { c.textContent = SCRAMBLE_POOL[Math.floor(Math.random() * SCRAMBLE_POOL.length)]; });
  return chars;
}

function runScramble(chars) {
  chars.forEach((span, i) => {
    const final = span.dataset.final;
    if (/[.,!?·\-:;—]/.test(final)) { span.textContent = final; return; }
    const start = i * 28 + 50;
    const ticks = 6 + Math.floor(Math.random() * 5);
    let n = 0;
    setTimeout(() => {
      const id = setInterval(() => {
        if (n >= ticks) { clearInterval(id); span.textContent = final; }
        else { span.textContent = SCRAMBLE_POOL[Math.floor(Math.random() * SCRAMBLE_POOL.length)]; n++; }
      }, 40);
    }, start);
  });
}

function setupWordReveal(root) {
  const words = [];
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
  const nodes = []; let n;
  while ((n = walker.nextNode())) if (n.nodeValue && n.nodeValue.trim()) nodes.push(n);
  nodes.forEach(node => {
    const frag = document.createDocumentFragment();
    const parts = node.nodeValue.split(/(\s+)/);
    parts.forEach(part => {
      if (!part) return;
      if (/^\s+$/.test(part)) {
        frag.appendChild(document.createTextNode(part));
      } else {
        const outer = document.createElement('span');
        outer.className = 'word-reveal';
        const inner = document.createElement('span');
        inner.className = 'word-reveal-inner';
        inner.textContent = part;
        outer.appendChild(inner);
        words.push(inner);
        frag.appendChild(outer);
      }
    });
    node.parentNode.replaceChild(frag, node);
  });
  words.forEach((w, i) => { w.style.animationDelay = `${i * 70}ms`; });
}

function Reveal({ children, delay = 0, scramble = false, wordReveal = false, as: Tg = 'div', className = '', ...rest }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    const motionOff = document.body.classList.contains('no-motion');
    const chars = (scramble && !motionOff) ? setupScramble(el) : null;
    if (wordReveal && !motionOff && !scramble) setupWordReveal(el);
    const obs = new IntersectionObserver(([e]) => {
      if (!e.isIntersecting) return;
      setTimeout(() => {
        el.classList.add('in');
        if (chars) runScramble(chars);
      }, delay);
      obs.disconnect();
    }, { threshold: 0.15 });
    obs.observe(el); return () => obs.disconnect();
  }, [delay, scramble, wordReveal]);
  return <Tg ref={ref} className={`reveal ${className}`.trim()} {...rest}>{children}</Tg>;
}

function Footer({ onNav }) {
  const bigRef = useRef(null);
  useEffect(() => {
    const el = bigRef.current; if (!el) return;
    const obs = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { el.classList.add('in'); obs.disconnect(); }
    }, { threshold: 0.25 });
    obs.observe(el);
    return () => obs.disconnect();
  }, []);
  return (
    <footer className="footer">
      <div className="grid">
        <div>
          <div
            ref={bigRef}
            className="big big-reveal"
            data-text="Lass uns automatisieren."
            onMouseMove={(e) => {
              const el = e.currentTarget;
              const r = el.getBoundingClientRect();
              el.style.setProperty('--big-mx', `${((e.clientX - r.left) / r.width) * 100}%`);
              el.style.setProperty('--big-my', `${((e.clientY - r.top) / r.height) * 100}%`);
              el.classList.add('big-active');
            }}
            onMouseLeave={(e) => e.currentTarget.classList.remove('big-active')}
            onTouchMove={(e) => {
              const t = e.touches[0]; if (!t) return;
              const el = e.currentTarget;
              const r = el.getBoundingClientRect();
              el.style.setProperty('--big-mx', `${((t.clientX - r.left) / r.width) * 100}%`);
              el.style.setProperty('--big-my', `${((t.clientY - r.top) / r.height) * 100}%`);
              el.classList.add('big-active');
            }}
            onTouchEnd={(e) => e.currentTarget.classList.remove('big-active')}
          >Lass uns automatisieren.</div>
          <div style={{ marginTop: 28 }}><Btn onClick={() => onNav('kontakt')} lg>Erstgespräch buchen</Btn></div>
        </div>
        <div>
          <h5>Navigation</h5>
          <ul>
            <Magnetic as="li" onClick={() => onNav('home')}>home</Magnetic>
            <Magnetic as="li" onClick={() => onNav('creative')}>about</Magnetic>
            <Magnetic as="li" onClick={() => onNav('projects')}>cases</Magnetic>
            <Magnetic as="li" onClick={() => onNav('kontakt')}>kontakt</Magnetic>
          </ul>
        </div>
        <div>
          <h5>Social</h5>
          <ul>
            <li><Magnetic href="https://www.linkedin.com/in/kemalduran/" target="_blank" rel="noopener noreferrer">linkedin<span className="arrow"> ↗</span></Magnetic></li>
            <li><Magnetic href="https://www.instagram.com/cuvir.de" target="_blank" rel="noopener noreferrer">instagram<span className="arrow"> ↗</span></Magnetic></li>
          </ul>
        </div>
      </div>
      <div className="legal">
        <span><Magnetic onClick={() => onNav('home')} style={{ cursor: 'pointer' }}>© 2026 cuvir®</Magnetic></span>
        <span style={{ display: 'inline-flex', gap: 18 }}>
          <Magnetic onClick={() => onNav('legal:impressum')} style={{ cursor: 'pointer' }}>impressum</Magnetic>
          <Magnetic onClick={() => onNav('legal:agb')} style={{ cursor: 'pointer' }}>agb</Magnetic>
          <Magnetic onClick={() => onNav('legal:datenschutz')} style={{ cursor: 'pointer' }}>datenschutz</Magnetic>
        </span>
        <span>by <Magnetic href="https://kemalduran.de" target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'underline', textUnderlineOffset: 3 }}>kemalduran.de</Magnetic></span>
      </div>
    </footer>
  );
}

function CustomCursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  useEffect(() => {
    if (window.matchMedia('(pointer: coarse)').matches) return;
    const dot = dotRef.current, ring = ringRef.current;
    if (!dot || !ring) return;
    let tx = -100, ty = -100, dx = -100, dy = -100, rx = -100, ry = -100, raf = 0, hoverOn = false, frame = 0;
    const interactiveSel = 'a, button, [role="button"], input, textarea, select, .case, .svc-card, .price, .stream-cell, .footer ul li, summary';
    const onMove = (e) => { tx = e.clientX; ty = e.clientY; };
    let hidden = false;
    const updateHover = () => {
      const el = document.elementFromPoint(tx, ty);
      const overIframe = !!(el && (el.tagName === 'IFRAME' || (el.closest && el.closest('.terminal-window'))));
      if (overIframe !== hidden) {
        hidden = overIframe;
        ring.classList.toggle('fade', overIframe);
        dot.classList.toggle('fade', overIframe);
      }
      if (overIframe) return;
      const interactive = !!(el && el.closest && el.closest(interactiveSel));
      if (interactive !== hoverOn) {
        hoverOn = interactive;
        ring.classList.toggle('on', interactive);
        dot.classList.toggle('on', interactive);
      }
    };
    const tick = () => {
      dx += (tx - dx) * 0.45; dy += (ty - dy) * 0.45;
      rx += (tx - rx) * 0.18; ry += (ty - ry) * 0.18;
      dot.style.transform = `translate(${dx}px, ${dy}px)`;
      ring.style.transform = `translate(${rx}px, ${ry}px)`;
      if ((frame++ & 1) === 0) updateHover();
      raf = requestAnimationFrame(tick);
    };
    document.addEventListener('mousemove', onMove);
    raf = requestAnimationFrame(tick);
    return () => {
      cancelAnimationFrame(raf);
      document.removeEventListener('mousemove', onMove);
    };
  }, []);
  return (
    <>
      <div ref={ringRef} className="cursor-ring" aria-hidden />
      <div ref={dotRef} className="cursor-dot" aria-hidden />
    </>
  );
}

function FloatingCTA({ onNav, route }) {
  const [show, setShow] = useState(false);
  useEffect(() => {
    const isKontakt = route === 'kontakt';
    const isLegal = route.startsWith('legal:') || ['impressum','agb','datenschutz'].includes(route);
    if (isKontakt || isLegal) { setShow(false); return; }
    let footerInView = false;
    const update = () => { setShow(window.scrollY > 500 && !footerInView); };
    let obs = null;
    const setupObserver = () => {
      const footer = document.querySelector('.footer');
      if (!footer) { setTimeout(setupObserver, 200); return; }
      obs = new IntersectionObserver(([e]) => {
        footerInView = e.isIntersecting;
        update();
      }, { threshold: 0, rootMargin: '0px 0px -40px 0px' });
      obs.observe(footer);
    };
    setupObserver();
    window.addEventListener('scroll', update, { passive: true });
    update();
    return () => {
      window.removeEventListener('scroll', update);
      if (obs) obs.disconnect();
    };
  }, [route]);
  return (
    <button
      className={`floating-cta ${show ? 'in' : ''}`}
      onClick={() => onNav && onNav('kontakt')}
      aria-label="Termin buchen"
      tabIndex={show ? 0 : -1}
    >
      <span>Termin buchen</span>
      <span className="floating-cta-arrow">→</span>
    </button>
  );
}

function CookieBanner({ onNav }) {
  const [show, setShow] = useState(false);
  useEffect(() => {
    try {
      // Only 'accepted' is persistent. Rejection is session-only — banner reappears next visit.
      const accepted = localStorage.getItem('cuvir-cookie-consent') === 'accepted';
      const sessionDismissed = sessionStorage.getItem('cuvir-cookie-session-dismissed') === '1';
      if (!accepted && !sessionDismissed) {
        const t = setTimeout(() => setShow(true), 800);
        return () => clearTimeout(t);
      }
    } catch (e) {}
  }, []);
  const accept = () => {
    try {
      localStorage.setItem('cuvir-cookie-consent', 'accepted');
      sessionStorage.removeItem('cuvir-cookie-session-dismissed');
      if (window.gtag) window.gtag('consent', 'update', { analytics_storage: 'granted' });
    } catch (e) {}
    setShow(false);
  };
  const reject = () => {
    try {
      // Don't persist rejection — banner re-appears on next visit
      localStorage.removeItem('cuvir-cookie-consent');
      sessionStorage.setItem('cuvir-cookie-session-dismissed', '1');
    } catch (e) {}
    setShow(false);
  };
  if (!show) return null;
  return (
    <div className="cookie-banner" role="dialog" aria-label="Cookie-Einstellungen">
      <div className="cookie-banner-title">/ Cookie-Einstellungen</div>
      <p className="cookie-banner-body">
        Wir nutzen Cookies für anonyme Analyse (Google Analytics) und um die Nutzung der Seite zu verbessern. Details in unserer{' '}
        <a
          href="/datenschutz/"
          onClick={(e) => { e.preventDefault(); onNav && onNav('legal:datenschutz'); }}
          className="cookie-banner-link"
        >Datenschutzerklärung</a>.
      </p>
      <div className="cookie-banner-actions">
        <button className="cookie-banner-btn cookie-banner-btn-secondary" onClick={reject}>Ablehnen</button>
        <button className="cookie-banner-btn cookie-banner-btn-primary" onClick={accept}>Akzeptieren</button>
      </div>
    </div>
  );
}

function OutlineMarquee({ text, paused = false, speed = 40 }) {
  const items = Array(6).fill(text);
  return (
    <div className={`outline-mq ${paused ? 'paused' : ''}`} aria-hidden>
      <div className="outline-mq-track" style={{ animationDuration: `${speed}s` }}>
        {[...items, ...items].map((t, i) => (
          <span key={i} className="outline-mq-item">{t}<span className="outline-mq-dot" /></span>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { Btn, Tag, Header, Footer, Marquee, ScrollCounter, Reveal, useMagnetic, CustomCursor, CookieBanner, OutlineMarquee, FloatingCTA });
