/* eslint-disable */
// Browse All — content library page. Pulls every published EN page from
// Supabase pages table, renders a filterable card grid. Lane (L1) and topic
// (L2) filters sync to URL query string. Load-more on scroll.

const SUPABASE_URL = "https://avmysznionlkohylcslj.supabase.co";
const SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImF2bXlzem5pb25sa29oeWxjc2xqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ0MDU0NjAsImV4cCI6MjA4OTk4MTQ2MH0.V517W3B9IkzDn-PMzz1-96LCfyVYZ4Y8kxgd1War-fs";

const PAGE_SIZE = 60;

// Ordered list of lanes — matches the site's canonical lane structure.
const LANES = [
  { id: "all",          label: "All" },
  { id: "plan",         label: "Plan" },
  { id: "book",         label: "Book" },
  { id: "pack",         label: "Pack" },
  { id: "visas-docs",   label: "Visas & Docs" },
  { id: "on-the-ground", label: "On the Ground" },
  { id: "budget",       label: "Budget" },
];

// Pretty-print a topic slug ("trip-types" -> "Trip Types").
function titleCase(slug) {
  if (!slug) return "";
  return slug.replace(/-/g, " ").replace(/\b\w/g, (m) => m.toUpperCase());
}

// Pull a sensible card description from the row's content_fields.
function pullDescription(row) {
  const cf = row.content_fields || {};
  return (
    cf.intro ||
    cf.introduction ||
    cf.description ||
    cf.quick_answer ||
    ""
  );
}

// Read the URL query string for filters.
function readQuery() {
  if (typeof window === "undefined") return { lane: "all", sub: "" };
  const p = new URLSearchParams(window.location.search);
  return {
    lane: p.get("lane") || "all",
    sub: p.get("sub") || "",
  };
}

// Update the URL query string without reloading.
function writeQuery(lane, sub) {
  if (typeof window === "undefined") return;
  const p = new URLSearchParams();
  if (lane && lane !== "all") p.set("lane", lane);
  if (sub) p.set("sub", sub);
  const qs = p.toString();
  const next = qs ? `/en/browse/?${qs}` : "/en/browse/";
  window.history.replaceState({}, "", next);
}

// Fetch a paginated slice of pages from Supabase. Returns {rows, totalCount}.
async function fetchPagesPage({ lane, sub, offset, limit }) {
  const params = new URLSearchParams();
  params.set("property", "eq.travel");
  params.set("language", "eq.en");
  params.set("select", "slug,title,lane,topic,destination,content_fields,created_at");
  params.set("order", "created_at.desc");
  if (lane && lane !== "all") params.set("lane", `eq.${lane}`);
  if (sub) params.set("topic", `eq.${sub}`);

  const url = `${SUPABASE_URL}/rest/v1/pages?${params.toString()}`;
  const res = await fetch(url, {
    headers: {
      apikey: SUPABASE_ANON_KEY,
      Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
      Prefer: "count=exact",
      Range: `${offset}-${offset + limit - 1}`,
    },
  });
  if (!res.ok) throw new Error(`Supabase fetch failed: ${res.status}`);

  const cr = res.headers.get("content-range") || "";
  const m = cr.match(/\/(\d+|\*)$/);
  const totalCount = m && m[1] !== "*" ? parseInt(m[1], 10) : 0;

  const rows = await res.json();
  return { rows, totalCount };
}

// Fetch the distinct topic slugs for a given lane (used for sub-lane filter).
async function fetchTopicsForLane(lane) {
  if (!lane || lane === "all") return [];
  const params = new URLSearchParams();
  params.set("property", "eq.travel");
  params.set("language", "eq.en");
  params.set("lane", `eq.${lane}`);
  params.set("select", "topic");
  params.set("limit", "1000");
  const url = `${SUPABASE_URL}/rest/v1/pages?${params.toString()}`;
  const res = await fetch(url, {
    headers: {
      apikey: SUPABASE_ANON_KEY,
      Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
    },
  });
  if (!res.ok) return [];
  const rows = await res.json();
  const topics = new Set();
  for (const r of rows) if (r.topic) topics.add(r.topic);
  return Array.from(topics).sort();
}

// ──────────────────────────────────────────────────────────────────────────
// Card

const Card = ({ row }) => {
  const href = row.slug.startsWith("/") ? row.slug : `/${row.slug}`;
  const desc = pullDescription(row);
  const laneLabel = (LANES.find((l) => l.id === row.lane) || {}).label || titleCase(row.lane);
  const topicLabel = titleCase(row.topic);

  return (
    <a href={href} className="bw-card">
      <div className="bw-card-eyebrow">
        <span className="bw-card-lane">{laneLabel}</span>
        {topicLabel && <span className="bw-card-sep">·</span>}
        {topicLabel && <span className="bw-card-topic">{topicLabel}</span>}
      </div>
      <h3 className="bw-card-title">{row.title}</h3>
      {desc && <p className="bw-card-desc">{desc}</p>}
      <div className="bw-card-foot">
        <span className="bw-card-read">Read guide</span>
        <Icon name="arrow" size={14} />
      </div>
    </a>
  );
};

// ──────────────────────────────────────────────────────────────────────────
// Filter pills

const LaneFilter = ({ active, onPick }) => (
  <div className="bw-pills" role="tablist" aria-label="Filter by lane">
    {LANES.map((l) => (
      <button
        key={l.id}
        type="button"
        role="tab"
        aria-selected={active === l.id}
        className={`bw-pill ${active === l.id ? "is-active" : ""}`}
        onClick={() => onPick(l.id)}
      >
        {l.label}
      </button>
    ))}
  </div>
);

const TopicFilter = ({ topics, activeSub, onPick }) => {
  if (!topics || topics.length === 0) return null;
  return (
    <div className="bw-subs" role="tablist" aria-label="Filter by topic">
      <button
        type="button"
        className={`bw-sub ${!activeSub ? "is-active" : ""}`}
        onClick={() => onPick("")}
      >
        All topics
      </button>
      {topics.map((t) => (
        <button
          key={t}
          type="button"
          role="tab"
          aria-selected={activeSub === t}
          className={`bw-sub ${activeSub === t ? "is-active" : ""}`}
          onClick={() => onPick(t)}
        >
          {titleCase(t)}
        </button>
      ))}
    </div>
  );
};

// ──────────────────────────────────────────────────────────────────────────
// App

const BrowseApp = () => {
  const initial = readQuery();
  const [lane, setLane] = useState(initial.lane);
  const [sub, setSub] = useState(initial.sub);
  const [topics, setTopics] = useState([]);
  const [rows, setRows] = useState([]);
  const [total, setTotal] = useState(0);
  const [offset, setOffset] = useState(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [pageReady, setPageReady] = useState(false);
  const sentinelRef = useRef(null);

  // Reload data when filters change.
  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(null);
    setRows([]);
    setOffset(0);
    fetchPagesPage({ lane, sub, offset: 0, limit: PAGE_SIZE })
      .then(({ rows: r, totalCount }) => {
        if (cancelled) return;
        setRows(r);
        setTotal(totalCount);
        setOffset(r.length);
        setLoading(false);
        // Trigger fade-in once first batch lands.
        requestAnimationFrame(() => setPageReady(true));
      })
      .catch((e) => {
        if (cancelled) return;
        console.error("Browse fetch failed:", e);
        setError("We couldn't load the library just now. Refresh the page and try again.");
        setLoading(false);
        setPageReady(true);
      });
    writeQuery(lane, sub);
    return () => { cancelled = true; };
  }, [lane, sub]);

  // Reload topic chips when lane changes.
  useEffect(() => {
    let cancelled = false;
    if (lane === "all") {
      setTopics([]);
      return;
    }
    fetchTopicsForLane(lane).then((t) => { if (!cancelled) setTopics(t); });
    return () => { cancelled = true; };
  }, [lane]);

  // Load more handler.
  const loadMore = useCallback(() => {
    if (loading || rows.length >= total) return;
    setLoading(true);
    fetchPagesPage({ lane, sub, offset, limit: PAGE_SIZE })
      .then(({ rows: r }) => {
        setRows((prev) => prev.concat(r));
        setOffset((prev) => prev + r.length);
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, [lane, sub, offset, total, rows.length, loading]);

  // Infinite-scroll observer.
  useEffect(() => {
    if (!sentinelRef.current) return;
    const io = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) loadMore();
    }, { rootMargin: "600px 0px" });
    io.observe(sentinelRef.current);
    return () => io.disconnect();
  }, [loadMore]);

  const remaining = Math.max(total - rows.length, 0);
  const showLoadMore = !loading && rows.length > 0 && remaining > 0;

  return (
    <>
      <Nav />
      <main className={`browse-shell ${pageReady ? "is-ready" : ""}`}>
        <header className="browse-head">
          <div className="browse-eyebrow">THE LIBRARY · EVERY GUIDE · ONE PLACE</div>
          <h1 className="browse-title">
            Browse all <em>guides.</em>
          </h1>
          <p className="browse-lede">
            Every guide HowTo: Travel Edition has ever published, in one filterable index. Filter by lane to narrow to a single editorial desk; pick a topic to drill in.
          </p>
          <div className="browse-stats">
            <span><strong>{total.toLocaleString()}</strong> guides</span>
            <span className="dot">·</span>
            <span>Updated continuously</span>
          </div>
        </header>

        <section className="browse-filters" aria-label="Filters">
          <LaneFilter active={lane} onPick={(v) => { setSub(""); setLane(v); }} />
          <TopicFilter topics={topics} activeSub={sub} onPick={setSub} />
        </section>

        {error && <div className="browse-empty"><p>{error}</p></div>}

        {!error && !loading && rows.length === 0 && (
          <div className="browse-empty">
            <p>No guides match this filter yet.</p>
          </div>
        )}

        <section className="browse-grid" aria-live="polite">
          {rows.map((r) => <Card key={r.slug} row={r} />)}
        </section>

        {loading && rows.length === 0 && (
          <div className="browse-loading"><span>Loading the library…</span></div>
        )}

        {showLoadMore && (
          <div className="browse-more">
            <button type="button" className="browse-more-btn" onClick={loadMore}>
              Load more <span className="browse-more-rest">({remaining.toLocaleString()} more)</span>
            </button>
          </div>
        )}

        {loading && rows.length > 0 && (
          <div className="browse-loading"><span>Loading…</span></div>
        )}

        <div ref={sentinelRef} aria-hidden="true" style={{ height: 1 }} />
      </main>
      <Footer />
    </>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(<BrowseApp />);
