/* Writing page — pulls from Substack RSS (client-side within these static
 * prototype constraints; a Next.js Route Handler with revalidate:21600 is
 * the prod target — logic lifts cleanly).
 *
 * Source feed: https://vibedebt.substack.com/feed
 * Via rss2json (public CORS-friendly parser).
 * Cache in localStorage for 6h (21600s). On failure, serve last cache with
 * a quiet "last fetched …" note. No cache + no response → "writing soon."
 */

const SUBSTACK_URL   = 'https://vibedebt.substack.com';
const FEED_URL       = 'https://vibedebt.substack.com/feed';
const FEED_VIA_RSS2J = 'https://api.rss2json.com/v1/api.json?rss_url=' + encodeURIComponent(FEED_URL);
const CACHE_KEY      = 'pf_writing_cache_v1';
const CACHE_TTL_MS   = 21600 * 1000;   // 6 hours

function stripHtml(html) {
  if (!html) return '';
  const tmp = document.createElement('div');
  tmp.innerHTML = html;
  return (tmp.textContent || tmp.innerText || '').replace(/\s+/g, ' ').trim();
}

function readingTimeFor(contentHtml) {
  const text = stripHtml(contentHtml);
  const words = text ? text.split(/\s+/).filter(Boolean).length : 0;
  if (words < 100) return 'short';
  return Math.max(1, Math.ceil(words / 225)) + ' min';
}

function excerptFor(p) {
  // prefer provided description; otherwise first 240 chars of content
  const source = p.description || p.content || '';
  const text = stripHtml(source);
  if (!text) return '';
  // cut at sentence boundary where possible, under ~220 chars
  const max = 220;
  if (text.length <= max) return text;
  const slice = text.slice(0, max);
  const lastStop = Math.max(slice.lastIndexOf('.'), slice.lastIndexOf('!'), slice.lastIndexOf('?'));
  if (lastStop > 80) return slice.slice(0, lastStop + 1);
  return slice.replace(/\s+\S*$/, '') + '…';
}

function sentenceCaseTitle(title) {
  if (!title) return '';
  // preserve all-caps acronyms (2+ caps); lowercase everything else
  const base = title.split(/(\s+)/).map(tok => {
    if (/^\s+$/.test(tok)) return tok;
    if (/^[A-Z]{2,}[A-Z0-9]*$/.test(tok)) return tok;
    return tok.toLowerCase();
  }).join('');
  // restore "I" pronoun + its contractions, then capitalize sentence starts
  return base
    .replace(/\bi\b/g, 'I')
    .replace(/\bi'/g, "I'")
    .replace(/(^|[.!?]\s+)([a-z])/g, (_, pre, c) => pre + c.toUpperCase());
}

function formatDate(iso) {
  try {
    const d = new Date(iso);
    if (isNaN(d.getTime())) return '';
    const mo = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'][d.getMonth()];
    return mo + ' ' + d.getDate() + ', ' + d.getFullYear();
  } catch (e) { return ''; }
}

function readCache() {
  try {
    const raw = localStorage.getItem(CACHE_KEY);
    if (!raw) return null;
    return JSON.parse(raw);
  } catch (e) { return null; }
}

function writeCache(payload) {
  try {
    localStorage.setItem(CACHE_KEY, JSON.stringify({
      fetchedAt: Date.now(),
      items: payload,
    }));
  } catch (e) { /* ignore quota */ }
}

async function fetchFeed() {
  const res = await fetch(FEED_VIA_RSS2J, { cache: 'no-store' });
  if (!res.ok) throw new Error('feed http ' + res.status);
  const body = await res.json();
  if (!body || body.status !== 'ok' || !Array.isArray(body.items)) {
    throw new Error('feed payload malformed');
  }
  return body.items.map((it) => ({
    title:       it.title || 'untitled',
    link:        it.link,
    pubDate:     it.pubDate || it.pub_date || '',
    description: it.description || '',
    content:     it.content || '',
  }));
}

function WritingPost({ p }) {
  const date = formatDate(p.pubDate);
  const time = readingTimeFor(p.content || p.description);
  const excerpt = excerptFor(p);
  return (
    <li className="pf-writing-post">
      <div className="pf-writing-post-date">{date}</div>
      <div className="pf-writing-post-main">
        <a className="pf-writing-post-title" href={p.link} target="_blank" rel="noopener">
          {sentenceCaseTitle(p.title || '')}
        </a>
        {excerpt && <p className="pf-writing-post-excerpt">{excerpt}</p>}
      </div>
      <div className="pf-writing-post-time">· {time}</div>
    </li>
  );
}

function WritingPage() {
  const { useState, useEffect } = React;
  const [state, setState] = useState(() => {
    const cached = readCache();
    if (cached && cached.items) {
      const fresh = (Date.now() - cached.fetchedAt) < CACHE_TTL_MS;
      return {
        items: cached.items,
        loading: !fresh,
        error: null,
        stale: !fresh,
        fetchedAt: cached.fetchedAt,
      };
    }
    return { items: null, loading: true, error: null, stale: false, fetchedAt: 0 };
  });

  useEffect(() => {
    const cached = readCache();
    const fresh = cached && (Date.now() - cached.fetchedAt) < CACHE_TTL_MS;
    if (fresh) return;   // still within ttl — skip network

    let cancelled = false;
    (async () => {
      try {
        const items = await fetchFeed();
        if (cancelled) return;
        writeCache(items);
        setState({ items, loading: false, error: null, stale: false, fetchedAt: Date.now() });
      } catch (err) {
        if (cancelled) return;
        const prev = readCache();
        if (prev && prev.items && prev.items.length) {
          setState({ items: prev.items, loading: false, error: null, stale: true, fetchedAt: prev.fetchedAt });
        } else {
          setState({ items: null, loading: false, error: err.message || 'fetch failed', stale: false, fetchedAt: 0 });
        }
      }
    })();
    return () => { cancelled = true; };
  }, []);

  const { items, loading, error, stale, fetchedAt } = state;

  let body;
  if (loading && (!items || !items.length)) {
    body = <p className="pf-writing-status">fetching from substack…</p>;
  } else if (items && items.length) {
    body = (
      <>
        <ul className="pf-writing-list">
          {items.map((p, i) => <WritingPost key={p.link || i} p={p} />)}
        </ul>
        {stale && (
          <p className="pf-writing-status stale">
            showing last cached version · fetched {formatDate(new Date(fetchedAt).toISOString())} · live feed unreachable right now.
          </p>
        )}
      </>
    );
  } else if (error) {
    body = <p className="pf-writing-status">writing soon.</p>;
  } else {
    body = <p className="pf-writing-status">writing soon.</p>;
  }

  return (
    <article data-screen-label="writing">
      <header className="pf-wp-hero">
        <div className="pf-wp-num">writing</div>
        <div className="pf-wp-head">
          <span className="pf-wp-kicker">essays · notes · substack</span>
          <h1 className="pf-wp-title">writing<span className="pf-hero-dot">.</span></h1>
          <p className="pf-wp-lede">
            Notes on AI automation in production. What breaks, what holds,
            and the patterns that travel. Published on{' '}
            <a href={SUBSTACK_URL} target="_blank" rel="noopener">Substack</a>,
            pulled live here.
          </p>
        </div>
      </header>

      <section className="pf-wp-section" data-screen-label="writing-list">
        <div className="pf-wp-section-label">posts</div>
        <div className="pf-wp-section-body">
          {body}
          <div className="pf-writing-footer-link">
            <a href={SUBSTACK_URL} target="_blank" rel="noopener">
              subscribe on substack →
            </a>
          </div>
        </div>
      </section>
    </article>
  );
}

function PrinciplesPage() {
  return (
    <article data-screen-label="principles">
      <header className="pf-wp-hero">
        <div className="pf-wp-num">principles</div>
        <div className="pf-wp-head">
          <span className="pf-wp-kicker">the engineering bible</span>
          <h1 className="pf-wp-title">principles<span className="pf-hero-dot">.</span></h1>
          <p className="pf-wp-lede">
            The rules I work by, written down. Chapters covering the why,
            the decision, and the tradeoff behind each one. Intro + chapters
            coming in a later pass.
          </p>
        </div>
      </header>

      <section className="pf-soon" data-screen-label="principles-soon">
        <div className="pf-soon-label">coming soon</div>
        <div className="pf-soon-body">
          <p>
            The engineering bible surfaces as readable chapters here. Each
            principle gets its own short section: the why, the decision, the
            tradeoff.
          </p>
          <p className="stone">
            Three-paragraph intro + chapters. Copy pending.
          </p>
        </div>
      </section>
    </article>
  );
}

function TrajectoryPage() {
  const horizons = [
    { n: '01', span: 'near-term',  window: 'next 6 months' },
    { n: '02', span: 'mid-term',   window: '1 — 2 years'    },
    { n: '03', span: 'long-term',  window: '3 — 5 years'    },
  ];
  return (
    <article data-screen-label="trajectory">
      <header className="pf-wp-hero">
        <div className="pf-wp-num">trajectory</div>
        <div className="pf-wp-head">
          <span className="pf-wp-kicker">where I&rsquo;m headed · subject to revision</span>
          <h1 className="pf-wp-title">trajectory<span className="pf-hero-dot">.</span></h1>
          <p className="pf-wp-lede">
            Three horizons, written out honestly. Most portfolios don&rsquo;t
            do this. I&rsquo;d rather say what I&rsquo;m aiming at and update
            as things change than leave it implicit.
          </p>
        </div>
      </header>

      <section className="pf-horizons" data-screen-label="trajectory-horizons">
        {horizons.map(h => (
          <div className="pf-horizon" key={h.n}>
            <div className="pf-horizon-key">
              <span className="pf-horizon-num">{h.n}</span>
              <span className="pf-horizon-span">{h.span}</span>
              <span className="pf-horizon-num">{h.window}</span>
            </div>
            <div className="pf-horizon-body">
              <h3>content pending.</h3>
              <p style={{ fontFamily: 'var(--font-mono)', fontSize: 14, lineHeight: 1.75, color: 'var(--stone)', margin: 0 }}>
                [PLACEHOLDER: copy to be written in a later pass.]
              </p>
            </div>
          </div>
        ))}
      </section>
    </article>
  );
}

Object.assign(window, {
  WritingPage, PrinciplesPage, TrajectoryPage,
  WritingPost, fetchFeed, readCache, writeCache, CACHE_TTL_MS, SUBSTACK_URL, formatDate,
});
