/* Shared components — image placeholder, reveal hook, hairline, etc. */

const { useState, useEffect, useRef, useMemo, useCallback } = React;

/* ---------- Striped image placeholder ---------- */

function Ph({ label, dark, aspect, style, children, seed = 1, slot, shape, className, src, locked }) {
  const stripes = useMemo(() => {
    const arr = [];
    const n = 7;
    for (let i = 0; i < n; i++) {
      const off = (seed * 13 + i * 41) % 100;
      arr.push({
        x: -20 + i * 22 + off / 8,
        w: 6 + off % 9,
        op: 0.04 + off % 7 / 120
      });
    }
    return arr;
  }, [seed]);

  // If a slot id is provided, render the image-slot web component so the user
  // can drag-and-drop a real photo onto it. Otherwise render the striped
  // placeholder.
  if (slot) {
    const slotStyle = { width: '100%', height: '100%', aspectRatio: aspect, display: 'block', ...style };
    return (
      <image-slot
        id={slot}
        placeholder={label || 'Drop a photo'}
        shape={shape || 'rect'}
        src={src}
        style={slotStyle}
        locked=""
        class={className}>
      </image-slot>);

  }

  return (
    <div className={"ph" + (dark ? " dark" : "") + (className ? " " + className : "")} style={{ aspectRatio: aspect, ...style }}>
      <svg viewBox="0 0 100 100" preserveAspectRatio="none">
        {stripes.map((s, i) =>
        <rect
          key={i}
          x={s.x} y="-10" width={s.w} height="120"
          fill={dark ? "#FAF6EF" : "#2A2622"}
          opacity={s.op}
          transform={`rotate(${-22 + i * 3 % 8} 50 50)`} />

        )}
      </svg>
      {label && <span className="ph-label">{label}</span>}
      {children}
    </div>);

}

/* ---------- Hero placeholder w/ stronger composition ---------- */

function HeroPh({ label, slot = 'hero', src = 'hero-placeholder.svg', locked }) {
  return (
    <div className="hero-media" style={{ position: 'absolute', inset: 0 }}>
      <image-slot
        id={slot}
        placeholder={label || 'Drop hero image'}
        shape="rect"
        src={src}
        hires=""
        locked=""
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', display: 'block' }}>
      </image-slot>
    </div>);

}

/* ---------- Scroll reveal ---------- */

function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll('.reveal:not(.in)');
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
    els.forEach((el) => io.observe(el));

    // Reversible reveal — fades back out when scrolled out of view (used by the
    // booking bar so it animates in on the way down and out on the way up).
    const rEls = document.querySelectorAll('.reveal-repeat');
    const ioR = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        e.target.classList.toggle('in', e.isIntersecting);
      });
    }, { threshold: 0.18, rootMargin: '0px 0px -40px 0px' });
    rEls.forEach((el) => ioR.observe(el));

    return () => { io.disconnect(); ioR.disconnect(); };
  });
}

/* ---------- Section header ---------- */

function SectionHead({ num, eyebrow, title, lede }) {
  return (
    <div className="section-head reveal">
      <div>
        <div className="eyebrow-num">
          <span className="eyebrow">{num ? num + ' · ' + eyebrow : eyebrow}</span>
        </div>
      </div>
      <div>
        <h2 className="display display-m" style={{ marginBottom: 24, width: "400px" }}>{title}</h2>
        {lede && <p className="lede">{lede}</p>}
      </div>
    </div>);

}

/* ---------- Navigation ---------- */

/* Clean paths per page, mirrored from PATH_BY_PAGE in app.jsx. Nav and footer
   links need a real href so crawlers can discover pages and so the links work
   with cmd-click / open-in-new-tab. Navigation is still handled in JS. */
const CASA_PATHS = {
  home: '', apartments: '/apartments', area: '/area', story: '/story',
  contact: '/visit', 'find-us': '/findus', faq: '/faq', 'house-manual': '/manual',
  vouchers: '/vouchers', press: '/press', imprint: '/imprint',
  privacy: '/privacy', cookies: '/cookies', terms: '/terms', booked: '/booked'
};
function casaHref(page, lang) {
  return '/' + (lang || 'en') + (CASA_PATHS[page] || '');
}
/* Props for a JS-navigated but crawlable link. */
function navLink(page, lang, setPage) {
  return {
    href: casaHref(page, lang),
    onClick: (e) => {
      if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
      e.preventDefault();
      setPage(page);
    }
  };
}

function Nav({ page, setPage, lang, setLang, t, onHero, onBook }) {
  const [scrolled, setScrolled] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);

  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 40);
    window.addEventListener('scroll', onScroll);
    return () => window.removeEventListener('scroll', onScroll);
  }, []);

  const links = [
  { id: 'home', label: t.nav.home },
  { id: 'apartments', label: t.nav.apartments },
  { id: 'story', label: t.nav.story },
  { id: 'area', label: t.nav.area },
  { id: 'contact', label: t.nav.contact }];


  return (
    <>
      <nav className={"nav" + (scrolled ? " scrolled" : "") + (onHero && !scrolled ? " on-dark" : "")}>
        <a className="nav-brand" {...navLink('home', lang, setPage)}>
          CASA LAUDIERI
          <small>EST. 1915</small>
        </a>
        <div className="nav-links">
          {links.map((l) =>
          <a
            key={l.id}
            className={page === l.id ? 'active' : ''}
            aria-current={page === l.id ? 'page' : undefined}
            {...navLink(l.id, lang, setPage)}>
            {l.label}</a>
          )}
        </div>
        <div className="nav-right">
          <div className="lang-switch">
            {['en', 'it', 'de'].map((l) =>
            <button
              key={l}
              className={lang === l ? 'active' : ''}
              aria-label={{ en: 'English', it: 'Italiano', de: 'Deutsch' }[l]}
              aria-pressed={lang === l}
              onClick={() => setLang(l)}>
              {l}</button>
            )}
          </div>
          <button className="btn btn-solid" onClick={() => onBook ? onBook() : setPage('contact')}>{t.nav.book}</button>
          <button className="menu-btn" aria-label={t.nav.menu} onClick={() => setMenuOpen(true)}>
            <span>{t.nav.menu}</span>
            <span style={{ display: 'inline-flex', flexDirection: 'column', gap: 3 }}>
              <span style={{ width: 18, height: 1, background: 'currentColor', display: 'block' }}></span>
              <span style={{ width: 18, height: 1, background: 'currentColor', display: 'block' }}></span>
            </span>
          </button>
        </div>
      </nav>

      {menuOpen &&
      <div style={{ position: 'fixed', inset: 0, zIndex: 150, background: 'var(--paper)', padding: '22px var(--gutter) 80px', display: 'flex', flexDirection: 'column' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', minHeight: 34, marginBottom: 60 }}>
            <img src="logo-casa-laudieri-ink.png" alt="Casa Laudieri" style={{ height: 22, width: 'auto', display: 'block' }} />
            <button onClick={() => setMenuOpen(false)} aria-label="Close menu" style={{ fontSize: 22 }}>×</button>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
            {links.map((l) =>
          <a key={l.id} href={casaHref(l.id, lang)}
          onClick={(e) => {if (e.metaKey || e.ctrlKey || e.shiftKey) return;e.preventDefault();setPage(l.id);setMenuOpen(false);}}
          className="display display-s" style={{ textAlign: 'left' }}>
                {l.label}
              </a>
          )}
          </div>
          <div style={{ marginTop: 'auto', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
            <div className="lang-switch">
              {['en', 'it', 'de'].map((l) =>
            <button key={l} className={lang === l ? 'active' : ''} onClick={() => setLang(l)}>{l}</button>
            )}
            </div>
            <button className="btn btn-solid" onClick={() => {onBook ? onBook() : setPage('contact');setMenuOpen(false);}}>{t.nav.book}</button>
          </div>
        </div>
      }
    </>);

}

/* ---------- Footer ---------- */

// Cloudflare Turnstile (invisible) — same site key as the enquiry form. Secret is
// TURNSTILE_SECRET_KEY in Vercel, verified in api/subscribe.js.
const NL_TURNSTILE_SITE_KEY = "0x4AAAAAADs7irwwFp6GIvC7";

function Footer({ t, setPage, lang }) {
  const [email, setEmail] = useState('');
  const [nlState, setNlState] = useState('idle'); // idle | sending | done | error
  const [tsToken, setTsToken] = useState('');
  const tsRef = useRef(null);
  const tsWidget = useRef(null);

  useEffect(() => {
    let tries = 0;
    const iv = setInterval(() => {
      if (window.turnstile && tsRef.current && tsWidget.current === null) {
        tsWidget.current = window.turnstile.render(tsRef.current, {
          sitekey: NL_TURNSTILE_SITE_KEY,
          callback: (token) => setTsToken(token),
          'error-callback': () => setTsToken(''),
          'expired-callback': () => { setTsToken(''); if (window.turnstile && tsWidget.current !== null) window.turnstile.reset(tsWidget.current); }
        });
        clearInterval(iv);
      }
      if (++tries > 150) clearInterval(iv);
    }, 100);
    return () => clearInterval(iv);
  }, []);

  const subscribe = async (e) => {
    e.preventDefault();
    if (nlState === 'sending') return;
    setNlState('sending');
    try {
      const res = await fetch('/api/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
        body: JSON.stringify({ email, lang: lang || 'en', 'cf-turnstile-response': tsToken })
      });
      setNlState(res.ok ? 'done' : 'error');
      if (window.turnstile && tsWidget.current !== null) { window.turnstile.reset(tsWidget.current); setTsToken(''); }
    } catch (err) {
      setNlState('error');
    }
  };

  return (
    <footer className="foot">
      <div className="foot-grid">
        <div className="foot-brand">
          <div className="foot-wordmark">CASA LAUDIERI</div>
          <p>{t.footer.tag}</p>
          <div id="newsletter" style={{ marginTop: 28, scrollMarginTop: 90 }}>
            <div className="eyebrow" style={{ color: 'rgba(250,246,239,0.75)' }}>{t.newsletter.eyebrow}</div>
            <p style={{ marginTop: 8, fontSize: 13, color: 'rgba(250,246,239,0.7)', maxWidth: 280 }}>{t.newsletter.lede}</p>
            {nlState === 'done' ?
            <p style={{ marginTop: 18, fontSize: 14, color: 'rgba(250,246,239,0.85)', maxWidth: 300, lineHeight: 1.5 }}>{t.newsletter.check}</p> :
            <>
                <form className="newsletter" onSubmit={subscribe}>
                  <input type="text" name="_gotcha" tabIndex="-1" autoComplete="off" aria-hidden="true" style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, opacity: 0 }} />
                  <label className="sr-only" htmlFor="cl-newsletter-email">{t.newsletter.placeholder}</label>
                  <input id="cl-newsletter-email" name="email" type="email" autoComplete="email" placeholder={t.newsletter.placeholder} value={email} onChange={(e) => setEmail(e.target.value)} required disabled={nlState === 'sending'} />
                  <button type="submit" aria-label={t.newsletter.subscribe} disabled={nlState === 'sending'}>→</button>
                </form>
                <div ref={tsRef} style={{ marginTop: 8 }}></div>
                {nlState === 'error' &&
                <p style={{ marginTop: 10, fontSize: 12.5, color: 'rgba(217,151,118,0.95)' }}>{t.newsletter.error}</p>
                }
                <p style={{ marginTop: 12, fontSize: 11, color: 'rgba(250,246,239,0.75)', maxWidth: 300, lineHeight: 1.5 }}>{t.newsletter.consent}</p>
              </>
            }
          </div>
          <a className="foot-social" href="https://www.instagram.com/casalaudieri/" target="_blank" rel="noopener noreferrer" aria-label="Casa Laudieri on Instagram" title="Instagram">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
              <rect x="2" y="2" width="20" height="20" rx="5" />
              <circle cx="12" cy="12" r="4" />
              <circle cx="17.5" cy="6.5" r="1" fill="currentColor" stroke="none" />
            </svg>
          </a>
        </div>
        <div>
          <h2>{t.footer.explore}</h2>
          <ul>
            <li><a {...navLink('apartments', lang, setPage)}>{t.nav.apartments}</a></li>
            <li><a {...navLink('story', lang, setPage)}>{t.nav.story}</a></li>
            <li><a {...navLink('area', lang, setPage)}>{t.nav.area}</a></li>
            <li><a {...navLink('contact', lang, setPage)}>{t.nav.contact}</a></li>
          </ul>
        </div>
        <div>
          <h2>{t.footer.practical}</h2>
          <ul>
            <li><a {...navLink('find-us', lang, setPage)}>{t.footer.practical_items[0]}</a></li>
            <li><a {...navLink('faq', lang, setPage)}>{t.footer.practical_items[1]}</a></li>
            <li><a {...navLink('house-manual', lang, setPage)}>{t.footer.practical_items[2]}</a></li>
            <li><a {...navLink('vouchers', lang, setPage)}>{t.footer.practical_items[3]}</a></li>
            <li><a {...navLink('press', lang, setPage)}>{t.footer.practical_items[4]}</a></li>
          </ul>
        </div>
        <div>
          <h2>{t.footer.legal}</h2>
          <ul>
            <li><a {...navLink('imprint', lang, setPage)}>{t.footer.legal_items[0]}</a></li>
            <li><a {...navLink('privacy', lang, setPage)}>{t.footer.legal_items[1]}</a></li>
            <li><a {...navLink('cookies', lang, setPage)}>{t.footer.legal_items[2]}</a></li>
            <li><a {...navLink('terms', lang, setPage)}>{t.footer.legal_items[3]}</a></li>
          </ul>
        </div>
      </div>
      <div className="foot-bottom">
        <span>{t.footer.copyright}</span>
        <span>{t.footer.vat}</span>
        <span className="foot-addr"><span className="foot-addr-a">{t.contact.address_text.split('\n')[0]}</span><span className="foot-addr-b">{t.contact.address_text.split('\n')[1]}</span></span>
      </div>
    </footer>);

}

// Default photo URLs sourced from the existing casalaudieri.com Squarespace
// site. Users can override any slot by dragging a new image onto it; the
// override persists in browser storage. Replace these with locally-hosted
// images once the user is ready.
const CASA_PHOTOS = {
  hero: "hero-2000.jpg",
  storyDetail: "story-bookshelf-1200.jpg",
  storyHistory: "story-history-1200.jpg",
  "apt-jochtal": "apt-jochtal-1200.jpg",
  "area-skiing": "area-skiing-1600.jpg",
  "area-brixen": "area-brixen-1600.jpg",
  "area-plose-cable": "area-plose-cable-1600.jpg"
};

/* ---------- Shared Leaflet map ---------- */

function CasaMap({ lat = 46.71362, lng = 11.65426, aspect = '4 / 3' }) {
  const mapRef = React.useRef(null);
  React.useEffect(() => {
    if (!window.L || !mapRef.current || mapRef.current._init) return;
    mapRef.current._init = true;
    const map = window.L.map(mapRef.current, {
      scrollWheelZoom: false, zoomControl: true, attributionControl: true
    }).setView([lat, lng], 16);
    window.L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; OpenStreetMap contributors', className: 'map-soft-tiles',
      subdomains: 'abcd', maxZoom: 19
    }).addTo(map);
    const icon = window.L.divIcon({
      className: 'casa-pin',
      html: '<div class="pin-pulse"></div><div class="pin-dot"></div><span class="sr-only">Casa Laudieri, Fallmerayerstraße 5, Brixen</span>',
      iconSize: [16, 16], iconAnchor: [8, 8]
    });
    window.L.marker([lat, lng], { icon }).addTo(map).bindTooltip('Casa Laudieri', {
      permanent: true, direction: 'right', offset: [14, 0], className: 'casa-tooltip'
    });
    return () => { map.remove(); if (mapRef.current) mapRef.current._init = false; };
  }, [lat, lng]);

  return (
    <div className="map-ph" style={{ aspectRatio: aspect }}>
      <div ref={mapRef} className="casa-map"></div>
      <div className="map-cap">
        <small>Casa Laudieri</small>
        Fallmerayerstraße 5, Brixen
      </div>
    </div>);

}

function CookieBanner({ t, setPage, lang }) {
  const [show, setShow] = useState(false);
  useEffect(() => {
    let v = null;
    try {v = localStorage.getItem('cl-cookie-consent');} catch (e) {}
    window.casaCookieConsent = v; // 'accepted' | 'declined' | null — future analytics can gate on this
    if (!v) setShow(true);
    const reopen = () => setShow(true);
    window.addEventListener('cookie-reopen', reopen);
    return () => window.removeEventListener('cookie-reopen', reopen);
  }, []);
  if (!show) return null;
  const cb = t.cookieBanner || {};
  const choose = (v) => {
    try {localStorage.setItem('cl-cookie-consent', v);} catch (e) {}
    window.casaCookieConsent = v;
    window.dispatchEvent(new CustomEvent('cookie-consent', { detail: v }));
    setShow(false);
  };
  return (
    <div className="cookie-banner" role="dialog" aria-label="Cookies">
      <p>
        {cb.text}{' '}
        <a href={casaHref('cookies', lang)} onClick={(e) => {if (e.metaKey || e.ctrlKey) return;e.preventDefault();setPage && setPage('cookies');}}>{cb.link}</a>
      </p>
      <div className="cookie-actions">
        <button className="cookie-btn cookie-btn--ghost" onClick={() => choose('declined')}>{cb.decline}</button>
        <button className="cookie-btn cookie-btn--solid" onClick={() => choose('accepted')}>{cb.accept}</button>
      </div>
    </div>);

}

function FormSelect({ name, options, defaultValue, id }) {
  const [value, setValue] = useState(defaultValue != null ? defaultValue : (options[0] && options[0].value));
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);
  const current = options.find((o) => o.value === value) || options[0];
  return (
    <div className="form-select" ref={ref}>
      <input type="hidden" name={name} value={value} />
      <button type="button" id={id} className={"form-select-trigger" + (open ? " open" : "")} onClick={() => setOpen((v) => !v)} aria-haspopup="listbox" aria-expanded={open}>
        <span>{current ? current.label : ''}</span>
        <span className="apt-select-caret" aria-hidden="true">⌄</span>
      </button>
      {open &&
      <ul className="apt-select-menu" role="listbox">
          {options.map((o) =>
        <li key={o.value} role="option" aria-selected={value === o.value} className={value === o.value ? 'sel' : ''} onClick={() => {setValue(o.value);setOpen(false);}}>{o.label}</li>
        )}
        </ul>
      }
    </div>);

}

Object.assign(window, { Ph, HeroPh, useReveal, SectionHead, Nav, Footer, CASA_PHOTOS, CasaMap, CookieBanner, FormSelect, casaHref, navLink });