// ForGood — Redesign SPA
// Adapted from the design package's landing.jsx / jobs.jsx / job-detail.jsx / app.jsx
// Data comes from jobs.json (emitted by generate_redesign_site.py).

const { useState, useEffect, useMemo, useRef } = React;

const REMOTE_OPTS = ["Remote", "Hybrid", "On-site"];
const LEVEL_OPTS = ["Entry", "Mid", "Senior", "Director", "Executive"];
const PAGE_SIZE = 50;

const orgInitials = (org) =>
  (org || "??").split(/\s+/).map(w => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();

const BookmarkIcon = ({ filled }) => (
  <svg viewBox="0 0 16 16" fill="none">
    <path d="M3 2h10v12l-5-3-5 3V2z"
      fill={filled ? "currentColor" : "none"}
      stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/>
  </svg>
);

const useSavedJobs = () => {
  const [saved, setSaved] = useState(() => {
    try { return JSON.parse(localStorage.getItem("savedJobs") || "[]"); } catch { return []; }
  });
  const toggle = (id) => {
    setSaved(prev => {
      const next = prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id];
      localStorage.setItem("savedJobs", JSON.stringify(next));
      return next;
    });
  };
  return [saved, toggle];
};

const JobCard = ({ job, isSaved, onToggleSave }) => (
  <a className="job-card" href={job.page_url || "#"} style={{textDecoration: "none", color: "inherit"}}>
    <div className="org-mark" title={job.org}>{orgInitials(job.org)}</div>
    <div className="job-body">
      <div className="job-org">
        <span>{job.org || "Organization not specified"}</span>
      </div>
      <h3 className="job-title">{job.title || "Untitled role"}</h3>
      <div className="job-meta">
        {job.location && <><span>{job.location}</span><span className="dot">•</span></>}
        {job.level && <><span>{job.level}</span><span className="dot">•</span></>}
        {job.remote && <span>{job.remote}</span>}
      </div>
      <div className="job-tags">
        {job.cause && <span className="tag cause">{job.cause}</span>}
        {job.remote === "Remote" && <span className="tag remote">Remote</span>}
        {job.salaryRange && <span className="tag salary">{job.salaryRange}</span>}
      </div>
    </div>
    <div className="job-side">
      <button
        className={`bookmark ${isSaved ? "saved" : ""}`}
        onClick={(e) => { e.stopPropagation(); onToggleSave(job.id); }}
        aria-label={isSaved ? "Unsave job" : "Save job"}
        title={isSaved ? "Saved" : "Save"}
      >
        <BookmarkIcon filled={isSaved} />
      </button>
      {job.posted && <span className="posted">{job.posted} ago</span>}
    </div>
  </a>
);

const FilterCheckbox = ({ label, count, active, onClick }) => (
  <label className={`filter-option ${active ? "active" : ""}`} onClick={onClick}>
    <span className="checkbox" />
    <span>{label}</span>
    <span className="count">{count}</span>
  </label>
);

// ---- Landing ----
const Landing = ({ jobs, causes, navigate }) => {
  const [q, setQ] = useState("");
  const [cause, setCause] = useState("");
  const [loc, setLoc] = useState("");

  const submitSearch = (e) => {
    e?.preventDefault?.();
    const params = new URLSearchParams();
    if (q) params.set("q", q);
    if (cause) params.set("cause", cause);
    if (loc) params.set("loc", loc);
    navigate("jobs", params.toString());
  };

  const totalJobs = jobs.length;
  const causeCounts = useMemo(() => {
    const m = {};
    jobs.forEach(j => { if (j.cause) m[j.cause] = (m[j.cause]||0)+1; });
    return m;
  }, [jobs]);

  return (
    <div className="fade-up">
      <section className="hero">
        <div className="container">
          <div className="eyebrow" style={{marginBottom: 24}}>★ The job board for people who refuse to stay neutral</div>
          <h1 className="display">
            Work that <em>matters.</em><br/>
            Pay that <em>doesn't</em><br/>
            ask you to settle.
          </h1>

          <div className="hero-meta">
            <p className="hero-lede">
              {totalJobs.toLocaleString()}+ roles at climate orgs, civic tech outfits, public-health labs, and movement nonprofits — every one with salary disclosed and impact measured.
            </p>
            <div className="hero-actions">
              <button className="btn btn-primary btn-lg" onClick={() => navigate("jobs")}>Browse all roles →</button>
              <button className="btn btn-ghost btn-lg" onClick={() => navigate("jobs", "saved=1")}>Saved roles</button>
            </div>
          </div>

          <form className="hero-search" onSubmit={submitSearch}>
            <label className="field" htmlFor="search-q">
              <span>What</span>
              <input id="search-q" placeholder="role, skill, or keyword" value={q} onChange={e=>setQ(e.target.value)} />
            </label>
            <label className="field" htmlFor="search-cause">
              <span>Cause</span>
              <select id="search-cause" value={cause} onChange={e=>setCause(e.target.value)}>
                <option value="">Any cause area</option>
                {causes.map(c => <option key={c.name} value={c.name}>{c.name}</option>)}
              </select>
            </label>
            <label className="field" htmlFor="search-loc">
              <span>Where</span>
              <input id="search-loc" placeholder="city or remote" value={loc} onChange={e=>setLoc(e.target.value)} />
            </label>
            <button className="btn btn-accent submit" type="submit">Search →</button>
          </form>
        </div>
      </section>

      <div className="marquee-wrap" aria-hidden="true">
        <div className="marquee">
          <span>Climate</span><span>Democracy</span><span>Public Health</span><span>Education Equity</span><span>Housing Justice</span><span>Workers' Rights</span><span>Civic Tech</span><span>Food Systems</span>
          <span>Climate</span><span>Democracy</span><span>Public Health</span><span>Education Equity</span><span>Housing Justice</span><span>Workers' Rights</span><span>Civic Tech</span><span>Food Systems</span>
        </div>
      </div>

      <section className="section">
        <div className="container">
          <div className="section-head">
            <div>
              <div className="eyebrow" style={{marginBottom: 16}}>01 / Browse by cause</div>
              <h2>Pick the <em>fight</em> that fits.</h2>
            </div>
            <p>Every role is tagged by cause area. Filter by what you care about and skip the generic listings.</p>
          </div>

          <div className="causes">
            {causes.map(c => (
              <div key={c.name} className="cause-card" onClick={() => navigate("jobs", `cause=${encodeURIComponent(c.name)}`)}>
                <div>
                  <div className="cause-name">{c.name}</div>
                  <div style={{marginTop: 8, fontSize: 13, color: "var(--ink-soft)", maxWidth: "22ch"}}>{c.desc}</div>
                </div>
                <div className="cause-meta">
                  <span className="cause-count"><em>{causeCounts[c.name] || 0}</em> open</span>
                  <span className="cause-arrow">→</span>
                </div>
              </div>
            ))}
          </div>
        </div>
      </section>

      <section className="manifesto">
        <div className="container">
          <div className="section-head" style={{marginBottom: 48}}>
            <div>
              <div className="eyebrow" style={{color: "rgba(255,255,255,0.5)", marginBottom: 16}}>02 / Our standards</div>
              <h2 style={{color: "var(--cream)"}}>We <em>curate</em> the work.</h2>
            </div>
            <p style={{color: "rgba(255,255,255,0.7)"}}>Most job boards count listings. We count receipts.</p>
          </div>

          <div className="manifesto-grid">
            <h3>"Mission-driven work, surfaced from the orgs already doing it."</h3>
            <div>
              <p><strong>Salary on every listing when disclosed.</strong> Real ranges in real currencies — no "competitive" hand-waves.</p>
              <p><strong>Cause-tagged, not keyword-spammed.</strong> Each role is sorted by the issue it works on, so you can browse what you actually care about.</p>
              <p><strong>Fresh listings only.</strong> Roles roll off after their apply-by date or 90 days, whichever comes first.</p>
            </div>
          </div>

          <div className="stats" style={{marginTop: 80}}>
            <div className="stat">
              <div className="stat-num"><em>{Object.keys(causeCounts).length}</em></div>
              <div className="stat-label">Cause Areas</div>
            </div>
            <div className="stat">
              <div className="stat-num"><em>{totalJobs.toLocaleString()}</em></div>
              <div className="stat-label">Active Roles</div>
            </div>
            <div className="stat">
              <div className="stat-num"><em>Daily</em></div>
              <div className="stat-label">Refresh Cadence</div>
            </div>
            <div className="stat">
              <div className="stat-num"><em>90d</em></div>
              <div className="stat-label">Max Listing Age</div>
            </div>
          </div>
        </div>
      </section>

      <section className="section">
        <div className="container">
          <div style={{display: "grid", gridTemplateColumns: "1fr auto", gap: 32, alignItems: "end", paddingBottom: 32, borderBottom: "1px solid var(--ink)"}} className="cta-strip">
            <h2 className="display" style={{fontSize: "clamp(40px, 7vw, 96px)", margin: 0}}>
              Ready to <em>get to work?</em>
            </h2>
            <button className="btn btn-accent btn-lg" onClick={() => navigate("jobs")}>See all open roles →</button>
          </div>
        </div>
      </section>
    </div>
  );
};

// ---- Jobs Board ----
const JobsBoard = ({ jobs, causes, initialQuery, navigate }) => {
  const params = new URLSearchParams(initialQuery || "");
  const [search, setSearch] = useState(params.get("q") || "");
  const [activeCauses, setActiveCauses] = useState(params.get("cause") ? [params.get("cause")] : []);
  const [remotes, setRemotes] = useState(params.get("loc") ?
    (params.get("loc").toLowerCase().includes("remote") ? ["Remote"] : []) : []);
  const [levels, setLevels] = useState([]);
  const [minSalary, setMinSalary] = useState(0);
  const [showSaved, setShowSaved] = useState(params.get("saved") === "1");
  const [sort, setSort] = useState("newest");
  const [filtersOpen, setFiltersOpen] = useState(false);
  const [toast, setToast] = useState("");
  const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
  const [saved, toggleSaved] = useSavedJobs();
  const toastTimer = useRef();

  const showToast = (msg) => {
    setToast(msg);
    clearTimeout(toastTimer.current);
    toastTimer.current = setTimeout(() => setToast(""), 1800);
  };

  const handleToggleSave = (id) => {
    const wasSaved = saved.includes(id);
    toggleSaved(id);
    showToast(wasSaved ? "Removed from saved" : "Saved ✓");
  };

  const toggle = (arr, setter) => (val) => {
    setter(arr.includes(val) ? arr.filter(x => x !== val) : [...arr, val]);
  };

  const baseSet = useMemo(() => {
    return showSaved ? jobs.filter(j => saved.includes(j.id)) : jobs;
  }, [jobs, showSaved, saved]);

  const filtered = useMemo(() => {
    let out = baseSet.filter(j => {
      if (search) {
        const q = search.toLowerCase();
        const hay = `${j.title || ""} ${j.org || ""} ${j.cause || ""} ${(j.tags || []).join(" ")} ${j.location || ""}`.toLowerCase();
        if (!hay.includes(q)) return false;
      }
      if (activeCauses.length && !activeCauses.includes(j.cause)) return false;
      if (remotes.length && !remotes.includes(j.remote)) return false;
      if (levels.length && !levels.includes(j.level)) return false;
      if (minSalary && (j.salary || 0) < minSalary) return false;
      return true;
    });

    if (sort === "salary") out = [...out].sort((a,b) => (b.salary || 0) - (a.salary || 0));
    else if (sort === "salary-asc") out = [...out].sort((a,b) => (a.salary || 0) - (b.salary || 0));
    return out;
  }, [baseSet, search, activeCauses, remotes, levels, minSalary, sort]);

  // Reset paging when filters change
  useEffect(() => { setVisibleCount(PAGE_SIZE); }, [search, activeCauses, remotes, levels, minSalary, sort, showSaved]);

  const countFor = (dim, val) => {
    return baseSet.filter(j => {
      if (dim !== "cause" && activeCauses.length && !activeCauses.includes(j.cause)) return false;
      if (dim !== "remote" && remotes.length && !remotes.includes(j.remote)) return false;
      if (dim !== "level" && levels.length && !levels.includes(j.level)) return false;
      if (search) {
        const q = search.toLowerCase();
        const hay = `${j.title || ""} ${j.org || ""} ${j.cause || ""} ${(j.tags || []).join(" ")} ${j.location || ""}`.toLowerCase();
        if (!hay.includes(q)) return false;
      }
      if (minSalary && (j.salary || 0) < minSalary) return false;
      if (dim === "cause") return j.cause === val;
      if (dim === "remote") return j.remote === val;
      if (dim === "level") return j.level === val;
      return true;
    }).length;
  };

  const clearAll = () => {
    setActiveCauses([]); setRemotes([]); setLevels([]);
    setMinSalary(0); setSearch("");
  };

  const activeCount = activeCauses.length + remotes.length + levels.length + (minSalary ? 1 : 0) + (search ? 1 : 0);
  const visible = filtered.slice(0, visibleCount);

  return (
    <div className="fade-up">
      <section className="jobs-hero">
        <div className="container">
          <div className="eyebrow" style={{marginBottom: 16}}>★ {jobs.length.toLocaleString()} open roles · updated daily</div>
          <h1>Find a role <em>worth</em><br/>showing up for.</h1>
          <p className="lede">Search across climate, civic, health, education, and justice orgs. Salary is shown when disclosed.</p>
        </div>
      </section>

      <div className="container">
        <div className="jobs-toolbar">
          <div className="search-box">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" style={{flexShrink:0}}>
              <circle cx="7" cy="7" r="5" stroke="currentColor" strokeWidth="1.5"/>
              <path d="M11 11l3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
            </svg>
            <input
              placeholder="Search by title, org, skill, or location"
              value={search}
              onChange={e => setSearch(e.target.value)}
            />
            {search && <button className="clear" onClick={() => setSearch("")} aria-label="Clear search">×</button>}
          </div>

          <div className="list-tabs">
            <button className={!showSaved ? "active" : ""} onClick={() => setShowSaved(false)}>All</button>
            <button className={showSaved ? "active" : ""} onClick={() => setShowSaved(true)}>
              Saved {saved.length ? `· ${saved.length}` : ""}
            </button>
          </div>

          <div className="sort-wrap">
            <select value={sort} onChange={e => setSort(e.target.value)}>
              <option value="newest">Newest</option>
              <option value="salary">Highest salary</option>
              <option value="salary-asc">Lowest salary</option>
            </select>
          </div>

          <div className="toolbar-meta">
            <b>{filtered.length.toLocaleString()}</b> of {baseSet.length.toLocaleString()} roles
            {activeCount > 0 && <> · <button onClick={clearAll} style={{background:"none",border:0,padding:0,color:"var(--terracotta-deep)",fontFamily:"var(--mono)",fontSize:11,letterSpacing:"0.06em",textTransform:"uppercase",cursor:"pointer"}}>Clear filters</button></>}
          </div>
        </div>

        <div className="jobs-layout">
          <aside className="filters" style={{maxHeight: filtersOpen || window.innerWidth > 1024 ? "none" : "44px", overflow: "hidden"}}>
            <button className="filters-toggle" onClick={() => setFiltersOpen(o => !o)}>
              <span>Filters {activeCount ? `· ${activeCount}` : ""}</span>
              <span>{filtersOpen ? "−" : "+"}</span>
            </button>

            <div className="filter-group">
              <h4>
                Cause area
                {activeCauses.length > 0 && <button className="clear-btn" onClick={() => setActiveCauses([])}>Clear</button>}
              </h4>
              {causes.map(c => (
                <FilterCheckbox
                  key={c.name}
                  label={c.name}
                  count={countFor("cause", c.name)}
                  active={activeCauses.includes(c.name)}
                  onClick={() => toggle(activeCauses, setActiveCauses)(c.name)}
                />
              ))}
            </div>

            <div className="filter-group">
              <h4>Remote</h4>
              <div className="chip-row">
                {REMOTE_OPTS.map(r => (
                  <button key={r} className={`chip ${remotes.includes(r) ? "active" : ""}`} onClick={() => toggle(remotes, setRemotes)(r)}>
                    {r} <span style={{opacity:0.6, marginLeft:4, fontFamily:"var(--mono)", fontSize:11}}>{countFor("remote", r)}</span>
                  </button>
                ))}
              </div>
            </div>

            <div className="filter-group">
              <h4>Seniority</h4>
              <div className="chip-row">
                {LEVEL_OPTS.map(l => (
                  <button key={l} className={`chip ${levels.includes(l) ? "active" : ""}`} onClick={() => toggle(levels, setLevels)(l)}>
                    {l} <span style={{opacity:0.6, marginLeft:4, fontFamily:"var(--mono)", fontSize:11}}>{countFor("level", l)}</span>
                  </button>
                ))}
              </div>
            </div>

            <div className="filter-group">
              <h4>Min salary</h4>
              <div className="salary-row">
                <span>${(minSalary/1000).toFixed(0)}K</span>
                <input type="range" min="0" max="200000" step="5000" value={minSalary} onChange={e => setMinSalary(parseInt(e.target.value))} />
                <span>$200K</span>
              </div>
            </div>
          </aside>

          <div className="job-list">
            {filtered.length === 0 ? (
              <div className="empty">
                <h3>No matching roles {showSaved ? "saved yet" : ""}</h3>
                <p>{showSaved ? "Tap the bookmark on any job to save it for later." : "Try fewer filters or broaden your search."}</p>
                {!showSaved && activeCount > 0 && (
                  <button className="btn btn-ghost" style={{marginTop: 16}} onClick={clearAll}>Clear filters</button>
                )}
              </div>
            ) : (
              <>
                {visible.map(job => (
                  <JobCard
                    key={job.id}
                    job={job}
                    isSaved={saved.includes(job.id)}
                    onToggleSave={handleToggleSave}
                  />
                ))}
                {visibleCount < filtered.length && (
                  <div style={{padding: "32px 0", textAlign: "center"}}>
                    <button className="btn btn-ghost btn-lg" onClick={() => setVisibleCount(v => v + PAGE_SIZE)}>
                      Load {Math.min(PAGE_SIZE, filtered.length - visibleCount)} more
                    </button>
                    <div style={{marginTop: 12, fontFamily: "var(--mono)", fontSize: 11, color: "var(--ink-faint)", letterSpacing: "0.08em", textTransform: "uppercase"}}>
                      Showing {visibleCount.toLocaleString()} of {filtered.length.toLocaleString()}
                    </div>
                  </div>
                )}
              </>
            )}
          </div>
        </div>
      </div>

      <div className={`toast ${toast ? "show" : ""}`}>{toast}</div>
    </div>
  );
};

// Job detail lives in dedicated static HTML pages (see generate_redesign_site.py);
// the SPA only handles Landing + Jobs Board.

// ---- App shell ----
const App = () => {
  const [view, setView] = useState("home");
  const [query, setQuery] = useState("");
  const [jobs, setJobs] = useState(null);
  const [causes, setCauses] = useState([]);
  const [loadError, setLoadError] = useState(null);

  useEffect(() => {
    fetch("jobs.json")
      .then(r => r.ok ? r.json() : Promise.reject(new Error("Failed to load jobs.json: " + r.status)))
      .then(payload => {
        setJobs(payload.jobs || []);
        setCauses(payload.causes || []);
      })
      .catch(err => { console.error(err); setLoadError(err.message); });
  }, []);

  useEffect(() => {
    const sync = () => {
      const hash = window.location.hash.replace(/^#/, "");
      const [path, q] = hash.split("?");
      if (path === "jobs") setView("jobs");
      else setView("home");
      setQuery(q || "");
      window.scrollTo({ top: 0, behavior: "instant" });
    };
    sync();
    window.addEventListener("hashchange", sync);
    return () => window.removeEventListener("hashchange", sync);
  }, []);

  const navigate = (target, q = "") => {
    window.location.hash = q ? `${target}?${q}` : target;
  };

  const renderMain = () => {
    if (loadError) return (
      <div className="container" style={{padding: "120px 0", textAlign: "center"}}>
        <h1 className="display" style={{fontSize: 48}}>Couldn't <em>load roles.</em></h1>
        <p style={{marginTop: 16, color: "var(--ink-soft)"}}>{loadError}</p>
      </div>
    );
    if (jobs === null) return (
      <div className="container" style={{padding: "120px 0", textAlign: "center"}}>
        <p style={{fontFamily: "var(--mono)", fontSize: 12, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--ink-soft)"}}>Loading roles…</p>
      </div>
    );
    if (view === "home") return <Landing jobs={jobs} causes={causes} navigate={navigate} />;
    if (view === "jobs") return <JobsBoard jobs={jobs} causes={causes} initialQuery={query} navigate={navigate} />;
    return null;
  };

  return (
    <>
      <header className="header">
        <div className="container header-inner">
          <a className="brand" href="#home" onClick={(e)=>{e.preventDefault();navigate("home");}}>
            <span className="mark" />
            <span>For<em>Good</em></span>
          </a>
          <nav className="nav">
            <a className={view === "home" ? "active" : ""} href="#home" onClick={(e)=>{e.preventDefault();navigate("home");}}>Home</a>
            <a className={view === "jobs" ? "active" : ""} href="#jobs" onClick={(e)=>{e.preventDefault();navigate("jobs");}}>Jobs</a>
            <a href="#jobs" onClick={(e)=>{e.preventDefault();navigate("jobs", "cause=" + encodeURIComponent((causes[0] && causes[0].name) || ""));}}>Causes</a>
            <a href="#" onClick={e=>e.preventDefault()}>About</a>
          </nav>
          <div className="header-cta">
            <button className="btn btn-ghost" onClick={() => navigate("jobs", "saved=1")}>Saved</button>
            <button className="btn btn-primary" onClick={() => navigate("jobs")}>Find work →</button>
          </div>
        </div>
      </header>

      <main>{renderMain()}</main>

      <footer className="footer">
        <div className="container">
          <div className="footer-grid">
            <div>
              <div className="brand" style={{marginBottom: 16}}>
                <span className="mark" />
                <span>For<em>Good</em></span>
              </div>
              <p className="footer-tagline">The job board for people who refuse to <em>stay neutral.</em></p>
            </div>
            <div>
              <h4>For Job Seekers</h4>
              <ul>
                <li><a href="#jobs" onClick={e=>{e.preventDefault();navigate("jobs");}}>Browse jobs</a></li>
                <li><a href="#jobs" onClick={e=>{e.preventDefault();navigate("jobs","saved=1");}}>Saved roles</a></li>
              </ul>
            </div>
            <div>
              <h4>Browse</h4>
              <ul>
                {causes.slice(0, 4).map(c => (
                  <li key={c.name}><a href="#" onClick={e=>{e.preventDefault();navigate("jobs", `cause=${encodeURIComponent(c.name)}`);}}>{c.name}</a></li>
                ))}
              </ul>
            </div>
            <div>
              <h4>About</h4>
              <ul>
                <li><a href="https://forgoodjobs.org" target="_blank" rel="noopener">forgoodjobs.org</a></li>
              </ul>
            </div>
          </div>
          <div className="footer-bottom">
            <span>© {new Date().getFullYear()} ForGood</span>
            <span>Made with conviction</span>
          </div>
        </div>
      </footer>
    </>
  );
};

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