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

// ---------- Chip definitions ----------
const CHIPS = [
  { id: "time", label: "time doesn't work", emoji: "🕘" },
  { id: "event", label: "don't like the event", emoji: "🤷" }
];

// ---------- Helpers ----------
const audienceLabel = (a) => ({
  family: "family",
  couple: "couple",
  "mom-solo": "mom-solo",
  flex: "flex"
}[a] || a);

// ---------- Analytics (PostHog) ----------
function track(event, props = {}) {
  if (typeof window !== "undefined" && window.posthog && typeof window.posthog.capture === "function") {
    window.posthog.capture(event, props);
  }
}

// ---------- ICS generation ----------
function pad(n) { return String(n).padStart(2, "0"); }

function formatIcsUtc(iso) {
  const d = new Date(iso);
  return d.getUTCFullYear() + pad(d.getUTCMonth() + 1) + pad(d.getUTCDate()) +
    "T" + pad(d.getUTCHours()) + pad(d.getUTCMinutes()) + pad(d.getUTCSeconds()) + "Z";
}

function escapeIcs(text) {
  return String(text || "")
    .replace(/\\/g, "\\\\")
    .replace(/;/g, "\\;")
    .replace(/,/g, "\\,")
    .replace(/\n/g, "\\n");
}

function buildIcs(events) {
  const stamp = formatIcsUtc(new Date().toISOString());
  const lines = [
    "BEGIN:VCALENDAR",
    "VERSION:2.0",
    "PRODID:-//weekend.//Local Event Planner//EN",
    "CALSCALE:GREGORIAN",
    "METHOD:PUBLISH"
  ];
  events.forEach((e) => {
    lines.push(
      "BEGIN:VEVENT",
      `UID:${e.id}@weekend.local`,
      `DTSTAMP:${stamp}`,
      `DTSTART:${formatIcsUtc(e.dtstart)}`,
      `DTEND:${formatIcsUtc(e.dtend)}`,
      `SUMMARY:${escapeIcs(e.title)}`,
      `LOCATION:${escapeIcs(`${e.venue}, ${e.city}`)}`,
      `DESCRIPTION:${escapeIcs(`${e.why}\n\nSource: ${e.source}`)}`,
      `URL:${e.source}`,
      "END:VEVENT"
    );
  });
  lines.push("END:VCALENDAR");
  return lines.join("\r\n");
}

function downloadIcs(events) {
  const ics = buildIcs(events);
  const blob = new Blob([ics], { type: "text/calendar;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `weekend-${new Date().toISOString().slice(0, 10)}.ics`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

// ---------- Image export (renders inline preview; user long-presses to save) ----------
// Pattern that works on iOS Safari: don't try to trigger a download — render the
// PNG inline as an <img>, instruct user to long-press to save (iOS-native).
// Provide an optional Share button (Web Share API) when available.
async function renderSummaryToBlob(selector) {
  const el = document.querySelector(selector);
  if (!el || typeof window.html2canvas !== "function") {
    return { ok: false, reason: "html2canvas missing or element not found" };
  }

  try {
    const canvas = await window.html2canvas(el, {
      backgroundColor: "#FFF8E7",
      scale: 2,
      useCORS: true,
      logging: false
    });

    const blob = await new Promise((resolve, reject) => {
      canvas.toBlob((b) => b ? resolve(b) : reject(new Error("canvas.toBlob returned null")), "image/png");
    });

    return { ok: true, blob };
  } catch (err) {
    console.error("Image render failed:", err);
    return { ok: false, reason: err && err.message ? err.message : String(err) };
  }
}

// ---------- Hero (image or stylized placeholder) ----------
function Hero({ event }) {
  const [imgFailed, setImgFailed] = useState(false);

  if (event.image_url && !imgFailed) {
    return (
      <div className="hero hero-image" style={{ "--hue": event.hue }}>
        <img
          className="hero-img"
          src={event.image_url}
          alt={event.title}
          loading="eager"
          crossOrigin="anonymous"
          onError={() => setImgFailed(true)}
        />
        <div className="hero-image-tint" />
        <div className="hero-meta">
          <span className="mono">img · {event.tags[0]}</span>
        </div>
      </div>
    );
  }

  if (event.img === "stripes") {
    return (
      <div className="hero hero-stripes" style={{ "--hue": event.hue }}>
        <div className="stripes" />
        <div className="hero-meta">
          <span className="mono">img · {event.tags[0]}</span>
        </div>
      </div>
    );
  }
  return (
    <div className="hero hero-photo" style={{ "--hue": event.hue }}>
      <div className="photo-wash" />
      <div className="photo-grain" />
      <div className="hero-meta">
        <span className="mono">photo · {event.title.split(" ").slice(0, 2).join(" ").toLowerCase()}</span>
      </div>
    </div>
  );
}

// ---------- Card ----------
function Card({ event, phase }) {
  return (
    <div className={`card phase-${phase}`}>
      <Hero event={event} />
      <div className="card-body">
        <div className="row chips-row">
          <span className={`pill pill-aud aud-${event.audience}`}>{audienceLabel(event.audience)}</span>
          <span className="pill pill-price">{event.price}</span>
          <span className="pill pill-drive">{event.drive} min</span>
        </div>

        <div className="title-row">
          <h2 className="card-title">{event.title}</h2>
          <div className="dt-block">
            <div className="dt-date">{event.date_display}</div>
            <div className="dt-time">{event.time_display}</div>
          </div>
        </div>

        <div className="row loc-row">
          <svg className="loc-pin" width="13" height="13" viewBox="0 0 13 13" fill="none">
            <path d="M6.5 12s-4-3.5-4-7a4 4 0 1 1 8 0c0 3.5-4 7-4 7z" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/>
            <circle cx="6.5" cy="5" r="1.4" stroke="currentColor" strokeWidth="1.4"/>
          </svg>
          <span className="loc-venue">{event.venue}</span>
          <span className="loc-sep">·</span>
          <span className="loc-city">{event.city}</span>
        </div>

        <p className="why">{event.why}</p>

        <a className="source" href={event.source} target="_blank" rel="noopener noreferrer">
          <span>view source</span>
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
            <path d="M3 3h6v6M9 3l-6 6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
          </svg>
        </a>
      </div>
    </div>
  );
}

// ---------- Chip Sheet (auto-advances after AUTO_ADVANCE_MS once a chip is picked) ----------
const AUTO_ADVANCE_MS = 1200;

function ChipSheet({ open, selected, onToggle, onCancel }) {
  const canAdvance = selected.length > 0;
  return (
    <div className={`sheet-wrap ${open ? "open" : ""}`} aria-hidden={!open}>
      <div className="sheet-scrim" onClick={onCancel} />
      <div className="sheet">
        <div className="sheet-handle" />
        <div className="sheet-head">
          <h3>Quick — why not?</h3>
          <p>Pick at least one so we can curate better next week.</p>
        </div>
        <div className="chip-list">
          {CHIPS.map((c) => {
            const on = selected.includes(c.id);
            return (
              <button
                key={c.id}
                className={`chip ${on ? "on" : ""}`}
                onClick={() => onToggle(c.id)}
              >
                <span className="chip-check">
                  {on ? (
                    <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                      <path d="M3 7.5L6 10.5L11 4.5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                  ) : null}
                </span>
                <span className="chip-emoji">{c.emoji}</span>
                <span className="chip-label">{c.label}</span>
              </button>
            );
          })}
        </div>
        <div className="sheet-foot single">
          <button className="btn btn-ghost btn-block" onClick={onCancel}>cancel</button>
        </div>
        {canAdvance && (
          <div className="sheet-progress" key={`p-${selected.length}-${selected.join(",")}`}>
            <div className="sheet-progress-fill" style={{ animationDuration: `${AUTO_ADVANCE_MS}ms` }} />
          </div>
        )}
      </div>
    </div>
  );
}

// ---------- Summary ----------
function Summary({ saved, weekLabel, onRestart, onIcs, onImage }) {
  const byDay = useMemo(() => {
    // Sort by dtstart first so days come out chronologically and
    // events within each day are time-ordered. Falls back to date_display
    // string compare if dtstart is missing for any reason.
    const sorted = [...saved].sort((a, b) => {
      const ta = a.dtstart ? new Date(a.dtstart).getTime() : 0;
      const tb = b.dtstart ? new Date(b.dtstart).getTime() : 0;
      return ta - tb;
    });
    const m = new Map();
    sorted.forEach((e) => {
      if (!m.has(e.date_display)) m.set(e.date_display, []);
      m.get(e.date_display).push(e);
    });
    return [...m.entries()];
  }, [saved]);

  // Fire summary_reached once when this view mounts
  useEffect(() => {
    track("summary_reached", { saved_count: saved.length });
  }, []);

  if (saved.length === 0) {
    return (
      <div className="summary empty">
        <div className="summary-head">
          <div className="mono">your week</div>
          <h1>No picks this week.</h1>
          <p>That's okay — we'll send a fresh deck next Sunday.</p>
        </div>
        <button className="btn btn-primary big" onClick={onRestart}>start over</button>
      </div>
    );
  }

  return (
    <div className="summary">
      <div className="summary-head">
        <div className="mono">{weekLabel}</div>
        <h1>{saved.length} {saved.length === 1 ? "plan" : "plans"} locked in.</h1>
        <p>Saved from your Sunday deck. Tap below to keep them.</p>
      </div>

      <div className="summary-list">
        {byDay.map(([day, evs]) => (
          <div key={day} className="day-block">
            <div className="day-label">{day}</div>
            {evs.map((e) => (
              <a
                key={e.id}
                className="day-event"
                href={e.source}
                target="_blank"
                rel="noopener noreferrer"
                onClick={() => track("summary_event_clicked", { card_id: e.id, card_title: e.title, audience: e.audience })}
              >
                <div className="dot" style={{ "--hue": e.hue }} />
                <div className="day-event-body">
                  <div className="day-event-title">{e.title}</div>
                  <div className="day-event-meta">
                    <span>{e.time_display}</span>
                    <span className="sep">·</span>
                    <span>{e.venue}, {e.city}</span>
                  </div>
                </div>
                <span className={`pill pill-aud aud-${e.audience} pill-sm`}>{audienceLabel(e.audience)}</span>
                <svg className="day-event-arrow" width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true">
                  <path d="M3 3h6v6M9 3l-6 6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
                </svg>
              </a>
            ))}
          </div>
        ))}
      </div>

      <div className="summary-actions">
        <button className="btn btn-primary big" onClick={onIcs}>
          <span className="btn-icon">📅</span>
          <span>Add to Calendar</span>
          <span className="btn-sub">.ics · all calendars</span>
        </button>
        <button className="btn btn-secondary big" onClick={onImage}>
          <span className="btn-icon">🖼</span>
          <span>Save as Image</span>
          <span className="btn-sub">PNG · share-ready</span>
        </button>
      </div>

      <button className="link-btn" onClick={onRestart}>↺ start the deck over</button>
    </div>
  );
}

// ---------- Toast ----------
function Toast({ msg }) {
  if (!msg) return null;
  return <div className="toast">{msg}</div>;
}

// ---------- Loading ----------
function Loading() {
  return (
    <div style={{ flex: 1, display: "grid", placeItems: "center", padding: 32 }}>
      <div className="mono" style={{ color: "var(--ink-3)" }}>loading…</div>
    </div>
  );
}

// ---------- App ----------
function App() {
  const [deck, setDeck] = useState(null);
  const [loadError, setLoadError] = useState(null);
  const [idx, setIdx] = useState(0);
  const [saved, setSaved] = useState([]);
  const [phase, setPhase] = useState("active");
  const [sheetOpen, setSheetOpen] = useState(false);
  const [selectedChips, setSelectedChips] = useState([]);
  const [view, setView] = useState("deck");
  const [toast, setToast] = useState("");
  const [previewUrl, setPreviewUrl] = useState(null);
  const [previewBlob, setPreviewBlob] = useState(null);

  const cardRef = useRef(null);
  const dragRef = useRef({ x0: 0, y0: 0, dx: 0, dy: 0, dragging: false, kind: null });

  // ---- Load deck.json on mount
  useEffect(() => {
    fetch("./deck.json", { cache: "no-store" })
      .then((r) => {
        if (!r.ok) throw new Error(`Deck fetch failed: ${r.status}`);
        return r.json();
      })
      .then((d) => {
        setDeck(d);
        track("link_opened", { week: d.week_label, event_count: d.events?.length || 0 });
      })
      .catch((err) => {
        console.error(err);
        setLoadError(err.message);
      });
  }, []);

  function flash(msg) {
    setToast(msg);
    setTimeout(() => setToast(""), 1600);
  }

  function advance(direction) {
    if (!deck) return;
    const total = deck.events.length;
    setPhase(direction === "right" ? "out-right" : "out-left");
    setTimeout(() => {
      const next = idx + 1;
      if (next >= total) {
        setView("summary");
        setPhase("active");
      } else {
        setIdx(next);
        setPhase("in");
        requestAnimationFrame(() => {
          requestAnimationFrame(() => setPhase("active"));
        });
      }
    }, 280);
  }

  function onYes() {
    const event = deck.events[idx];
    setSaved((s) => [...s, event]);
    track("card_swiped", {
      direction: "right",
      card_id: event.id,
      card_title: event.title,
      audience: event.audience,
      position: idx + 1
    });
    flash("saved ✓");
    advance("right");
  }

  function onNo() {
    setSheetOpen(true);
  }

  function toggleChip(id) {
    setSelectedChips((cs) => cs.includes(id) ? cs.filter((c) => c !== id) : [...cs, id]);
  }

  function confirmNo() {
    const event = deck.events[idx];
    track("card_swiped", {
      direction: "left",
      card_id: event.id,
      card_title: event.title,
      audience: event.audience,
      chips: selectedChips,
      position: idx + 1
    });
    setSheetOpen(false);
    setSelectedChips([]);
    advance("left");
  }

  function cancelNo() {
    setSheetOpen(false);
    setSelectedChips([]);
  }

  function restart() {
    setIdx(0);
    setSaved([]);
    setPhase("active");
    setSheetOpen(false);
    setSelectedChips([]);
    setView("deck");
  }

  function handleIcs() {
    if (saved.length === 0) return;
    downloadIcs(saved);
    track("summary_saved_ics", { saved_count: saved.length });
    flash("⬇ events.ics downloaded");
  }

  async function handleImage() {
    if (saved.length === 0) return;
    flash("📸 capturing…");
    const result = await renderSummaryToBlob(".summary");
    if (!result.ok) {
      flash("⚠ image render failed");
      console.error("render failed:", result.reason);
      return;
    }
    // Revoke any previous URL
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    const url = URL.createObjectURL(result.blob);
    setPreviewBlob(result.blob);
    setPreviewUrl(url);
    track("summary_saved_image", { saved_count: saved.length, method: "preview" });
  }

  function closePreview() {
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(null);
    setPreviewBlob(null);
  }

  async function sharePreview() {
    if (!previewBlob) return;
    const filename = `weekend-${new Date().toISOString().slice(0, 10)}.png`;
    const file = new File([previewBlob], filename, { type: "image/png" });
    if (typeof navigator !== "undefined" && navigator.canShare && navigator.canShare({ files: [file] })) {
      try {
        await navigator.share({ files: [file], title: "My weekend" });
        track("summary_saved_image", { saved_count: saved.length, method: "share" });
      } catch (err) {
        if (err && err.name !== "AbortError") {
          console.error("Share failed:", err);
          flash("⚠ share failed");
        }
      }
    } else if (previewUrl) {
      // No share API: try download as last resort
      const a = document.createElement("a");
      a.href = previewUrl;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    }
  }

  function onPointerDown(e) {
    if (sheetOpen || !deck) return;
    const p = e.touches ? e.touches[0] : e;
    dragRef.current = { x0: p.clientX, y0: p.clientY, dx: 0, dy: 0, dragging: true, kind: null };
  }

  function onPointerMove(e) {
    if (!dragRef.current.dragging || sheetOpen) return;
    const p = e.touches ? e.touches[0] : e;
    const dx = p.clientX - dragRef.current.x0;
    const dy = p.clientY - dragRef.current.y0;
    dragRef.current.dx = dx;
    dragRef.current.dy = dy;

    if (cardRef.current) {
      const opacity = Math.max(0.3, 1 - Math.abs(dx) / 360);
      cardRef.current.style.transform = `translate(${dx * 0.6}px, ${dy * 0.2}px)`;
      cardRef.current.style.opacity = opacity;

      const hintR = cardRef.current.querySelector(".hint-yes");
      const hintL = cardRef.current.querySelector(".hint-no");
      if (hintR) hintR.style.opacity = Math.max(0, Math.min(1, dx / 120));
      if (hintL) hintL.style.opacity = Math.max(0, Math.min(1, -dx / 120));
    }
  }

  function onPointerUp() {
    if (!dragRef.current.dragging || sheetOpen) return;
    const { dx } = dragRef.current;
    dragRef.current.dragging = false;

    if (cardRef.current) {
      cardRef.current.style.transform = "";
      cardRef.current.style.opacity = "";
      const hintR = cardRef.current.querySelector(".hint-yes");
      const hintL = cardRef.current.querySelector(".hint-no");
      if (hintR) hintR.style.opacity = 0;
      if (hintL) hintL.style.opacity = 0;
    }

    if (dx > 90) onYes();
    else if (dx < -90) onNo();
  }

  useEffect(() => {
    function key(e) {
      if (view !== "deck" || !deck) return;
      if (sheetOpen) {
        if (e.key === "Escape") cancelNo();
        return;
      }
      if (e.key === "ArrowRight") onYes();
      if (e.key === "ArrowLeft") onNo();
    }
    window.addEventListener("keydown", key);
    return () => window.removeEventListener("keydown", key);
  });

  // Auto-advance after the chip sheet has at least one chip selected.
  // Each toggle resets the timer (so multi-select works).
  useEffect(() => {
    if (!sheetOpen || selectedChips.length === 0) return;
    const t = setTimeout(() => { confirmNo(); }, AUTO_ADVANCE_MS);
    return () => clearTimeout(t);
  }, [sheetOpen, selectedChips]);

  // ---- Render: error
  if (loadError) {
    return (
      <div className="app">
        <div style={{ flex: 1, display: "grid", placeItems: "center", padding: 32, textAlign: "center" }}>
          <div>
            <div className="mono" style={{ color: "var(--ink-3)", marginBottom: 8 }}>error</div>
            <p>Couldn't load this week's deck.</p>
            <p className="mono" style={{ color: "var(--ink-3)", fontSize: 11, marginTop: 12 }}>{loadError}</p>
          </div>
        </div>
      </div>
    );
  }

  // ---- Render: loading
  if (!deck) {
    return (
      <div className="app">
        <Loading />
      </div>
    );
  }

  // ---- Render: deck or summary
  const events = deck.events;
  const total = events.length;
  const event = events[idx];
  const progress = (idx / total) * 100;

  return (
    <div className="app">
      <Toast msg={toast} />

      {view === "deck" && (
        <>
          <header className="topbar">
            <div className="brand">
              <div className="brand-mark">
                <span className="brand-sun" />
              </div>
              <div className="brand-name">
                <div className="brand-row1">weekend.</div>
                <div className="brand-row2 mono">{deck.city_label} · week of {deck.week_label}</div>
              </div>
            </div>
            <div className="counter mono">{Math.min(idx + 1, total)} / {total}</div>
          </header>

          <div className="progress">
            <div className="progress-fill" style={{ width: `${progress}%` }} />
          </div>

          <div
            className="stage"
            onMouseDown={onPointerDown}
            onMouseMove={onPointerMove}
            onMouseUp={onPointerUp}
            onMouseLeave={onPointerUp}
            onTouchStart={onPointerDown}
            onTouchMove={onPointerMove}
            onTouchEnd={onPointerUp}
          >
            {idx + 1 < total && (
              <div className="card card-peek" aria-hidden="true">
                <Hero event={events[idx + 1]} />
              </div>
            )}
            <div ref={cardRef} className="card-wrap">
              <Card event={event} phase={phase} />
              <div className="hint hint-yes">YES</div>
              <div className="hint hint-no">NOT THIS</div>
            </div>
          </div>

          <div className="actions">
            <button className="action action-no" onClick={onNo} aria-label="No">
              <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
                <path d="M5 5l12 12M17 5L5 17" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"/>
              </svg>
            </button>
            <div className="action-hint mono">
              <span>← skip</span>
              <span className="dot-sep">·</span>
              <span>save →</span>
            </div>
            <button className="action action-yes" onClick={onYes} aria-label="Yes">
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
                <path d="M12 21s-7-4.5-9.5-9C.7 8.5 3 4 7 4c2 0 3.5 1.2 5 3 1.5-1.8 3-3 5-3 4 0 6.3 4.5 4.5 8C19 16.5 12 21 12 21z" fill="currentColor"/>
              </svg>
            </button>
          </div>

          <ChipSheet
            open={sheetOpen}
            selected={selectedChips}
            onToggle={toggleChip}
            onCancel={cancelNo}
          />
        </>
      )}

      {view === "summary" && (
        <Summary
          saved={saved}
          weekLabel={deck.summary_week_label}
          onRestart={restart}
          onIcs={handleIcs}
          onImage={handleImage}
        />
      )}

      {previewUrl && (
        <div
          className="image-preview-overlay"
          onClick={(e) => { if (e.target === e.currentTarget) closePreview(); }}
        >
          <div className="image-preview-content">
            <p className="image-preview-hint mono">long-press the image to save 💛</p>
            <img src={previewUrl} alt="your weekend" />
            <div className="image-preview-actions">
              <button className="btn btn-primary" onClick={sharePreview}>
                <span>📤</span><span>share</span>
              </button>
              <button className="btn btn-ghost image-preview-close" onClick={closePreview}>close</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

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