/* 99 Frames — user-side pages */
const { useState, useEffect, useRef } = React;

/* -------------------------------------------------- HOME */
function Home({ go }) {
  return (
    <div className="home-wrap">
      <section className="card hero">
        <p className="byline byline-top"><span className="nines">99x</span>Frames presents</p>
        <img className="wordmark" src="assets/print-exchange.png" alt="Print Exchange" />
        <div className="hero-pair">
          <img className="frog" src="assets/frog.png" alt="" />
          <img className="duck" src="assets/duck.png" alt="" />
        </div>
        <div className="row" style={{ justifyContent: 'center', marginTop: 6 }}>
          <CloudButton color="green" size="big" onClick={() => go('submit')}>Submit a Photo</CloudButton>
        </div>
      </section>
    </div>);

}

/* -------------------------------------------------- ABOUT */
function About({ go }) {
  return (
    <div className="stack-lg">
      <section className="card">
        <h1>Hi! We're <span style={{ color: 'var(--blue)' }}>99x</span>Frames</h1>
        <p className="lead">Photography organization ran by and for the community</p>
        <p>Encouraging everyone to shoot and develop photos.</p>
        <div className="row" style={{ marginTop: 22 }}>
          <CloudButton color="green" onClick={() => go('submit')}>I'm in — Submit a Photo</CloudButton>
          <CloudButton color="white" onClick={() => go('event')}>Event details</CloudButton>
        </div>
      </section>
    </div>);

}

/* -------------------------------------------------- EVENT */
function Event({ go }) {
  return (
    <div className="stack-lg">
      <section className="card">
        <h1>Photo Print Swap</h1>
        <div className="event-grid">
          <div className="event-cell"><div className="k">Date</div><div className="v">Saturday, June 20, 2026</div></div>
          <div className="event-cell"><div className="k">Time</div><div className="v">5 &ndash; 9 pm</div></div>
          <div className="event-cell"><div className="k">Place</div><div className="v"><a className="map-link" href="https://www.google.com/maps/search/?api=1&query=2005+Eye+Street+Bakersfield+California" target="_blank" rel="noreferrer">House of Skye Studio</a></div></div>
          <div className="event-cell"><div className="k">City</div><div className="v">Bakersfield, CA</div></div>
          <div className="event-cell"><div className="k">Music</div><div className="v">DJ playin some jamz</div></div>
          <div className="event-cell"><div className="k">Bring</div><div className="v">Any prints you'd like to share or trade</div></div>
        </div>
        <div className="row" style={{ marginTop: 22 }}>
          <CloudButton color="green" size="big" onClick={() => go('submit')}>Submit Your Photo</CloudButton>
        </div>
      </section>

      <section className="card">
        <h2>How it works</h2>
        <div className="how-list">
          <p>Print your favorite photos at home or at any print shop.</p>
          <p>Don't have any prints?<br />Submit 5 photos and we will get you started.</p>
          <p>Bring your prints to the event<br />swap with another photographer</p>
          <p>Pin your print on the gallery board</p>
        </div>
      </section>

      <section className="card">
        <h2>Good to know</h2>
        <ul style={{ fontSize: '1.12rem', lineHeight: 1.7, paddingLeft: '1.1em' }}>
          <li>Submit a photo to RSVP</li>
          <li>All cameras welcome</li>
          <li>Invite your friends they don't need to be a photographer</li>
        </ul>
      </section>
    </div>);

}

/* -------------------------------------------------- SUBMIT */
const emailOk = (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
const uid = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 7);

/* ---- per-event submission budget (no sign-ups) ----------------------------
   Caps how many photos one device can send PER EVENT, so the admin isn't
   flooded — while still letting someone come back and add a few they forgot.
   The active event id comes from the admin's Event ID page (via the backend),
   so when the admin switches the active event, every device's quota resets
   automatically. Trade-off: with no accounts, clearing browser storage or
   using another device starts a fresh quota. */
const MAX_EXCHANGE_PER_DEVICE = 5; // exchange prints per device, per event
const MAX_GALLERY_PER_DEVICE = 1;  // gallery-board photos per device, per event
function budgetKey(eventId) { return '99frames_budget_' + (eventId || 'none'); }
function readBudget(eventId) {
  try {
    const b = JSON.parse(localStorage.getItem(budgetKey(eventId)) || '{}');
    return { exchange: b.exchange || 0, gallery: b.gallery || 0, submissionId: b.submissionId || '' };
  } catch (e) { return { exchange: 0, gallery: 0, submissionId: '' }; }
}
function writeBudget(eventId, b) { try { localStorage.setItem(budgetKey(eventId), JSON.stringify(b)); } catch (e) {} }

function MultiPhotoDrop({ items, onAdd, onRemove, error, max }) {
  const inputRef = useRef(null);
  const [drag, setDrag] = useState(false);
  const full = items.length >= max;

  const handleFiles = (files) => {
    const imgs = Array.prototype.slice.call(files || []).filter((f) => f.type.startsWith('image/'));
    if (imgs.length) onAdd(imgs);
  };

  return (
    <div className="field">
      <label>Your photos <span className="req">*</span></label>
      <div className="hint" style={{ marginTop: -2, marginBottom: 10 }}>
        Same or different — your choice! (up to {max})
      </div>
      <div className="photo-grid">
        {items.map((it) => (
          <div className="photo-thumb" key={it.id}>
            {it.url
              ? <img src={it.url} alt="upload preview" />
              : <div className="thumb-loading">…</div>}
            <button type="button" className="thumb-x" onClick={() => onRemove(it.id)} aria-label="Remove photo">×</button>
          </div>
        ))}
        {!full && (
          <div
            className={'dropzone mini' + (drag ? ' drag' : '') + (error ? ' err' : '')}
            onClick={() => inputRef.current.click()}
            onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
            onDragLeave={() => setDrag(false)}
            onDrop={(e) => { e.preventDefault(); setDrag(false); handleFiles(e.dataTransfer.files); }}>
            <div className="big-plus">＋</div>
            <div className="dz-sub">{items.length === 0 ? 'Add photos' : `Add more (${items.length}/${max})`}</div>
          </div>
        )}
      </div>
      {error && <div className="msg-err">{error}</div>}
      <input ref={inputRef} type="file" accept="image/*" multiple hidden
        onChange={(e) => { handleFiles(e.target.files); e.target.value = ''; }} />
    </div>);

}

/* Single-photo picker: the one shot the submitter wants on the gallery board. */
function GalleryPhotoDrop({ items, onAdd, onRemove }) {
  const inputRef = useRef(null);
  const [drag, setDrag] = useState(false);
  const full = items.length >= 1;

  const handleFiles = (files) => {
    const imgs = Array.prototype.slice.call(files || []).filter((f) => f.type.startsWith('image/'));
    if (imgs.length) onAdd(imgs);
  };

  return (
    <div className="field gallery-pick">
      <label>Photo for the gallery board</label>
      <div className="hint" style={{ marginTop: -2, marginBottom: 10 }}>
        Optional — pick the one photo you'd like featured on the gallery wall (1 max).
      </div>
      <div className="photo-grid">
        {items.map((it) => (
          <div className="photo-thumb" key={it.id}>
            {it.url
              ? <img src={it.url} alt="gallery pick preview" />
              : <div className="thumb-loading">…</div>}
            <button type="button" className="thumb-x" onClick={() => onRemove(it.id)} aria-label="Remove photo">×</button>
          </div>
        ))}
        {!full && (
          <div
            className={'dropzone mini gallery' + (drag ? ' drag' : '')}
            onClick={() => inputRef.current.click()}
            onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
            onDragLeave={() => setDrag(false)}
            onDrop={(e) => { e.preventDefault(); setDrag(false); handleFiles(e.dataTransfer.files); }}>
            <div className="big-plus">＋</div>
            <div className="dz-sub">Add 1 photo</div>
          </div>
        )}
      </div>
      <input ref={inputRef} type="file" accept="image/*" hidden
        onChange={(e) => { handleFiles(e.target.files); e.target.value = ''; }} />
    </div>);

}

function Submit({ go }) {
  const [event, setEvent] = useState(null);     // { id, title } once loaded
  const [eventLoaded, setEventLoaded] = useState(false);
  const [budget, setBudget] = useState({ exchange: 0, gallery: 0, submissionId: '' });

  useEffect(() => {
    let alive = true;
    Store.activeEvent()
      .then((ev) => { if (!alive) return; const e = ev || { id: '', title: '' }; setEvent(e); setBudget(readBudget(e.id)); })
      .catch(() => { if (!alive) return; setEvent({ id: '', title: '' }); setBudget(readBudget('')); })
      .then(() => { if (alive) setEventLoaded(true); });
    return () => { alive = false; };
  }, []);

  const eventId = event ? event.id : '';
  // Only enforce the per-device cap when we actually know the active event.
  // If we can't (no active event set, or the backend is unreachable), let people
  // submit rather than stranding them — the admin resets quotas by switching events.
  const capped = !!eventId;
  const remainingExchange = capped ? Math.max(0, MAX_EXCHANGE_PER_DEVICE - budget.exchange) : MAX_EXCHANGE_PER_DEVICE;
  const remainingGallery = capped ? Math.max(0, MAX_GALLERY_PER_DEVICE - budget.gallery) : MAX_GALLERY_PER_DEVICE;
  const noRoom = capped && remainingExchange <= 0 && remainingGallery <= 0;

  const [f, setF] = useState({ firstName: '', lastName: '', email: '', phone: '', instagram: '' });
  const [photos, setPhotos] = useState([]); // { id, file, url } — exchange prints
  const [galleryPhotos, setGalleryPhotos] = useState([]); // { id, file, url } — for the gallery board
  const [errs, setErrs] = useState({});
  const [busy, setBusy] = useState(false);
  const [done, setDone] = useState(false);
  const [sentCount, setSentCount] = useState(0);

  const set = (k) => (v) => { setF((s) => ({ ...s, [k]: v })); setErrs((e) => ({ ...e, [k]: undefined, contact: undefined })); };

  const makeAdder = (setList, max) => (incoming) => {
    setErrs((e) => ({ ...e, photo: undefined }));
    setList((cur) => {
      const room = Math.max(0, max - cur.length);
      if (room <= 0) return cur;
      const entries = incoming.slice(0, room).map((file) => ({ id: 'f_' + uid(), file, url: '' }));
      entries.forEach((entry) => {
        const r = new FileReader();
        r.onload = () => setList((p) => p.map((x) => (x.id === entry.id ? { ...x, url: r.result } : x)));
        r.readAsDataURL(entry.file);
      });
      return cur.concat(entries);
    });
  };
  const addPhotos = makeAdder(setPhotos, remainingExchange);
  const addGalleryPhotos = makeAdder(setGalleryPhotos, remainingGallery);
  const removePhoto = (id) => setPhotos((p) => p.filter((x) => x.id !== id));
  const removeGalleryPhoto = (id) => setGalleryPhotos((p) => p.filter((x) => x.id !== id));

  const submit = async () => {
    const e = {};
    if (!f.firstName.trim()) e.firstName = 'We need a first name';
    if (!f.lastName.trim()) e.lastName = 'And a last name';
    if (f.email.trim() && !emailOk(f.email)) e.email = 'Hmm, that email looks off';
    // Email, phone & Instagram are each optional — but at least one is required.
    if (!f.email.trim() && !f.phone.trim() && !f.instagram.trim()) e.contact = 'Please add contact info';
    if (photos.length === 0 && galleryPhotos.length === 0) e.photo = 'Add at least one photo to submit!';
    setErrs(e);
    if (Object.keys(e).length) return;
    setBusy(true);
    try {
      // Reuse this device's submission id for the event, so a top-up merges
      // into the SAME admin entry. Only the last photo triggers the email.
      const submissionId = budget.submissionId || ('sub_' + uid());
      const queue = photos.map((p) => ({ file: p.file, forGallery: false }))
        .concat(galleryPhotos.map((p) => ({ file: p.file, forGallery: true })));
      for (let i = 0; i < queue.length; i++) {
        await Store.submit({ ...f, file: queue[i].file, forGallery: queue[i].forGallery, submissionId, silent: i < queue.length - 1 });
      }
      const next = {
        exchange: budget.exchange + photos.length,
        gallery: budget.gallery + galleryPhotos.length,
        submissionId,
      };
      if (capped) { writeBudget(eventId, next); setBudget(next); }
      setSentCount(queue.length);
      setDone(true);
      window.scrollTo({ top: 0, behavior: 'smooth' });
    } catch (err) {
      setErrs({ photo: "Hmm, that didn't send. Try fewer or smaller photos, or check your connection." });
    } finally {
      setBusy(false);
    }
  };

  const resetForm = () => { setDone(false); setF({ firstName: '', lastName: '', email: '', phone: '', instagram: '' }); setPhotos([]); setGalleryPhotos([]); setErrs({}); };

  if (!eventLoaded) {
    return (
      <section className="card">
        <h1>Submit a Photo</h1>
        <FrogLoader label="Loading…" />
      </section>);

  }

  if (done) {
    const leftEx = capped ? Math.max(0, MAX_EXCHANGE_PER_DEVICE - budget.exchange) : MAX_EXCHANGE_PER_DEVICE;
    const leftGal = capped ? Math.max(0, MAX_GALLERY_PER_DEVICE - budget.gallery) : MAX_GALLERY_PER_DEVICE;
    const canMore = leftEx > 0 || leftGal > 0;
    return (
      <section className="card success">
        <div className="checkmark"></div>
        <h1>Yay, you're in!</h1>
        <p className="lead">Thanks, {f.firstName}! We got your {sentCount} photo{sentCount === 1 ? '' : 's'}.</p>
        <p className="muted">We give every photo a quick look before it goes up — it'll appear in the gallery soon. See you 6/20 at House of Skye Studio!</p>
        {capped && canMore
          ? <p className="muted">Forgot one? You can still add {leftEx > 0 ? (leftEx + ' more print' + (leftEx === 1 ? '' : 's')) : ''}{leftEx > 0 && leftGal > 0 ? ' and ' : ''}{leftGal > 0 ? 'a gallery photo' : ''} for this event.</p>
          : null}
        <div className="row" style={{ justifyContent: 'center', marginTop: 18 }}>
          <CloudButton color="blue" onClick={() => go('gallery')}>Visit the Gallery</CloudButton>
          {canMore ? <CloudButton color="white" onClick={resetForm}>Add more photos</CloudButton> : null}
        </div>
      </section>);

  }

  if (noRoom) {
    return (
      <section className="card success">
        <div className="checkmark"></div>
        <h1>You're all set for this event!</h1>
        <p className="lead">You've already sent your photos for this event from this device.</p>
        <p className="muted">Each device can send up to {MAX_EXCHANGE_PER_DEVICE} prints and {MAX_GALLERY_PER_DEVICE} gallery photo per event. There'll be more events soon — you can submit again then. If something looks off, reach out and we'll help.</p>
        <div className="row" style={{ justifyContent: 'center', marginTop: 18 }}>
          <CloudButton color="blue" onClick={() => go('gallery')}>Visit the Gallery</CloudButton>
          <CloudButton color="white" onClick={() => go('home')}>Back home</CloudButton>
        </div>
      </section>);

  }

  const alreadySent = capped && (budget.exchange > 0 || budget.gallery > 0);

  return (
    <section className="card">
      <h1>Submit a Photo</h1>
      {event && event.title ? <p className="muted" style={{ marginTop: -6 }}>For: <b>{event.title}</b></p> : null}
      {alreadySent
        ? <p className="muted" style={{ marginTop: event && event.title ? 0 : -6 }}>You've already sent {budget.exchange} print{budget.exchange === 1 ? '' : 's'}{budget.gallery ? ' and a gallery photo' : ''} for this event — you can add a few more below.</p>
        : null}
      <div className="two-col">
        <TextField label="First name" name="firstName" value={f.firstName} onChange={set('firstName')} error={errs.firstName} required placeholder="Beaula" />
        <TextField label="Last name" name="lastName" value={f.lastName} onChange={set('lastName')} error={errs.lastName} required placeholder="Bean" />
      </div>
      <ContactBlock f={f} set={set} error={errs.contact} emailError={errs.email} />
      {remainingExchange > 0
        ? <MultiPhotoDrop items={photos} onAdd={addPhotos} onRemove={removePhoto} error={errs.photo} max={remainingExchange} />
        : <div className="field"><label>Your photos</label><div className="hint" style={{ marginTop: -2 }}>You've added all {MAX_EXCHANGE_PER_DEVICE} prints for this event.</div></div>}
      {remainingGallery > 0
        ? <GalleryPhotoDrop items={galleryPhotos} onAdd={addGalleryPhotos} onRemove={removeGalleryPhoto} />
        : <div className="field gallery-pick"><label>Photo for the gallery board</label><div className="hint" style={{ marginTop: -2 }}>You've already chosen your gallery photo for this event.</div></div>}
      {remainingExchange <= 0 && errs.photo ? <div className="msg-err">{errs.photo}</div> : null}
      <div className="row" style={{ marginTop: 8 }}>
        {(() => {
          const needName = !f.firstName.trim() || !f.lastName.trim();
          const needContact = !f.email.trim() && !f.phone.trim() && !f.instagram.trim();
          const needPhoto = photos.length === 0 && galleryPhotos.length === 0;
          const prompt = busy ? null
            : needName ? 'Please add your name'
            : needContact ? 'Add phone, email, or @instagram'
            : needPhoto ? 'Add at least one photo'
            : null;
          return (
            <CloudButton color={prompt ? 'white' : 'green'} size="big" className={prompt ? 'btn-prompt' : ''} onClick={submit} disabled={busy}>
              {busy ? 'Sending…' : prompt || 'Submit my photos →'}
            </CloudButton>
          );
        })()}
        <CloudButton color="white" onClick={() => go('home')}>Cancel</CloudButton>
      </div>
    </section>);

}

/* -------------------------------------------------- HOST APPLICATION */
function Application({ go }) {
  const [f, setF] = useState({ firstName: '', lastName: '', email: '', phone: '', instagram: '', message: '' });
  const [errs, setErrs] = useState({});
  const [busy, setBusy] = useState(false);
  const [done, setDone] = useState(false);

  const set = (k) => (v) => { setF((s) => ({ ...s, [k]: v })); setErrs((e) => ({ ...e, [k]: undefined, contact: undefined })); };

  const submit = async () => {
    const e = {};
    if (!f.firstName.trim()) e.firstName = 'We need a first name';
    if (!f.lastName.trim()) e.lastName = 'And a last name';
    // Hosting requires an email so the organisers can reply.
    if (!f.email.trim()) e.email = 'Please add an email so we can reply';
    else if (!emailOk(f.email)) e.email = 'Hmm, that email looks off';
    if (!f.message.trim()) e.message = 'Tell us a little about your idea';
    setErrs(e);
    if (Object.keys(e).length) return;
    setBusy(true);
    try {
      await Store.applyHost({ ...f });
      setDone(true);
      window.scrollTo({ top: 0, behavior: 'smooth' });
    } catch (err) {
      setErrs({ message: "Hmm, that didn't send. Check your connection and try again." });
    } finally {
      setBusy(false);
    }
  };

  if (done) {
    return (
      <section className="card success">
        <div className="checkmark"></div>
        <h1>Thanks, {f.firstName}!</h1>
        <p className="lead">Your host application is in.</p>
        <p className="muted">We'll take a look and reach out by email. Thanks for helping grow the community!</p>
        <div className="row" style={{ justifyContent: 'center', marginTop: 18 }}>
          <CloudButton color="blue" onClick={() => go('home')}>Back home</CloudButton>
        </div>
      </section>);

  }

  return (
    <section className="card">
      <h1>Host an Event</h1>
      <p className="lead">Want to host the next <span style={{ color: 'var(--blue)' }}>99x</span>Frames event? Tell us about it.</p>
      <div className="two-col">
        <TextField label="First name" name="firstName" value={f.firstName} onChange={set('firstName')} error={errs.firstName} required placeholder="Beaula" />
        <TextField label="Last name" name="lastName" value={f.lastName} onChange={set('lastName')} error={errs.lastName} required placeholder="Bean" />
      </div>
      <ContactBlock f={f} set={set} error={errs.contact} requireEmail emailError={errs.email} />
      <TextArea label="Tell us about the event you'd like to host" name="message" value={f.message} onChange={set('message')} error={errs.message} required placeholder="Where, when, what kind of event, and anything else you have in mind…" />
      <div className="row" style={{ marginTop: 8 }}>
        {(() => {
          const prompt = busy ? null
            : (!f.firstName.trim() || !f.lastName.trim()) ? 'Please add your name'
            : !f.email.trim() ? 'Please add your email'
            : !f.message.trim() ? 'Tell us about your event'
            : null;
          return (
            <CloudButton color={prompt ? 'white' : 'green'} size="big" className={prompt ? 'btn-prompt' : ''} onClick={submit} disabled={busy}>
              {busy ? 'Sending…' : prompt || 'Send application →'}
            </CloudButton>
          );
        })()}
        <CloudButton color="white" onClick={() => go('home')}>Cancel</CloudButton>
      </div>
    </section>);

}

/* -------------------------------------------------- GALLERY */
function Gallery({ go }) {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let alive = true;
    const load = () => Store.publicPhotos().then((p) => { if (alive) { setItems(p); setLoading(false); } }).
    catch(() => { if (alive) setLoading(false); });
    load();
    const unsub = Store.subscribe(load);
    return () => { alive = false; unsub && unsub(); };
  }, []);

  return (
    <section className="card">
      <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-end' }}>
        <div style={{ flex: '1 1 200px' }}>
          <h1 style={{ margin: 0 }}>The Gallery</h1>
        </div>
      </div>
      {loading ?
      <FrogLoader label="Hopping to the gallery…" /> :
      <div className="empty-state">
          <img src="assets/frog.png" alt="" />
          <p className="muted">Submit a photo and after events it'll pop up right here</p>
          {items.length > 0 ?
          <div className="gallery-grid">
              {items.map((it) =>
            <figure className="polaroid" key={it.id}>
                  <img src={it.src} alt={'photo by ' + it.firstName} loading="lazy" />
                  <figcaption className="cap">
                    {it.firstName} {it.lastName}
                    {it.instagram ? <small>@{it.instagram}</small> : null}
                  </figcaption>
                </figure>
            )}
            </div> : null}
          <CloudButton color="blue" onClick={() => go('submit')}>Submit a Photo</CloudButton>
        </div>
      }
    </section>);

}

Object.assign(window, { Home, About, Event, Submit, Application, Gallery });
