// Shared primitives + small pieces.
const { useState, useEffect, useRef, useMemo } = React;

/* ---------------- Wordmark ---------------- */
// Variants: 'on-white' (default, white bg), 'light' (petrol bg),
// 'transparent' (no bg, petrol text), 'transparent-light' (no bg, paper text — for dark surfaces),
// 'dark' (petrol bg, paper text).
const Wordmark = ({ height = 20, variant = 'transparent' }) => (
  <img src={`assets/logos/wordmark-${variant}.svg?v=3`} alt="oneill" style={{ height, display: 'block' }} />
);

/* ---------------- Live local time ---------------- */
const useLisbonTime = () => {
  const [t, setT] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setT(new Date()), 1000 * 30);
    return () => clearInterval(id);
  }, []);
  const formatter = useMemo(() => new Intl.DateTimeFormat('en-GB', {
    hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Lisbon'
  }), []);
  return formatter.format(t);
};

/* ---------------- Scroll spy hook ---------------- */
const useScrollSpy = (ids) => {
  const [active, setActive] = useState(ids[0]);
  useEffect(() => {
    const onScroll = () => {
      const y = window.scrollY + window.innerHeight * 0.35;
      let current = ids[0];
      for (const id of ids) {
        const el = document.getElementById(id);
        if (el && el.offsetTop <= y) current = id;
      }
      setActive(current);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [ids.join('|')]);
  return active;
};

/* ---------------- Scroll reveal ---------------- */
const Reveal = ({ children, delay = 0, className = '' }) => {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let done = false;
    const markIn = () => {
      if (done) return;
      done = true;
      setTimeout(() => el.classList.add('in'), delay);
    };
    // If already in view on mount, reveal immediately.
    const rect = el.getBoundingClientRect();
    if (rect.top < window.innerHeight - 20 && rect.bottom > 0) {
      markIn();
      return;
    }
    // Fallback to scroll listener (more reliable inside nested iframes than IO).
    const onScroll = () => {
      if (done) return;
      const r = el.getBoundingClientRect();
      if (r.top < window.innerHeight - 40 && r.bottom > 0) {
        markIn();
        window.removeEventListener('scroll', onScroll);
      }
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    // Safety net — after 1.5s, just reveal anything still pending above the fold-ish.
    const safety = setTimeout(() => {
      if (!done && el.getBoundingClientRect().top < window.innerHeight + 200) markIn();
    }, 1500);
    return () => {
      window.removeEventListener('scroll', onScroll);
      clearTimeout(safety);
    };
  }, [delay]);
  return <div ref={ref} className={`reveal ${className}`}>{children}</div>;
};

/* ---------------- Top bar ---------------- */
const Bar = ({ active }) => {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 12);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  const time = useLisbonTime();
  const items = [
    { id: 'currently', label: 'Currently' },
    { id: 'about',     label: 'About'    },
    { id: 'note',      label: 'Connect'  },
  ];
  return (
    <div className={`bar ${scrolled ? 'scrolled' : ''}`}>
      <a href="#top" className="bar__brand" aria-label="oneill">
        <Wordmark height={32} />
      </a>
      <nav className="bar__nav">
        {items.map(i => (
          <a key={i.id} href={`#${i.id}`} className={active === i.id ? 'active' : ''}>
            {i.label}
          </a>
        ))}
      </nav>
      <div className="bar__status" aria-live="polite">
        <span className="pulse" />
        <span>Portugal · {time}</span>
      </div>
    </div>
  );
};

/* ---------------- Contact form with validation ---------------- */
// Posts to /api/contact (Vercel serverless fn → Resend). See api/contact.js.
const CONTACT_ENDPOINT = '/api/contact';

const Form = () => {
  const [values, setValues] = useState({ name: '', email: '', msg: '', _gotcha: '' });
  const [errors, setErrors] = useState({});
  const [touched, setTouched] = useState({});
  const [sent, setSent] = useState(false);
  const [sending, setSending] = useState(false);
  const [sendError, setSendError] = useState('');

  const validate = (v) => {
    const e = {};
    if (!v.name.trim()) e.name = 'I need a name.';
    else if (v.name.trim().length < 2) e.name = 'Too short.';
    if (!v.email.trim()) e.email = 'I need an email.';
    else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.email)) e.email = 'That email looks off.';
    if (!v.msg.trim()) e.msg = 'A sentence is enough.';
    else if (v.msg.trim().length < 6) e.msg = 'Give me a little more.';
    return e;
  };

  const update = (k) => (ev) => {
    const next = { ...values, [k]: ev.target.value };
    setValues(next);
    if (touched[k]) setErrors(validate(next));
  };
  const onBlur = (k) => () => {
    setTouched({ ...touched, [k]: true });
    setErrors(validate(values));
  };
  const onSubmit = async (e) => {
    e.preventDefault();
    const errs = validate(values);
    setErrors(errs);
    setTouched({ name: true, email: true, msg: true });
    if (Object.keys(errs).length > 0) return;
    if (values._gotcha) return; // honeypot tripped

    setSending(true);
    setSendError('');
    try {
      const res = await fetch(CONTACT_ENDPOINT, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: values.name,
          email: values.email,
          message: values.msg,
        }),
      });
      if (res.ok) {
        setSent(true);
      } else {
        const data = await res.json().catch(() => ({}));
        setSendError(data?.error || 'Something went wrong. Email brendan@oneill.sg directly.');
      }
    } catch (err) {
      setSendError('Network error. Email brendan@oneill.sg directly.');
    } finally {
      setSending(false);
    }
  };

  const hint = (k, fallback) => (touched[k] && errors[k]) ? errors[k] : fallback;

  return (
    <form className="form" onSubmit={onSubmit} noValidate>
      <div className="form__spacer" />
      <div className="form__body">
        <div className="form__row">
          <div className={`form__field ${touched.name && errors.name ? 'invalid' : ''}`}>
            <label htmlFor="f-name">Name</label>
            <input
              id="f-name" type="text" autoComplete="name"
              value={values.name} onChange={update('name')} onBlur={onBlur('name')}
              placeholder="First and last"
              disabled={sent}
            />
            <span className="form__hint">{hint('name', 'Required.')}</span>
          </div>
          <div className={`form__field ${touched.email && errors.email ? 'invalid' : ''}`}>
            <label htmlFor="f-email">Email</label>
            <input
              id="f-email" type="email" autoComplete="email"
              value={values.email} onChange={update('email')} onBlur={onBlur('email')}
              placeholder="you@company.com"
              disabled={sent}
            />
            <span className="form__hint">{hint('email', 'Direct inbox. Not a list.')}</span>
          </div>
        </div>
        <div className={`form__field ${touched.msg && errors.msg ? 'invalid' : ''}`}>
          <label htmlFor="f-msg">What are you working on?</label>
          <textarea
            id="f-msg" rows={2}
            value={values.msg} onChange={update('msg')} onBlur={onBlur('msg')}
            placeholder="A sentence is enough."
            disabled={sent}
          />
          <span className="form__hint">{hint('msg', 'A sentence is enough.')}</span>
        </div>
        <div className="form__submit">
          <button type="submit" className={sent ? 'sent' : ''} disabled={sent || sending}>
            {sent && <span className="teal-dot" />}
            {sent ? 'Sent — I reply within a day.' : (sending ? 'Sending…' : 'Send')}
          </button>
          {!sent && !sendError && <span className="form__note">Direct to Brendan.</span>}
          {sendError && <span className="form__note form__note--err">{sendError}</span>}
        </div>
        {/* honeypot — hidden from humans, bots fill it, we discard */}
        <input
          type="text" name="_gotcha" tabIndex="-1" autoComplete="off"
          value={values._gotcha}
          onChange={(e) => setValues({ ...values, _gotcha: e.target.value })}
          style={{ position: 'absolute', left: '-9999px', width: '1px', height: '1px', opacity: 0 }}
          aria-hidden="true"
        />
      </div>
    </form>
  );
};

Object.assign(window, {
  Wordmark, Bar, Form, Reveal, useScrollSpy, useLisbonTime,
});
