/* HTH26 Hi-Fi Sponsorships page
   Full inventory, filterable by category, with Givebutter widget mount.
*/

// Sponsorship inventory.
// Display names match Givebutter exactly so the modal handoff is consistent.
// Prices + quantities synced to Givebutter as of 2026-04-30 (committee
// workbook = source of truth).
// `includes` bullets sourced from HTH26_SponsorshipPacket.pdf where
// available; tiers without packet copy are TODO-flagged inline.
// Live counts are hydrated from /api/availability at render time so the
// remaining/total values here are seeded but not authoritative.
//
// Two slugs are now dormant (kept in DB for historical sponsor records,
// hidden from this page): `welcome` (merged into community) and
// `event-sign` (merged into signage).
//
// Hole-in-One: Givebutter has 4 spots; 1 is comped to Excite Motorsports
// (hole 13, off-platform recognition), so this page shows 3-of-3 to avoid
// over-selling.
const SPONSOR_INVENTORY = [
  {
    category: "Premium (Foursome Included)",
    key: "premium",
    blurb: "Top-tier visibility plus a full foursome. Pays for itself in brand impact and gets your team on the course.",
    items: [
      {
        name: "Meal Sponsor",
        price: "$5,000",
        givebutterSku: "meal",
        includes: [
          "Tied to the most social part of the day, with a branded item every golfer receives and keeps",
          "Dedicated meal station signage",
          "Verbal recognition during meal service",
          "Foursome of golfers included",
        ],
        remaining: 2, total: 2, featured: true,
      },
      {
        name: "Beverage Sponsor",
        price: "$3,500",
        givebutterSku: "beverage",
        includes: [
          "Full logo placement on the beverage cart",
          "Continuous visibility throughout the course, with branded items placed directly in players' hands all day",
          "On-cart signage",
          "Foursome of golfers included",
        ],
        remaining: 2, total: 2,
      },
      {
        name: "Community Area Sponsor",
        price: "$3,000",
        givebutterSku: "community",
        includes: [
          "Logo placement throughout the Community Connection Space",
          "Verbal recognition as \"inside the [Sponsor] Community Resources Tent\"",
          "Opportunity to distribute branded materials or staff a table",
          "Foursome of golfers included",
          "Strategic visibility with both golfers and the broader community",
        ],
        remaining: 1, total: 1,
      },
      {
        name: "Premium Hole + Foursome",
        price: "$1,800",
        givebutterSku: "premium-hole",
        includes: [
          "Custom logoed pin flag at your sponsored signature Ely Park hole, yours to keep",
          "Foursome of golfers included",
          "Double exposure through both flights of play",
        ],
        remaining: 18, total: 18,
      },
      {
        name: "Tee Box Sponsor + Foursome",
        price: "$650",
        givebutterSku: "teebox-foursome",
        includes: [
          "Tee box signage at your assigned hole",
          "Foursome of golfers included",
          "Double exposure across both flights",
        ],
        remaining: 5, total: 5,
      },
    ],
  },
  {
    category: "Contest Sponsorships",
    key: "contest",
    blurb: "Put your name on the day's most memorable moments — the longest drive, closest to the pin, and the practice green where the putting contest kicks off the day.",
    items: [
      // TODO: replace placeholder bullets with packet copy
      {
        name: "Hole In One Sponsor",
        price: "$1,000",
        givebutterSku: "hio",
        includes: ["Contest signage + naming rights", "Prize insurance included", "Winner spotlight at awards"],
        remaining: 3, total: 3,
      },
      {
        name: "Longest Drive Contest Sponsor",
        price: "$1,000",
        givebutterSku: "long-drive",
        includes: [
          "Exclusive signage at the long drive hole",
          "Verbal recognition during awards",
          "Sole brand presence at the long drive contest during the flight's event",
        ],
        remaining: 2, total: 2,
      },
      // TODO: replace placeholder bullets with packet copy
      {
        name: "Closest to Pin Sponsor",
        price: "$750",
        givebutterSku: "ctp",
        includes: ["Par-3 tee signage", "Winner recognition at awards"],
        remaining: 1, total: 1,
      },
      {
        name: "Putting Contest Sponsor",
        price: "$1,000",
        givebutterSku: "putting",
        includes: [
          "Exclusive signage at the putting contest station",
          "Verbal recognition during the contest",
          "Sole brand presence at the putting green during the flight's contest",
        ],
        remaining: 2, total: 2,
      },
      {
        name: "Practice Greens Sponsor",
        price: "$200",
        givebutterSku: "flag",
        includes: [
          "Custom logoed pin flag on the practice putting green",
          "Double exposure across both flights",
        ],
        remaining: 6, total: 6,
      },
    ],
  },
  {
    category: "Standard",
    key: "standard",
    blurb: "Affordable entry points for small businesses or individuals who want to put their name on a sign and support the cause.",
    items: [
      {
        name: "Tee Box Sponsor",
        price: "$250",
        givebutterSku: "teebox",
        includes: [
          "Tee box signage at your assigned hole",
          "Double exposure across both flights",
        ],
        remaining: 41, total: 41,
      },
      // TODO: replace placeholder bullets with packet copy
      {
        name: "Event Signage Sponsor",
        price: "$250",
        givebutterSku: "signage",
        includes: ["Logo on directional + registration signs", "Day-of recognition"],
        remaining: 24, total: 24,
      },
    ],
  },
  {
    category: "In-Kind",
    key: "inkind",
    blurb: "Donate raffle prizes, gift cards, or services. Any value welcome — every donation is recognized publicly.",
    items: [
      // TODO: replace placeholder bullets with packet copy
      {
        name: "Raffle Prize Donation",
        price: "Any value",
        givebutterSku: "raffle",
        includes: ["Public thanks during awards", "Recognition at raffle drawing", "Social media mention"],
        remaining: null, total: null, inkind: true,
      },
    ],
  },
];

/* ------------------------------------------------------------------ */
/* Helpers */
const availabilityBadge = (remaining, total) => {
  if (remaining == null) return null;
  if (remaining === 0) return null; // sold-out stamp handled separately
  const label = `${remaining} remaining`;
  if (total <= 1) return { cls: "last", label };
  const pct = remaining / total;
  if (pct <= 0.34) return { cls: "last", label };
  if (pct <= 0.67) return { cls: "few", label };
  return { cls: "lots", label };
};

/* ------------------------------------------------------------------ */
const PageHero = () => (
  <header className="page-hero">
    <div className="page-hero-photo" style={{ backgroundImage: "url(assets/ely-1.webp)" }} />
    <div className="page-hero-wash" />
    <div className="page-hero-inner">
      <div className="eyebrow on-navy">Sponsorship Packages · 2026</div>
      <h1>
        Sponsor a<br />
        <span className="gold">Hometown Hero</span>
      </h1>
      <p className="lead">
        Every package directly funds the Southern Tier Veterans Support Group —
        direct assistance, peer counseling, and programs for veterans across Broome County and beyond.
      </p>
      <div className="page-hero-meta">
        13 Packages · 4 Categories · $200 – $5,000
      </div>
    </div>
  </header>
);

const WhySponsor = () => (
  <section className="why-sponsor">
    <div className="why-sponsor-grid">
      <div className="why-card">
        <h3>Local Reach</h3>
        <p>~400 golfers, business owners, and community leaders see your brand over a full day at Ely Park — on signage, on carts, at the podium.</p>
      </div>
      <div className="why-card">
        <h3>100% Stays Local</h3>
        <p>Every net dollar funds the Southern Tier Veterans Support Group. Your sponsorship shows up in real assistance for real neighbors.</p>
      </div>
      <div className="why-card">
        <h3>Tax-Deductible</h3>
        <p>Portions of most packages are tax-deductible. Your receipt itemizes the deductible amount once goods/services are factored.</p>
      </div>
    </div>
  </section>
);

const FilterBar = ({ active, setActive, counts }) => {
  const chips = [
    { key: "all", label: "All packages" },
    ...SPONSOR_INVENTORY.map(c => ({ key: c.key, label: c.category.replace(" (Foursome Included)", "") })),
  ];
  return (
    <div className="filter-bar">
      <div className="filter-bar-inner">
        {chips.map(c => (
          <button
            key={c.key}
            className={`chip ${active === c.key ? "active" : ""}`}
            onClick={() => setActive(c.key)}
          >
            {c.label}
            <span className="count">{counts[c.key]}</span>
          </button>
        ))}
      </div>
    </div>
  );
};

const InventoryCard = ({ item, onSponsor }) => {
  const { sold, inkind, featured, remaining, total } = item;
  const avail = availabilityBadge(remaining, total);
  return (
    <div className={`inv-card ${featured ? "featured" : ""} ${sold ? "sold" : ""} ${inkind ? "inkind" : ""}`}>
      <div className="inv-header">
        <div style={{ flex: 1 }}>
          <div className="tier-pill">
            {inkind ? "In-Kind Donation" : featured ? "Flagship Sponsor" : "Sponsor Package"}
          </div>
        </div>
        {avail && !sold && <div className={`inv-availability ${avail.cls}`}>{avail.label}</div>}
      </div>
      <h3>{item.name}</h3>
      <div className="price">{item.price}</div>
      <ul>
        {item.includes.map((inc, i) => (
          <li key={i}>
            <span className="inv-check">✓</span>
            <span>{inc}</span>
          </li>
        ))}
      </ul>
      <div className="inv-btn-row">
        {sold ? (
          <button className="btn btn-ghost" disabled style={{ opacity: 0.5 }}>
            Sold out
          </button>
        ) : (
          <button
            className={`btn ${featured || inkind ? "btn-red" : "btn-navy"} btn-arrow`}
            onClick={() => onSponsor(item)}
          >
            <span>{inkind ? "Donate a Prize" : "Sponsor This"}</span>
          </button>
        )}
      </div>
    </div>
  );
};

const CategorySection = ({ category, idx, onSponsor }) => {
  const availableCount = category.items.filter(i => !i.sold).length;
  return (
    <section className="category" id={`cat-${category.key}`}>
      <div className="category-head">
        <div className="category-num">{String(idx + 1).padStart(2, "0")}</div>
        <h2 className="category-name">{category.category}</h2>
        <div className="category-meta">
          {availableCount} of {category.items.length} remaining
        </div>
      </div>
      <p className="body" style={{ maxWidth: 720, marginTop: -8, marginBottom: 24 }}>{category.blurb}</p>
      <div className="inv-grid">
        {category.items.map(item => (
          <InventoryCard key={item.name} item={item} onSponsor={onSponsor} />
        ))}
      </div>
    </section>
  );
};

const SponsorModal = ({ item, onClose }) => {
  const modalRef = React.useRef(null);
  const titleId = "sponsor-modal-title";

  // Focus management: move focus into the dialog on open, trap Tab inside,
  // and restore focus to the triggering element on close.
  React.useEffect(() => {
    if (!item) return;
    const previouslyFocused = document.activeElement;

    const raf = requestAnimationFrame(() => {
      const closeBtn = modalRef.current?.querySelector(".modal-close");
      closeBtn?.focus();
    });

    const handleKey = (e) => {
      if (e.key !== "Tab" || !modalRef.current) return;
      const focusables = modalRef.current.querySelectorAll(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      if (focusables.length === 0) return;
      const first = focusables[0];
      const last = focusables[focusables.length - 1];
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    };
    document.addEventListener("keydown", handleKey);

    return () => {
      cancelAnimationFrame(raf);
      document.removeEventListener("keydown", handleKey);
      if (previouslyFocused && typeof previouslyFocused.focus === "function") {
        previouslyFocused.focus();
      }
    };
  }, [item]);

  if (!item) return null;
  return (
    <div
      className={`modal-backdrop ${item ? "open" : ""}`}
      onClick={onClose}
      role="presentation"
    >
      <div
        className="modal"
        role="dialog"
        aria-modal="true"
        aria-labelledby={titleId}
        ref={modalRef}
        onClick={e => e.stopPropagation()}
      >
        <div className="modal-header">
          <div>
            <div className="tier-pill">{item.inkind ? "In-Kind Donation" : "Sponsor Package"}</div>
            <h3 id={titleId}>{item.name}</h3>
          </div>
          <button className="modal-close" onClick={onClose} aria-label="Close dialog">×</button>
        </div>
        <div className="modal-body">
          <div className="modal-price-line">
            <div className="label">Package amount</div>
            <div className="amount">{item.price}</div>
          </div>
          <div className="widget-mount" data-pkg={item.givebutterSku}>
            {/* Inline Givebutter donation form. The widget script in
                Sponsorships.html upgrades this element on first paint.
                Pending Givebutter support sorting out the campaign config
                so this renders successfully — when it does, no code
                change needed here. */}
            <givebutter-giving-form campaign="GHSGolfClassic"></givebutter-giving-form>
          </div>
          <p className="body-sm" style={{ marginTop: 20, textAlign: "center", color: "var(--ink-3)" }}>
            Secure checkout via Givebutter · Card or ACH · Receipt emailed immediately
          </p>
        </div>
      </div>
    </div>
  );
};

const HowItWorks = () => (
  <section className="how-it-works">
    <div className="section-inner">
      <div className="section-head">
        <div className="eyebrow">How It Works</div>
        <h2 className="display-lg">Three Steps,<br />Two Minutes</h2>
      </div>
      <div className="steps">
        <div className="step">
          <div className="step-num">1</div>
          <h3>Pick a Package</h3>
          <p>Tap "Sponsor This" on any tier above. The checkout opens with your package pre-selected — no forms to fill twice.</p>
        </div>
        <div className="step">
          <div className="step-num">2</div>
          <h3>Pay Securely</h3>
          <p>Card or ACH via Givebutter. Capture your logo, foursome names, and any day-of requests at checkout.</p>
        </div>
        <div className="step">
          <div className="step-num">3</div>
          <h3>Get Confirmation</h3>
          <p>Emailed receipt immediately. Committee follows up within 2 business days to lock in logistics.</p>
        </div>
      </div>
    </div>
  </section>
);

const ContactBand = () => (
  <section className="contact-band">
    <div className="section-inner">
      <h3>Questions or Custom Package?</h3>
      <p>
        Don't see the right fit? Want to combine packages or sponsor multiple years?
        The committee will work something out.
      </p>
      <div className="btn-row">
        <a href={`mailto:${SITE.contact}`} className="btn btn-navy btn-arrow">
          <span>Email the Committee</span>
        </a>
      </div>
    </div>
  </section>
);

/* ------------------------------------------------------------------ */
// Merge live availability from /api/availability into the static inventory.
// Falls back to the hard-coded `remaining` values if the API isn't reachable.
const mergeAvailability = (inventory, availability) => {
  if (!availability) return inventory;
  return inventory.map(cat => ({
    ...cat,
    items: cat.items.map(item => {
      const a = availability[item.givebutterSku];
      if (!a) return item;
      const remaining = a.remaining != null ? a.remaining : item.remaining;
      return {
        ...item,
        remaining,
        total: a.total != null ? a.total : item.total,
        sold: remaining === 0 ? true : item.sold,
      };
    }),
  }));
};

const App = () => {
  const [filter, setFilter] = React.useState("all");
  const [modalItem, setModalItem] = React.useState(null);
  const [availability, setAvailability] = React.useState(null);

  React.useEffect(() => {
    let cancelled = false;
    fetch("/api/availability")
      .then(r => r.ok ? r.json() : null)
      .then(data => { if (!cancelled && data) setAvailability(data); })
      .catch(() => { /* fall back to hard-coded values */ });
    return () => { cancelled = true; };
  }, []);

  const inventory = React.useMemo(
    () => mergeAvailability(SPONSOR_INVENTORY, availability),
    [availability]
  );

  const counts = {
    all: inventory.reduce((n, c) => n + c.items.length, 0),
    ...Object.fromEntries(inventory.map(c => [c.key, c.items.length])),
  };

  const visible = filter === "all"
    ? inventory
    : inventory.filter(c => c.key === filter);

  // ESC to close modal
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") setModalItem(null); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, []);

  return (
    <>
      <Topbar active="sponsors" />
      <main>
        <PageHero />
        <WhySponsor />
        <FilterBar active={filter} setActive={setFilter} counts={counts} />
        <section className="inventory">
          {visible.map((cat) => (
            <CategorySection
              key={cat.key}
              category={cat}
              idx={inventory.findIndex(c => c.key === cat.key)}
              onSponsor={setModalItem}
            />
          ))}
        </section>
        <HowItWorks />
        <ContactBand />
      </main>
      <Footer />
      <SponsorModal item={modalItem} onClose={() => setModalItem(null)} />
    </>
  );
};

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