// Blog — index of posts.
// Reads /content/blog/index.json (built by scripts/build-blog.mjs from
// the .md files in content/blog/).

const PAGE_SIZE_OPTIONS = [5, 10, 20, 30];
const DEFAULT_PAGE_SIZE = 10;
const TAGS_COLLAPSED_LIMIT = 20;

const Pagination = ({ currentPage, totalPages, setPage, T }) => {
  if (totalPages <= 1) return null;
  const pages = [];
  const delta = 2;
  for (let n = 1; n <= totalPages; n++) {
    if (n === 1 || n === totalPages || Math.abs(n - currentPage) <= delta) {
      pages.push(n);
    } else if (pages[pages.length - 1] !== '…') {
      pages.push('…');
    }
  }
  return (
    <div style={{
      display: 'flex', gap: 8, justifyContent: 'center',
      alignItems: 'center', flexWrap: 'wrap',
    }}>
      <button
        onClick={() => setPage(p => Math.max(1, p - 1))}
        disabled={currentPage === 1}
        style={{
          ...tagBtn(T),
          opacity: currentPage === 1 ? 0.3 : 1,
          cursor: currentPage === 1 ? 'not-allowed' : 'pointer',
        }}
      >← prev</button>
      {pages.map((n, i) => (
        n === '…' ? (
          <span key={`e${i}`} style={{ fontSize: 11, color: T.fgGhost, padding: '0 4px' }}>…</span>
        ) : (
          <button
            key={n}
            onClick={() => setPage(n)}
            style={{
              ...tagBtn(T),
              color: currentPage === n ? T.bg : T.fg,
              background: currentPage === n ? T.accent : 'transparent',
              fontWeight: currentPage === n ? 700 : 400,
            }}
          >{n}</button>
        )
      ))}
      <button
        onClick={() => setPage(p => Math.min(totalPages, p + 1))}
        disabled={currentPage === totalPages}
        style={{
          ...tagBtn(T),
          opacity: currentPage === totalPages ? 0.3 : 1,
          cursor: currentPage === totalPages ? 'not-allowed' : 'pointer',
        }}
      >next →</button>
    </div>
  );
};

const BlogPage = () => {
  const T = window.TOKENS;
  const [posts, setPosts] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [selectedTags, setSelectedTags] = React.useState([]);
  const [tagMatchMode, setTagMatchMode] = React.useState('any'); // 'any' | 'all'
  const [query, setQuery] = React.useState('');
  const [tagQuery, setTagQuery] = React.useState('');
  const [showAllTags, setShowAllTags] = React.useState(false);
  const [pageSize, setPageSize] = React.useState(DEFAULT_PAGE_SIZE);
  const [page, setPage] = React.useState(1);

  React.useEffect(() => {
    fetch('/content/blog/index.json', { cache: 'no-cache' })
      .then(r => {
        if (!r.ok) throw new Error(`index.json ${r.status}`);
        return r.json();
      })
      .then(setPosts)
      .catch(e => setError(e.message));
  }, []);

  React.useEffect(() => { setPage(1); }, [selectedTags, tagMatchMode, query, pageSize]);

  const tagInfo = React.useMemo(() => {
    if (!posts) return { sorted: [], counts: {} };
    const counts = {};
    posts.forEach(p => p.tags.forEach(t => { counts[t] = (counts[t] || 0) + 1; }));
    const sorted = Object.keys(counts).sort((a, b) =>
      counts[b] - counts[a] || a.localeCompare(b)
    );
    return { sorted, counts };
  }, [posts]);

  const filtered = React.useMemo(() => {
    if (!posts) return [];
    const q = query.trim().toLowerCase();
    return posts.filter(p => {
      if (selectedTags.length > 0) {
        const matches = tagMatchMode === 'all'
          ? selectedTags.every(t => p.tags.includes(t))
          : selectedTags.some(t => p.tags.includes(t));
        if (!matches) return false;
      }
      if (q && !p.title.toLowerCase().includes(q)) return false;
      return true;
    });
  }, [posts, selectedTags, tagMatchMode, query]);

  const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
  const currentPage = Math.min(page, totalPages);
  const visible = filtered.slice((currentPage - 1) * pageSize, currentPage * pageSize);

  const toggleTag = (t) => {
    setSelectedTags(prev => prev.includes(t) ? prev.filter(x => x !== t) : [...prev, t]);
  };

  // visible tag list = selected (always shown) + filtered/sorted remainder, collapsed unless toggled
  const tagSearchLower = tagQuery.trim().toLowerCase();
  const matchTagQuery = (t) => !tagSearchLower || t.toLowerCase().includes(tagSearchLower);
  const remainingTags = tagInfo.sorted.filter(t => !selectedTags.includes(t) && matchTagQuery(t));
  const tagDisplayLimit = showAllTags || tagSearchLower ? remainingTags.length : TAGS_COLLAPSED_LIMIT;
  const tagsShown = remainingTags.slice(0, tagDisplayLimit);
  const tagsHidden = Math.max(0, remainingTags.length - tagDisplayLimit);

  return (
    <div style={{
      minHeight: '100vh', position: 'relative', zIndex: 1,
      padding: '140px 40px 160px', maxWidth: 1200, margin: '0 auto',
      fontFamily: window.TOKENS.mono, color: window.TOKENS.fg,
    }}>
      <div style={{
        fontSize: 11, letterSpacing: '0.4em', color: T.accent, marginBottom: 24,
      }}>
        ━━ INDEX / BLOG
      </div>

      <window.GlitchTitle lines={[{ text: 'THE' }, { text: 'BLOG.', accent: true }]} />

      <div style={{
        marginTop: 24, fontSize: 14, color: T.fgMid, maxWidth: 600, lineHeight: 1.6,
      }}>
        Long-form writing on coding, AI engineering, Linux, adjacent rabbit holes, and
        life in general. Posts here are <span style={{ color: T.fg }}>canonical</span>
      </div>

      {/* search + page size */}
      <div style={{
        marginTop: 48, paddingTop: 24, borderTop: `1px solid ${T.fgFaint}`,
        display: 'flex', gap: 16, flexWrap: 'wrap', alignItems: 'center',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: '1 1 280px' }}>
          <span style={{ fontSize: 10, letterSpacing: '0.3em', color: T.fgGhost }}>SEARCH ›</span>
          <input
            type="text" value={query} onChange={e => setQuery(e.target.value)}
            placeholder="filter titles…"
            style={{
              flex: 1, padding: '8px 12px', background: T.bgSunken,
              border: `1px solid ${T.fgFaint}`, color: T.fg,
              fontFamily: T.mono, fontSize: 12, outline: 'none',
            }}
          />
          {query && (
            <button onClick={() => setQuery('')} style={tagBtn(T)}>clear</button>
          )}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ fontSize: 10, letterSpacing: '0.3em', color: T.fgGhost }}>PER PAGE ›</span>
          {PAGE_SIZE_OPTIONS.map(n => (
            <button
              key={n}
              onClick={() => setPageSize(n)}
              style={{
                ...tagBtn(T),
                color: pageSize === n ? T.bg : T.fg,
                background: pageSize === n ? T.accent : 'transparent',
              }}
            >{n}</button>
          ))}
        </div>
      </div>

      {/* tag filter */}
      <div style={{
        marginTop: 16, paddingTop: 16, borderTop: `1px solid ${T.fgFaint}`,
      }}>
        <div style={{
          display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center', marginBottom: 12,
        }}>
          <span style={{ fontSize: 10, letterSpacing: '0.3em', color: T.fgGhost }}>
            TAGS ({tagInfo.sorted.length}) ›
          </span>
          <input
            type="text" value={tagQuery} onChange={e => setTagQuery(e.target.value)}
            placeholder="find tag…"
            style={{
              flex: '0 1 180px', padding: '6px 10px', background: T.bgSunken,
              border: `1px solid ${T.fgFaint}`, color: T.fg,
              fontFamily: T.mono, fontSize: 11, outline: 'none',
            }}
          />
          <button
            onClick={() => { setSelectedTags([]); }}
            style={{
              ...tagBtn(T), color: selectedTags.length === 0 ? T.bg : T.fg,
              background: selectedTags.length === 0 ? T.accent : 'transparent',
            }}
          >all</button>
          {selectedTags.length > 0 && (
            <span style={{ fontSize: 10, color: T.fgGhost, letterSpacing: '0.2em' }}>
              {selectedTags.length} SELECTED ·
            </span>
          )}
          {selectedTags.length > 1 && (
            <div style={{ display: 'flex', gap: 4 }}>
              <button
                onClick={() => setTagMatchMode('any')}
                style={{
                  ...tagBtn(T),
                  color: tagMatchMode === 'any' ? T.bg : T.fg,
                  background: tagMatchMode === 'any' ? T.accent : 'transparent',
                }}
                title="Post matches if it has any selected tag"
              >ANY</button>
              <button
                onClick={() => setTagMatchMode('all')}
                style={{
                  ...tagBtn(T),
                  color: tagMatchMode === 'all' ? T.bg : T.fg,
                  background: tagMatchMode === 'all' ? T.accent : 'transparent',
                }}
                title="Post matches only if it has all selected tags"
              >ALL</button>
            </div>
          )}
        </div>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {selectedTags.map(t => (
            <button
              key={`s-${t}`}
              onClick={() => toggleTag(t)}
              style={{ ...tagBtn(T), color: T.bg, background: T.accent }}
            >#{t} <span style={{ opacity: 0.6, marginLeft: 4 }}>×</span></button>
          ))}
          {tagsShown.map(t => (
            <button
              key={t}
              onClick={() => toggleTag(t)}
              style={{ ...tagBtn(T) }}
            >
              #{t}
              <span style={{ color: T.fgGhost, marginLeft: 4, fontSize: 9 }}>
                {tagInfo.counts[t]}
              </span>
            </button>
          ))}
          {tagsHidden > 0 && (
            <button
              onClick={() => setShowAllTags(true)}
              style={{ ...tagBtn(T), color: T.fgDim, fontStyle: 'italic' }}
            >+ {tagsHidden} more</button>
          )}
          {showAllTags && !tagSearchLower && tagInfo.sorted.length > TAGS_COLLAPSED_LIMIT && (
            <button
              onClick={() => setShowAllTags(false)}
              style={{ ...tagBtn(T), color: T.fgDim, fontStyle: 'italic' }}
            >collapse</button>
          )}
          {tagSearchLower && remainingTags.length === 0 && (
            <span style={{ fontSize: 11, color: T.fgGhost, padding: '6px 12px' }}>
              no tags match "{tagQuery}"
            </span>
          )}
        </div>
      </div>

      {/* result count */}
      {posts && (
        <div style={{ marginTop: 24, fontSize: 11, color: T.fgGhost, letterSpacing: '0.2em' }}>
          {filtered.length} POST{filtered.length === 1 ? '' : 'S'}
          {filtered.length !== posts.length && ` / ${posts.length} TOTAL`}
          {totalPages > 1 && ` · PAGE ${currentPage} / ${totalPages}`}
        </div>
      )}

      {/* TOP pagination */}
      {posts && totalPages > 1 && (
        <div style={{ marginTop: 16 }}>
          <Pagination currentPage={currentPage} totalPages={totalPages} setPage={setPage} T={T} />
        </div>
      )}

      {/* posts list */}
      {posts === null && !error && (
        <div style={{ marginTop: 48, fontSize: 12, letterSpacing: '0.3em', color: T.fgGhost }}>
          LOADING POSTS…
        </div>
      )}
      {error && (
        <div style={{ marginTop: 48, fontSize: 12, letterSpacing: '0.2em', color: T.danger }}>
          [ERROR] {error}. Run `npm run build` to regenerate /content/blog/index.json.
        </div>
      )}
      {posts && posts.length === 0 && (
        <div style={{ marginTop: 48, fontSize: 13, color: T.fgDim }}>
          No posts yet. Drop a .md in /content/blog/ and run `npm run build`.
        </div>
      )}
      {posts && posts.length > 0 && filtered.length === 0 && (
        <div style={{ marginTop: 48, fontSize: 13, color: T.fgDim }}>
          No posts match. Clear filters or try a different query.
        </div>
      )}
      <div style={{ marginTop: 32 }}>
        {visible.map((p, i) => (
          <article
            key={p.id}
            onClick={() => window.navigate(`/blog/${p.slug}`)}
            style={{
              display: 'grid', gridTemplateColumns: '60px 100px 1fr 80px',
              alignItems: 'baseline', gap: 24,
              padding: '28px 0',
              borderTop: `1px solid ${T.fgFaint}`,
              borderBottom: i === visible.length - 1 ? `1px solid ${T.fgFaint}` : 'none',
              cursor: 'pointer', position: 'relative',
              transition: 'transform .15s',
            }}
            onMouseEnter={(e) => {
              e.currentTarget.style.transform = 'translateX(8px)';
              e.currentTarget.querySelector('h2').style.color = window.TOKENS.accent;
            }}
            onMouseLeave={(e) => {
              e.currentTarget.style.transform = 'translateX(0)';
              e.currentTarget.querySelector('h2').style.color = window.TOKENS.fg;
            }}
          >
            <span style={{ fontSize: 12, color: T.fgGhost, letterSpacing: '0.1em' }}>
              [{p.id}]
            </span>
            <span style={{ fontSize: 11, color: T.fgDim, letterSpacing: '0.1em' }}>
              {p.date.replace(/-/g, '.')}
            </span>
            <div>
              <h2 style={{
                margin: 0, fontSize: 24, fontWeight: 700, letterSpacing: '-0.01em',
                lineHeight: 1.25, transition: 'color .15s',
              }}>
                {p.title}
              </h2>
              <div style={{ marginTop: 10, fontSize: 13, color: T.fgMid, lineHeight: 1.5, maxWidth: 680 }}>
                {p.excerpt}
              </div>
              <div style={{ marginTop: 12, display: 'flex', gap: 12, fontSize: 10, letterSpacing: '0.15em', color: T.fgGhost, flexWrap: 'wrap' }}>
                {p.tags.map(t => <span key={t}>#{t}</span>)}
              </div>
            </div>
            <span style={{ fontSize: 11, color: T.fgDim, letterSpacing: '0.1em', textAlign: 'right' }}>
              {p.readMin} MIN →
            </span>
          </article>
        ))}
      </div>

      {/* BOTTOM pagination */}
      {posts && totalPages > 1 && (
        <div style={{ marginTop: 32 }}>
          <Pagination currentPage={currentPage} totalPages={totalPages} setPage={setPage} T={T} />
        </div>
      )}

      <div style={{
        marginTop: 64, padding: 24, border: `1px solid ${T.fgFaint}`,
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        flexWrap: 'wrap', gap: 16,
      }}>
        <div>
          <div style={{ fontSize: 11, letterSpacing: '0.3em', color: T.fgGhost, marginBottom: 4 }}>
            ━━ SUBSCRIBE
          </div>
          <div style={{ fontSize: 14, color: T.fgMid }}>
            Email signup not wired yet. RSS works today: <a
              href="/feed.xml"
              style={{ color: T.fg, textDecoration: 'underline', textUnderlineOffset: 3 }}
            >/feed.xml</a>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8 }} title="Email signup coming soon. Use RSS for now.">
          <input
            type="email" placeholder="coming soon"
            disabled
            aria-disabled="true"
            style={{
              padding: '10px 14px', background: T.bgSunken, border: `1px solid ${T.fgFaint}`,
              color: T.fgDim, fontFamily: T.mono, fontSize: 13, minWidth: 220, outline: 'none',
              cursor: 'not-allowed', opacity: 0.6,
            }}
          />
          <button
            type="button"
            disabled
            aria-disabled="true"
            style={{
              padding: '10px 20px', background: T.fgFaint, color: T.fgDim,
              border: 'none', fontFamily: T.mono, fontSize: 11, letterSpacing: '0.2em',
              textTransform: 'uppercase', cursor: 'not-allowed', fontWeight: 700,
              opacity: 0.7,
            }}
          >
            soon →
          </button>
        </div>
      </div>
    </div>
  );
};

const tagBtn = (T) => ({
  padding: '6px 12px', border: `1px solid ${T.fgFaint}`, fontFamily: T.mono,
  fontSize: 11, letterSpacing: '0.1em', cursor: 'pointer',
  background: 'transparent', color: T.fg,
});

window.BlogPage = BlogPage;
