/* AIMSify Blog — page components (Index, Article, Category, Author, Search) */

const {
  POSTS, AUTHORS, FRAMEWORKS, TOPICS, ROLES, TYPES,
  navigate, useRoute, fmtDate,
  Header, Footer, PostCard, Newsletter, AuthorCard, Related, FwTag, TypeBadge, Avatar,
  ContactCTA, Diagram,
} = window;

// ============================================================
// INDEX PAGE
// ============================================================
function IndexPage() {
  const [filter, setFilter] = useState({ framework: null, topic: null, role: null, type: null });

  const filtered = useMemo(() => {
    return POSTS.filter((p) => {
      if (filter.framework && !p.frameworks.includes(filter.framework)) return false;
      if (filter.topic && !p.topics.includes(filter.topic)) return false;
      if (filter.role && !p.roles.includes(filter.role)) return false;
      if (filter.type && p.type !== filter.type) return false;
      return true;
    });
  }, [filter]);

  const featured = POSTS.filter((p) => p.featured);
  const primary = featured[0];
  const secondary = featured.slice(1, 4);

  const setOne = (key, val) => setFilter((f) => ({ ...f, [key]: f[key] === val ? null : val }));
  const clearAll = () => setFilter({ framework: null, topic: null, role: null, type: null });
  const hasFilter = filter.framework || filter.topic || filter.role || filter.type;

  return (
    <>
      <Header active="home" />

      <section className="page-hero">
        <div className="eyebrow eyebrow-emerald">The AIMSify Journal · Issue 04</div>
        <h1 className="display" style={{ marginTop: 18 }}>
          Field notes for the practitioners <em>building Singapore's</em> AI assurance and cyber programmes.
        </h1>
        <p className="lede">
          Framework deep-dives, regulatory updates, and CISO playbooks — written by people running real programmes,
          not commentators. Published weekly.
        </p>
      </section>

      <div className="index-shell">
        {/* Featured row */}
        {!hasFilter && primary && (
          <section className="featured-row">
            <PostCard post={primary} variant="feature" />
            <div>
              <div className="eyebrow eyebrow-emerald" style={{ marginBottom: 18 }}>Editor's picks</div>
              {secondary.map((p) => <PostCard key={p.id} post={p} variant="secondary" />)}
            </div>
          </section>
        )}

        {/* Filters */}
        <div className="filter-bar">
          <span className="filter-label">Framework</span>
          {['iso27001', 'iso42001', 'nist_csf', 'nist_airmf', 'mas_trm', 'eu_aiact', 'ctm'].map((f) => (
            <FwTag key={f} id={f} active={filter.framework === f} onClick={() => setOne('framework', f)} />
          ))}
        </div>
        <div className="filter-bar" style={{ borderTop: 'none' }}>
          <span className="filter-label">Topic</span>
          {Object.entries(TOPICS).map(([k, v]) => (
            <button key={k}
              className={`fw-tag ${filter.topic === k ? 'is-active' : ''}`}
              onClick={() => setOne('topic', k)}>
              {v}
            </button>
          ))}
          <span className="filter-label" style={{ marginLeft: 16 }}>For</span>
          {Object.entries(ROLES).map(([k, v]) => (
            <button key={k}
              className={`fw-tag ${filter.role === k ? 'is-active' : ''}`}
              onClick={() => setOne('role', k)}>
              {v}
            </button>
          ))}
          {hasFilter && (
            <button className="fw-tag" style={{ marginLeft: 'auto', borderStyle: 'dashed' }} onClick={clearAll}>
              ✕ Clear filters
            </button>
          )}
        </div>

        {/* Grid */}
        <section className="posts-grid-section">
          <div className="posts-grid-header">
            <h2>{hasFilter ? `Filtered · ${filtered.length} ${filtered.length === 1 ? 'piece' : 'pieces'}` : 'All journal entries'}</h2>
            <span style={{ fontSize: 13, color: 'var(--ink-4)' }}>{POSTS.length} pieces · updated weekly</span>
          </div>
          <div className="posts-grid">
            {filtered.map((p) => <PostCard key={p.id} post={p} />)}
          </div>
          {filtered.length === 0 && (
            <div style={{ padding: '48px 0', textAlign: 'center', color: 'var(--ink-4)', fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 18 }}>
              No pieces match those filters yet.
            </div>
          )}
        </section>

        <Newsletter />
      </div>

      <Footer />
    </>
  );
}

// ============================================================
// ARTICLE PAGE
// ============================================================
function ArticlePage({ slug }) {
  const post = POSTS.find((p) => p.slug === slug);
  const [activeId, setActiveId] = useState(null);
  const articleRef = useRef(null);

  const blocks = post
    ? ((window.ARTICLE_BODIES || {})[post.id] || [{ kind: 'p', t: post.excerpt }])
    : [];
  const sections = blocks.filter((b) => b.kind === 'h2');

  // Scroll-spy — hook MUST run unconditionally; gate inside.
  useEffect(() => {
    if (!post) return;
    const obs = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) setActiveId(e.target.id);
      });
    }, { rootMargin: '-30% 0% -60% 0%' });
    sections.forEach((s) => {
      const el = document.getElementById(s.id);
      if (el) obs.observe(el);
    });
    return () => obs.disconnect();
  }, [post && post.id]);

  if (!post) return <NotFound />;
  const author = AUTHORS[post.author];

  return (
    <>
      <Header />

      <article className="article-shell">
        <div className="article-head">
          <div className="breadcrumb">
            <a href="#/">Journal</a>
            <span>›</span>
            <a href={`#/category/type/${post.type}`}>{TYPES[post.type].label}</a>
          </div>
          <TypeBadge type={post.type} />
          <h1>{post.title}</h1>
          <p className="article-dek">{post.dek}</p>
          <div className="article-meta">
            <span className="meta-author">
              By <a href={`#/author/${author.id}`} className="meta-author-link">{author.name}</a>
              <span className="meta-author-title"> &middot; {author.title}</span>
            </span>
            <span className="meta-sep">&middot;</span>
            <span>{fmtDate(post.date)}</span>
            <span className="meta-sep">&middot;</span>
            <span className="read-mins">{post.readMins} min read</span>
            <span className="meta-sep">&middot;</span>
            <div className="meta-fws">
              {post.frameworks.map((f) => <FwTag key={f} id={f} asLink />)}
            </div>
          </div>

          {/* TL;DR pull-quote bar — the hook before the read */}
          <TldrBar items={post.tldr || []} />
        </div>

        <div className="article-body-wrap">
          {/* TOC */}
          <aside className="toc-rail">
            <h4>In this piece</h4>
            <ol>
              {sections.map((s) => (
                <li key={s.id}>
                  <a href={`#${s.id}`} className={activeId === s.id ? 'is-active' : ''}>{s.t}</a>
                </li>
              ))}
            </ol>
          </aside>

          {/* Body */}
          <div className="article-body" ref={articleRef}>
            {blocks.map((b, i) => <ArticleBlock key={i} b={b} />)}

            {/* Authoritative third-party references */}
            <ReferencesPanel articleId={post.id} />

            {/* THE three-door CTA — the only navy moment, sorts readers by intent */}
            <ThreeDoorBand post={post} articleSlug={post.slug} />

            {/* Text-only author byline — the photo card stack is gone */}
            <AuthorByline authorId={post.author} />

            {/* Editorial newsletter strip — light, type-led, NOT navy */}
            <EditorialNewsletter />

            <Related post={post} />
          </div>

          {/* Share */}
          <aside className="share-rail">
            <h4>Share</h4>
            <button className="share-btn" title="Copy link" onClick={(e) => {
              navigator.clipboard?.writeText(location.href);
              const btn = e.currentTarget; btn.classList.add('copied');
              setTimeout(() => btn.classList.remove('copied'), 1400);
            }}>
              {/* Up-right share arrow (iOS-style) */}
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M7 17 L17 7"/>
                <path d="M9 7 H17 V15"/>
              </svg>
              <span className="copied-flash">Copied</span>
            </button>
            <a className="share-btn" title="Share on LinkedIn"
               href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(location.href)}`} target="_blank" rel="noreferrer">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M20.45 20.45h-3.55v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.95v5.66H9.36V9h3.41v1.56h.05c.47-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z"/></svg>
            </a>
            <a className="share-btn" title="Share on X"
               href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(post.title)}&url=${encodeURIComponent(location.href)}`} target="_blank" rel="noreferrer">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
            </a>
            <a className="share-btn" title="Email"
               href={`mailto:?subject=${encodeURIComponent(post.title)}&body=${encodeURIComponent(location.href)}`}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 5L2 7"/></svg>
            </a>
          </aside>
        </div>
      </article>

      <Footer />
    </>
  );
}

function ArticleBlock({ b }) {
  switch (b.kind) {
    case 'p': return <p>{b.t}</p>;
    case 'h2': return <h2 id={b.id}>{b.t}</h2>;
    case 'h3': return <h3 id={b.id}>{b.t}</h3>;
    case 'ul': return <ul>{b.items.map((it, i) => <li key={i}>{it}</li>)}</ul>;
    case 'ol': return <ol>{b.items.map((it, i) => <li key={i}>{it}</li>)}</ol>;
    case 'quote': return (
      <blockquote>
        "{b.t}"
        {b.cite && <cite>{b.cite}</cite>}
      </blockquote>
    );
    case 'callout': return (
      <div className={`callout ${b.tone || ''}`}>
        {b.eyebrow && <div className="callout-eyebrow">{b.eyebrow}</div>}
        <div>{b.t}</div>
      </div>
    );
    case 'figure': {
      const { kind: _ignore, ...rest } = b;
      return <Diagram {...rest} />;
    }
    case 'cta':    return null; /* legacy mid-article CTA suppressed */
    case 'inlinecta': return <InlineCTA {...b} />; /* new soft contextual CTA */
    case 'image': {
      return (
        <figure className="article-image">
          <img src={b.src} alt={b.alt || ''} loading="lazy" />
          {b.caption ? <figcaption>{b.caption}</figcaption> : null}
        </figure>
      );
    }
    default: return null;
  }
}

// ============================================================
// CATEGORY PAGE
// ============================================================
function CategoryPage({ kind, catKey }) {
  let label, sublabel, posts;
  if (kind === 'framework') {
    const fw = FRAMEWORKS[catKey];
    if (!fw) return <NotFound />;
    label = fw.label;
    sublabel = 'Framework';
    posts = POSTS.filter((p) => p.frameworks.includes(catKey));
  } else if (kind === 'topic') {
    label = TOPICS[catKey];
    if (!label) return <NotFound />;
    sublabel = 'Topic';
    posts = POSTS.filter((p) => p.topics.includes(catKey));
  } else if (kind === 'type') {
    label = TYPES[catKey]?.label;
    if (!label) return <NotFound />;
    sublabel = 'Format';
    posts = POSTS.filter((p) => p.type === catKey);
  } else if (kind === 'role') {
    label = ROLES[catKey];
    if (!label) return <NotFound />;
    sublabel = 'For';
    posts = POSTS.filter((p) => p.roles.includes(catKey));
  } else {
    return <NotFound />;
  }

  const totalRead = posts.reduce((s, p) => s + p.readMins, 0);
  const lastUpdated = posts.length ? posts[0].date : null;
  const distinctAuthors = new Set(posts.map((p) => p.author)).size;

  // Category-specific copy
  const dek = catCopy(kind, catKey, posts.length);

  return (
    <>
      <Header />

      <section className="category-hero">
        <div className="category-hero-inner">
          <div className="eyebrow eyebrow-emerald">{sublabel}</div>
          <h1 className="display" style={{ marginTop: 12, fontSize: 'clamp(36px, 5vw, 56px)' }}>{label}</h1>
          <p className="lede" style={{ marginTop: 18, maxWidth: '60ch' }}>{dek}</p>
          <div className="stat-strip">
            <div className="stat">
              <div className="stat-num">{posts.length}</div>
              <div className="stat-label">Pieces published</div>
            </div>
            <div className="stat">
              <div className="stat-num">{totalRead}</div>
              <div className="stat-label">Total read time (mins)</div>
            </div>
            <div className="stat">
              <div className="stat-num">{distinctAuthors}</div>
              <div className="stat-label">Contributing authors</div>
            </div>
            {lastUpdated && (
              <div className="stat">
                <div className="stat-num" style={{ fontSize: 22, paddingTop: 14 }}>{fmtDate(lastUpdated)}</div>
                <div className="stat-label">Last updated</div>
              </div>
            )}
          </div>
        </div>
      </section>

      <div className="index-shell">
        <section className="posts-grid-section">
          {posts.length === 0 ? (
            <div style={{ padding: '64px 0', textAlign: 'center', color: 'var(--ink-4)', fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 20 }}>
              No pieces in this category yet.
            </div>
          ) : (
            <div className="posts-grid">
              {posts.map((p) => <PostCard key={p.id} post={p} />)}
            </div>
          )}
        </section>
        <Newsletter />
      </div>

      <Footer />
    </>
  );
}

function catCopy(kind, key, n) {
  const map = {
    'framework:iso27001': 'Practitioner notes on ISO 27001:2022 implementation, Stage 2 audit preparation, and Annex A control evidence.',
    'framework:iso42001': 'Field notes on ISO 42001 — the AI Management System every CISO will be asked about by 2027.',
    'framework:nist_csf': 'Working with NIST Cybersecurity Framework 2.0 across regulated environments.',
    'framework:nist_airmf': 'Operationalising the NIST AI Risk Management Framework alongside ISO 42001.',
    'framework:mas_trm': 'MAS Technology Risk Management — examiner expectations, control evidence, and operational guidance for Singapore-regulated firms.',
    'framework:eu_aiact': 'EU AI Act — high-risk classification, conformity assessment, and post-market monitoring.',
    'framework:ctm': 'The Cyber Trust Mark scheme from CSA Singapore — tier criteria, common pitfalls, and ISO leverage.',
    'framework:csa_ccm': 'Cloud Security Alliance Cloud Controls Matrix and its role in cloud assurance.',
    'framework:soc2':    'SOC 2 alignment for product organisations and B2B SaaS.',
    'framework:dptm':    'Singapore\'s Data Protection Trust Mark — accountability evidence and PDPA alignment.',
    'topic:ai_governance':'AI governance — from shadow AI discovery to ISO 42001 certification, and everything in between.',
    'topic:supplier_risk':'Supplier risk and fourth-party concentration — the assurance question of 2026.',
    'topic:audit':       'Audit and assurance — evidence packs, Stage 2 preparation, and examiner expectations.',
    'topic:regulatory':  'Regulatory updates — what changed, what it means, what to do this quarter.',
    'topic:isms':        'ISMS practice — making the management system functional, not just documented.',
    'topic:risk':        'Risk management — from register to board report.',
    'topic:cloud':       'Cloud security and shared-responsibility assurance.',
    'topic:privacy':     'Privacy practice — PDPA, GDPR alignment, and ISO 27701 leverage.',
    'type:deepdive':  'Long-form analysis — framework deep-dives, control breakdowns, and field synthesis.',
    'type:update':    'Regulatory updates — concise, timely, and operational.',
    'type:playbook':  'CISO playbooks — repeatable patterns from the field.',
    'type:opinion':   'Opinion and thought leadership — from practitioners with skin in the game.',
    'role:ciso':      'For CISOs and Heads of Security.',
    'role:grc':       'For GRC teams who do the actual work.',
    'role:board':     'For boards and audit committees who make the calls.',
    'role:legal':     'For Legal, DPOs, and compliance counsel.',
  };
  const fallback = `${n} ${n === 1 ? 'piece' : 'pieces'} on this topic.`;
  return map[`${kind}:${key}`] || fallback;
}

// ============================================================
// AUTHOR PAGE
// ============================================================
function AuthorPage({ id }) {
  const a = AUTHORS[id];
  if (!a) return <NotFound />;
  const posts = POSTS.filter((p) => p.author === id);
  const totalRead = posts.reduce((s, p) => s + p.readMins, 0);

  // Topics this author writes about
  const topicCounts = {};
  posts.forEach((p) => p.topics.forEach((t) => { topicCounts[t] = (topicCounts[t] || 0) + 1; }));
  const topTopics = Object.entries(topicCounts).sort((a, b) => b[1] - a[1]).slice(0, 4);

  return (
    <>
      <Header />

      <div className="author-hero-band">
        <section className="author-hero">
          <span className="avatar-xl" style={{ background: a.tint }}>{a.mono}</span>
          <div>
            <div className="eyebrow eyebrow-emerald">Contributing author</div>
            <h1 className="display" style={{ fontSize: 'clamp(32px, 4vw, 48px)', marginTop: 12 }}>{a.name}</h1>
            <p className="lede" style={{ marginTop: 12, maxWidth: '60ch' }}>{a.title} · {a.location}</p>
            <p style={{ fontFamily: 'var(--serif)', fontSize: 18, lineHeight: 1.6, color: 'var(--ink-2)', marginTop: 20, maxWidth: '62ch' }}>{a.bio}</p>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 18 }}>
              {a.certs.map((c) => <span className="cert-pill" key={c}>{c}</span>)}
            </div>
          </div>
        </section>
      </div>

      <div className="index-shell">
        <div className="filter-bar">
          <span className="filter-label">Writes about</span>
          {topTopics.map(([k, n]) => (
            <a key={k} className="fw-tag" href={`#/category/topic/${k}`}>{TOPICS[k]} <span style={{ color: 'var(--ink-5)', marginLeft: 4 }}>{n}</span></a>
          ))}
          <span style={{ marginLeft: 'auto', fontSize: 12, color: 'var(--ink-4)' }}>
            {posts.length} pieces · {totalRead} mins of reading
          </span>
        </div>

        <section className="posts-grid-section">
          <div className="posts-grid">
            {posts.map((p) => <PostCard key={p.id} post={p} />)}
          </div>
        </section>

        <Newsletter />
      </div>

      <Footer />
    </>
  );
}

// ============================================================
// SEARCH PAGE
// ============================================================
function SearchPage() {
  const [q, setQ] = useState('');
  const inputRef = useRef(null);

  useEffect(() => { inputRef.current?.focus(); }, []);

  const results = useMemo(() => {
    const s = q.trim().toLowerCase();
    if (!s) return [];
    return POSTS.filter((p) => {
      const hay = [
        p.title, p.dek, p.excerpt,
        AUTHORS[p.author]?.name,
        ...p.frameworks.map((f) => FRAMEWORKS[f]?.label),
        ...p.topics.map((t) => TOPICS[t]),
        TYPES[p.type]?.label,
      ].join(' ').toLowerCase();
      return s.split(/\s+/).every((tok) => hay.includes(tok));
    });
  }, [q]);

  const suggestions = ['ISO 42001', 'EU AI Act', 'MAS TRM', 'supplier risk', 'audit evidence', 'CSA licensing'];

  return (
    <>
      <Header />

      <section className="search-shell">
        <div className="eyebrow eyebrow-emerald">Search</div>
        <h1 className="display" style={{ fontSize: 'clamp(32px, 4vw, 44px)', margin: '12px 0 32px' }}>
          Search the journal
        </h1>

        <div className="search-input-wrap">
          <span className="search-input-icon">
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
          </span>
          <input ref={inputRef} className="search-input" type="search"
            placeholder="ISO 42001, EU AI Act, supplier risk…"
            value={q} onChange={(e) => setQ(e.target.value)} />
        </div>

        {!q && (
          <div className="search-empty">
            <div className="empty-icon">
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
            </div>
            <p style={{ fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 18 }}>
              Search by framework, topic, author, or any keyword.
            </p>
            <div className="search-suggestions">
              {suggestions.map((s) => (
                <button key={s} className="fw-tag" onClick={() => setQ(s)}>{s}</button>
              ))}
            </div>
          </div>
        )}

        {q && results.length === 0 && (
          <div className="search-empty">
            <p style={{ fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 18 }}>
              No pieces match "{q}" yet.
            </p>
          </div>
        )}

        {q && results.length > 0 && (
          <>
            <div style={{ fontSize: 13, color: 'var(--ink-4)', marginBottom: 8 }}>
              {results.length} {results.length === 1 ? 'result' : 'results'} for "{q}"
            </div>
            <div className="posts-grid">
              {results.map((p) => <PostCard key={p.id} post={p} />)}
            </div>
          </>
        )}
      </section>

      <Footer />
    </>
  );
}

// ============================================================
// 404 / Fallback
// ============================================================
function NotFound() {
  return (
    <>
      <Header />
      <div style={{ maxWidth: 720, margin: '0 auto', padding: '120px 24px', textAlign: 'center' }}>
        <div className="eyebrow eyebrow-emerald">404</div>
        <h1 className="display" style={{ fontSize: 'clamp(36px, 5vw, 56px)', margin: '14px 0 18px' }}>
          We couldn't find that piece.
        </h1>
        <p className="lede">It may have been moved or unpublished. The journal index is the safest place to start.</p>
        <a className="btn-demo" style={{ marginTop: 24, display: 'inline-block' }} href="#/">Back to the journal</a>
      </div>
      <Footer />
    </>
  );
}

Object.assign(window, { IndexPage, ArticlePage, CategoryPage, AuthorPage, SearchPage, NotFound });
