/* IJWestern — the projects index. Step 5.
   Global: none. Renders into #root.

   Driven entirely by PROJECT_DATA, so there is no third place for
   project facts to drift: the cards read the same entries the project
   pages do, ordered by their own `number`. Every figure on this page,
   including the totals in the lead, is computed from that data rather
   than written down. */

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

/* Decorative only — which placeholder palette a photo-less project
   gets. Nothing here is a claim about the land. */
const ART = ["hills", "mist", "paddy", "earth", "dawn"];

const PROJECTS_IN_ORDER = Object.entries(PROJECT_DATA)
  .map(([slug, p]) => ({ slug, ...p }))
  .sort((a, b) => a.number.localeCompare(b.number));

const CHIPS = ["All", "Sales on-going", "Layout in-progress"];
const KIND_FOR = { "Sales on-going": "selling", "Layout in-progress": "progress" };

/* The site's own reveal system, minus app.jsx. Re-runs whenever the
   filter changes so a freshly mounted set of cards animates in rather
   than appearing fully formed. */
function useReveals(dep) {
  useEffect(() => {
    const SEL = ".reveal, .reveal-x, .reveal-fade";
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => {
        if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); }
      }),
      { threshold: 0.12, rootMargin: "0px 0px -6% 0px" }
    );
    document.querySelectorAll(SEL).forEach((el) => {
      if (!el.classList.contains("in")) io.observe(el);
    });
    return () => io.disconnect();
  }, [dep]);
}

function numberFrom(v) {
  const n = parseFloat(String(v).replace(/[^0-9.]/g, ""));
  return Number.isFinite(n) ? n : 0;
}
function figure(p, key) {
  return (p.figures || []).find((f) => f.k === key);
}

function ProjectCard({ p, i, art, wide = false }) {
  const shot = p.cardImage ? { src: p.cardImage } : (p.photos || [])[0];
  const approval = (p.documents || []).find((d) => d.nm === "Form 5 & 6");

  return (
    <a
      href={`${p.slug}.html`}
      className={`px-card reveal${wide ? " is-wide" : ""}`}
      style={{ "--reveal-delay": `${90 + i * 80}ms` }}
      aria-label={`${p.name}, ${p.locality}`}
    >
      <div className="px-media">
        {shot
          ? <img src={shot.src} alt="" loading="lazy" />
          : <LandPlaceholder theme={art} seed={i + 2} label="" />}
        {p.cardVideo && (typeof p.cardVideo === "string"
          ? <HoverVideo src={p.cardVideo} />
          : <HoverVideo sources={[
              { src: p.cardVideo.mp4, type: "video/mp4" },
              { src: p.cardVideo.webm, type: "video/webm" },
            ]} />)}
        <span className={`px-tag ${p.status.kind === "selling" ? "is-selling" : "is-progress"}`}>
          <span className="dot" />{p.status.label}
        </span>
        {!shot && <span className="px-nophoto">Photographs to follow</span>}
      </div>

      <div className="px-body">
        {/* Buy now on the title line (client, 3 Nov). Decorative: the
            whole card is the link, so card and button both open the
            project page. */}
        <div className="px-title-row">
          <h2 className="px-name"><ProjectName name={p.name} /></h2>
          <BuyNow className="buy-sm" />
        </div>
        <p className="px-place">{p.locality} · {p.district}</p>
        <p className="px-intro">{p.intro}</p>

        <div className="px-figs">
          {/* Approval is already the foot's own badge, so it is not
              repeated here; Classification shows its short form. */}
          {(p.figures || []).filter((f) => f.k !== "Approval").map((f) => (
            <div className="px-fig" key={f.k}>
              <div className="v">{f.k === "Classification" ? String(f.v).split(" / ")[0] : f.v}{f.u && <span className="unit">{f.u}</span>}</div>
              <div className="k">{f.k}</div>
            </div>
          ))}
        </div>

        <div className="px-foot">
          {approval && (
            <span className="px-approval">
              <span className="tick" aria-hidden="true">
                <svg viewBox="0 0 12 12" width="9" height="9" fill="none" stroke="currentColor"
                     strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M2 6.4l2.6 2.6L10 3.2" />
                </svg>
              </span>
              Form 5 &amp; 6 {approval.st.toLowerCase()}
            </span>
          )}
          {/* The "View project" words came out (client, 3 Nov); the
              icon alone says it. */}
          <span className="px-go" aria-hidden="true">
            <span className="arrow"><ArrowIcon /></span>
          </span>
        </div>
      </div>
    </a>
  );
}

function ProjectsIndex() {
  const [filter, setFilter] = useState("All");
  // Step 5. No single project to pre-set here, so the modal opens on
  // the dropdown's own first entry, exactly as the home page does.
  const [visit, setVisit] = useState(false);
  useReveals(filter);

  const counts = useMemo(() => ({
    All: PROJECTS_IN_ORDER.length,
    "Sales on-going": PROJECTS_IN_ORDER.filter((p) => p.status.kind === "selling").length,
    "Layout in-progress": PROJECTS_IN_ORDER.filter((p) => p.status.kind === "progress").length,
  }), []);

  const totals = useMemo(() => {
    const plots = PROJECTS_IN_ORDER.reduce((n, p) => n + numberFrom(figure(p, "Plots")?.v), 0);
    return { plots };
  }, []);

  const visible = filter === "All"
    ? PROJECTS_IN_ORDER
    : PROJECTS_IN_ORDER.filter((p) => p.status.kind === KIND_FOR[filter]);

  return (
    <>
      <TopBar onSchedule={() => setVisit(true)} />

      <main className="px-page" id="top">
        <div className="shell">
          <header className="px-head">
            <h1 className="t-display px-title reveal">
              {PROJECTS_IN_ORDER.length === 5 ? "Five" : PROJECTS_IN_ORDER.length} communities,
              <br />one Palakkad.
            </h1>
            <p className="px-lead reveal" style={{ "--reveal-delay": "160ms" }}>
              {totals.plots} plots in all. Every one on land we
              identified, surveyed and developed ourselves.
            </p>
          </header>

          <div className="filter-row px-filters reveal" style={{ "--reveal-delay": "260ms" }}>
            {CHIPS.map((k) => (
              <button
                key={k}
                className={`chip ${filter === k ? "active" : ""}`}
                onClick={() => setFilter(k)}
                aria-pressed={filter === k}
              >
                {k}<span className="count">{String(counts[k]).padStart(2, "0")}</span>
              </button>
            ))}
          </div>

          <div className="px-grid">
            {visible.map((p, i) => (
              <ProjectCard
                key={`${filter}-${p.slug}`}
                p={p}
                i={i}
                art={ART[(parseInt(p.number, 10) - 1) % ART.length]}
                /* An odd count leaves a hole in a two-up grid. The last
                   card takes the whole row instead, laid out sideways. */
                wide={visible.length % 2 === 1 && i === visible.length - 1}
              />
            ))}
          </div>
        </div>
      </main>

      <Footer onSchedule={() => setVisit(true)} />
      <VisitModal open={visit} onClose={() => setVisit(false)} />
    </>
  );
}

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