// Layout primitives + reveal-on-scroll.

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

// Reveal on scroll hook — observer-based, fires once
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const obs = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          obs.unobserve(e.target);
        }
      });
    }, { rootMargin: '-8% 0px -8% 0px', threshold: 0.1 });
    obs.observe(el);
    return () => obs.disconnect();
  }, []);
  return ref;
}

const Reveal = ({ children, delay = 0, as: As = 'div', className = '', ...rest }) => {
  const ref = useReveal();
  const style = delay ? { transitionDelay: `${delay}ms` } : undefined;
  return (
    <As ref={ref} className={`reveal ${className}`} style={style} {...rest}>
      {children}
    </As>
  );
};

// Eyebrow label — uppercase, tracked
const Eyebrow = ({ children, className = '' }) => (
  <div className={`eyebrow text-ink/55 ${className}`}>{children}</div>
);

// Hairline divider
const Hairline = ({ className = '', dark = false }) => (
  <div className={`w-full border-t ${dark ? 'hairline-bone' : 'hairline'} ${className}`} />
);

// Section wrapper — consistent vertical rhythm, generous padding
const Section = ({ id, dark = false, className = '', children, noPadX = false }) => (
  <section
    id={id}
    data-screen-label={id}
    className={`relative ${dark ? 'bg-ink text-bone' : 'bg-bone text-ink'} ${className}`}
  >
    <div className={`mx-auto max-w-[1480px] ${noPadX ? '' : 'px-6 md:px-10 lg:px-16'}`}>
      {children}
    </div>
  </section>
);

// Image placeholder — neutral, editorial
const PlaceholderImage = ({ tone = 'ph-1', label, aspect = 'aspect-[4/5]', className = '' }) => (
  <div className={`relative ${tone} ${aspect} ${className} overflow-hidden`}>
    <div className="absolute inset-0 grain"></div>
    {label ? (
      <div className="absolute bottom-3 left-3 eyebrow text-ink/55">{label}</div>
    ) : null}
  </div>
);

// Star row
const StarRow = ({ count = 5, className = '' }) => (
  <div className={`flex gap-1 text-accent ${className}`}>
    {Array.from({ length: count }).map((_, i) => (
      <IconStar key={i} size={12} strokeWidth={1.2} />
    ))}
  </div>
);

// Price formatting
const fmtPrice = (n) => '$' + n.toLocaleString('en-US');
const fmtMoney = (n) => '$' + Math.round(n).toLocaleString('en-US');
const fmtSqft = (n) => n.toLocaleString('en-US');

// ReadMore — clamps text on mobile; toggle only renders when content actually overflows.
const ReadMore = ({
  children,
  lines = 3,
  more = 'Read more',
  less = 'Show less',
  className = '',
  textClassName = '',
  mobileOnly = true,
}) => {
  const [open, setOpen] = useState(false);
  const [overflows, setOverflows] = useState(false);
  const textRef = useRef(null);

  useEffect(() => {
    const el = textRef.current;
    if (!el) return;
    const check = () => {
      // Only meaningful while clamp is active (closed state + mobile width)
      const onMobile = mobileOnly ? window.innerWidth < 768 : true;
      if (!onMobile) { setOverflows(false); return; }
      // Temporarily ensure clamp styles applied for measurement
      const prev = {
        display: el.style.display,
        webkitLineClamp: el.style.webkitLineClamp,
        webkitBoxOrient: el.style.webkitBoxOrient,
        overflow: el.style.overflow,
      };
      el.style.display = '-webkit-box';
      el.style.webkitLineClamp = String(lines);
      el.style.webkitBoxOrient = 'vertical';
      el.style.overflow = 'hidden';
      const isOverflowing = el.scrollHeight - 1 > el.clientHeight;
      // Restore (the inline style block below re-applies whichever state is active)
      el.style.display = prev.display;
      el.style.webkitLineClamp = prev.webkitLineClamp;
      el.style.webkitBoxOrient = prev.webkitBoxOrient;
      el.style.overflow = prev.overflow;
      setOverflows(isOverflowing);
    };
    check();
    window.addEventListener('resize', check);
    return () => window.removeEventListener('resize', check);
  }, [children, lines, mobileOnly]);

  const clampStyle = !open && overflows
    ? {
        display: '-webkit-box',
        WebkitLineClamp: lines,
        WebkitBoxOrient: 'vertical',
        overflow: 'hidden',
      }
    : {};

  return (
    <div className={className}>
      <div
        ref={textRef}
        className={`${textClassName} ${mobileOnly ? 'md:!block md:!overflow-visible' : ''}`}
        style={clampStyle}
      >
        {children}
      </div>
      {overflows ? (
        <button
          type="button"
          onClick={(e) => { e.preventDefault(); e.stopPropagation(); setOpen(o => !o); }}
          className={`mt-2 inline-flex items-center gap-1.5 eyebrow text-accent-deep hover:text-ink transition-colors ${mobileOnly ? 'md:hidden' : ''}`}
          aria-expanded={open}
        >
          {open ? less : more}
          <span className={`inline-block transition-transform ${open ? 'rotate-180' : ''}`}>↓</span>
        </button>
      ) : null}
    </div>
  );
};

Object.assign(window, { Reveal, Eyebrow, Hairline, Section, PlaceholderImage, StarRow, ReadMore, fmtPrice, fmtMoney, fmtSqft, useReveal });
