// App — main React shell with three views: Home (swarm), Index (grid), Detail.
const { useState, useEffect, useRef, useMemo } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "swarmCount": 8,
  "flapSpeed": 280,
  "background": "auto",
  "audioOn": false,
  "audioVolume": 0.4
}/*EDITMODE-END*/;

const BACKGROUNDS = {
  dawn: "linear-gradient(180deg, #f5d8b0 0%, #ecc090 30%, #d89868 60%, #a86048 100%)",
  noon: "linear-gradient(180deg, #fdf6e3 0%, #f0e4c2 40%, #d8c89c 100%)",
  dusk: "linear-gradient(180deg, #e8a890 0%, #c87858 35%, #6a3838 75%, #2a1820 100%)",
  olive: "linear-gradient(180deg, #d8c89c 0%, #a89868 50%, #5a5028 100%)"
};

function timeOfDayBackground() {
  return "noon";
}

const App = () => {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [view, setView] = useState("home"); // home | index | detail
  const [selected, setSelected] = useState(null);

  const pick = (species) => {
    setSelected(species);
    setView("detail");
    window.scrollTo(0, 0);
  };
  const goView = (v) => {
    setView(v);
    window.scrollTo(0, 0);
  };

  return (
    <div className="app" style={{ background: BACKGROUNDS[tweaks.background === "auto" ? timeOfDayBackground() : tweaks.background] || BACKGROUNDS.noon }}>
      {/* atmospheric texture */}
      <div className="grain" />
      <div className="vignette" />

      {/* Top nav */}
      <nav className="topnav">
        <div className="brand" onClick={() => goView("home")}>
          <span className="brand-mark">✦</span>
          <span className="brand-text">
            <span className="brand-title">Lepidoptera Cypria</span>
            <span className="brand-sub">Butterflies of Cyprus · A field journal</span>
          </span>
        </div>
        <div className="nav-links">
          <button className={`navlink ${view === "home" ? "active" : ""}`} onClick={() => goView("home")}>Swarm</button>
          <button className={`navlink ${view === "index" ? "active" : ""}`} onClick={() => goView("index")}>Index</button>
          <button className="navlink audio-toggle" onClick={() => setTweak("audioOn", !tweaks.audioOn)} title={tweaks.audioOn ? "Mute" : "Unmute"}>
            {tweaks.audioOn ? "♪ on" : "♪ off"}
          </button>
        </div>
      </nav>

      {view === "home" && <HomeView species={window.SPECIES} onPick={pick} tweaks={tweaks} />}
      {view === "index" && <IndexView species={window.SPECIES} onPick={pick} />}
      {view === "detail" && selected && <DetailView species={selected} onBack={() => goView("index")} onPick={pick} all={window.SPECIES} />}

      <AmbientAudio active={tweaks.audioOn} volume={tweaks.audioVolume} />

      <TweaksPanel>
        <TweakSection label="Scene" />
        <TweakRadio
          label="Background"
          value={tweaks.background}
          options={["auto", "dawn", "noon", "dusk", "olive"]}
          onChange={(v) => setTweak("background", v)}
        />
        <TweakSlider label="Swarm size" value={tweaks.swarmCount} min={6} max={48} step={1} onChange={(v) => setTweak("swarmCount", v)} />
        <TweakSlider label="Flap speed" value={tweaks.flapSpeed} min={120} max={500} step={10} unit="ms" onChange={(v) => setTweak("flapSpeed", v)} />
        <TweakSection label="Audio" />
        <TweakToggle label="Ambient sound" value={tweaks.audioOn} onChange={(v) => setTweak("audioOn", v)} />
        <TweakSlider label="Volume" value={Math.round(tweaks.audioVolume * 100)} min={0} max={100} step={5} unit="%" onChange={(v) => setTweak("audioVolume", v / 100)} />
      </TweaksPanel>
    </div>
  );
};

// HOME VIEW — swarm flying across gradient with title overlay
const HomeView = ({ species, onPick, tweaks }) => {
  return (
    <div className="view home">
      <window.Swarm count={tweaks.swarmCount} species={species} onPick={onPick} />
      <div className="hero">
        <div className="hero-eyebrow">A field journal of</div>
        <h1 className="hero-title">
          <span className="hero-line">Butterflies of</span>
          <span className="hero-line italic">Cyprus</span>
        </h1>
        <div className="hero-rule"></div>
        <p className="hero-lede">
          A complete field journal of the island's butterflies — drawn from pine ridges, coastal scrub, and the secret slopes of the Troodos. {species.length} species recorded on Cyprus, including the endemics that fly nowhere else on earth.
        </p>
        <div className="hero-cta">
          <button className="cta primary" onClick={() => onPick(species[1])}>Begin with the Paphos Blue</button>
          <button className="cta ghost" onClick={() => document.querySelector('.navlink:nth-child(2)').click()}>Open the Index</button>
        </div>
        <div className="home-foot">
          <span>Click any butterfly to follow it.</span>
          <span className="dot">·</span>
          <span>{species.length} species</span>
          <span className="dot">·</span>
          <span>{species.filter(s => s.endemic).length} endemic</span>
        </div>
      </div>
      <Newsletter />
    </div>
  );
};

// Newsletter — email collection card pinned to the bottom-right of home view
const Newsletter = () => {
  const [email, setEmail] = useState("");
  const [open, setOpen] = useState(false);
  const [done, setDone] = useState(false);
  const [error, setError] = useState("");
  const [busy, setBusy] = useState(false);
  const submit = async (e) => {
    e.preventDefault();
    if (!email.includes("@")) return;
    setBusy(true);
    setError("");
    try {
      const res = await fetch("/api/subscribe", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email }),
      });
      if (!res.ok) {
        const data = await res.json().catch(() => ({}));
        throw new Error(data.error || "Could not send. Try again.");
      }
      setDone(true);
    } catch (err) {
      setError(err.message || "Network error");
    } finally {
      setBusy(false);
    }
  };
  return (
    <div className={`newsletter ${open ? "open" : ""}`}>
      {!open && (
        <button className="news-fab" onClick={() => setOpen(true)}>
          <span className="nf-mark">✉</span>
          <span className="nf-text">Field notes by post</span>
        </button>
      )}
      {open && (
        <div className={`news-card ${done ? "is-done" : ""}`}>
          <button className="news-x" onClick={() => setOpen(false)} aria-label="Close">×</button>
          <div className="news-inner">
            {!done ? (
              <>
                <div className="news-stamp">
                  <span className="news-stamp-glyph">L.C.</span>
                  <span className="news-stamp-cents">Cyprus · 5¢</span>
                </div>
                <div className="news-eyebrow">Field Notes</div>
                <h3 className="news-title">A note from the meadow</h3>
                <p className="news-lede">
                  Quarterly dispatches on <em>flight seasons</em>, new sightings, and the work of the Cyprus Butterfly Study Group.
                </p>
                <form className="news-form" onSubmit={submit}>
                  <label className="news-field">
                    <span className="news-field-icon">✉</span>
                    <input
                      type="email"
                      required
                      placeholder="your.name@meadow.cy"
                      value={email}
                      onChange={(e) => setEmail(e.target.value)}
                    />
                  </label>
                  <button type="submit" disabled={busy}>
                    <span>{busy ? "Sending…" : "Send my address"}</span>
                    <span className="arrow">→</span>
                  </button>
                </form>
                {error && <div className="news-error">{error}</div>}
                <div className="news-fine">
                  <span className="lock">⚯</span>
                  <span>No spam · unsubscribe at any season</span>
                </div>
              </>
            ) : (
              <>
                <div className="news-stamp">
                  <span className="news-stamp-glyph">✓</span>
                  <span className="news-stamp-cents">Posted</span>
                </div>
                <div className="news-eyebrow">Welcome</div>
                <div className="news-success-mark">✓</div>
                <h3 className="news-title">You're on the list.</h3>
                <p className="news-lede">
                  Look out for the first dispatch at the start of next season. Until then — keep an eye on the <em>thyme</em>.
                </p>
                <div className="news-fine">
                  <span>—</span>
                  <span>{email}</span>
                </div>
              </>
            )}
          </div>
        </div>
      )}
    </div>
  );
};

// INDEX VIEW — editorial grid of species cards, each a slowly flapping butterfly
const IndexView = ({ species, onPick }) => {
  const [filter, setFilter] = useState("all");
  const filtered = species.filter((s) => {
    if (filter === "all") return true;
    if (filter === "endemic") return s.endemic;
    return s.family.toLowerCase().includes(filter);
  });
  return (
    <div className="view index">
      <header className="page-head">
        <div className="ph-eyebrow">{species.length} species · {species.filter(s => s.endemic).length} endemic</div>
        <h2 className="ph-title">The Index</h2>
        <p className="ph-lede">Every species cataloged in the journal. Click a plate to enter its profile.</p>
        <div className="ph-filters">
          {[
            ["all", `All ${species.length}`],
            ["endemic", "Endemic"],
            ["papilionidae", "Swallowtails"],
            ["lycaenidae", "Blues & Coppers"],
            ["nymphalidae", "Brushfoots"],
            ["pieridae", "Whites & Yellows"],
            ["hesperiidae", "Skippers"]
          ].map(([k, l]) => (
            <button key={k} className={`pill ${filter === k ? "on" : ""}`} onClick={() => setFilter(k)}>{l}</button>
          ))}
        </div>
      </header>
      <div className="grid">
        {filtered.map((s) => {
          const realIdx = species.findIndex((x) => x.id === s.id);
          return (
          <article key={s.id} className="card" onClick={() => onPick(s)}>
            <div className="card-plate">
              <div className="plate-mount">
                <window.Butterfly3D species={s} size={150} flap={420 + (realIdx % 5) * 40} pose="rest" tilt={8} />
              </div>
              <div className="plate-no">№ {String(realIdx + 1).padStart(2, "0")}</div>
            </div>
            <div className="card-meta">
              <h3 className="card-name">{s.common}</h3>
              <div className="card-sci">{s.scientific}</div>
              <div className="card-tags">
                <span className="tag">{s.family}</span>
                {s.endemic && <span className="tag tag-endemic">Endemic</span>}
                <span className="tag tag-muted">{s.wingspan}</span>
              </div>
            </div>
          </article>
          );
        })}
      </div>
    </div>
  );
};

// DETAIL VIEW
const DetailView = ({ species, onBack, onPick, all }) => {
  const others = all.filter(s => s.id !== species.id).slice(0, 4);
  const idx = all.findIndex(s => s.id === species.id);
  const next = all[(idx + 1) % all.length];
  const prev = all[(idx - 1 + all.length) % all.length];
  const [vw, setVw] = useState(typeof window !== "undefined" ? window.innerWidth : 1200);
  useEffect(() => {
    const onR = () => setVw(window.innerWidth);
    window.addEventListener("resize", onR);
    return () => window.removeEventListener("resize", onR);
  }, []);
  const heroSize = vw < 800 ? Math.max(110, Math.floor((vw - 80) / 2)) : 260;
  const bgSize = vw < 800 ? 0.35 : 1;

  return (
    <div className="view detail" key={species.id}>
      <button className="back" onClick={onBack}>← Index</button>

      <section className="detail-hero">
        <div className="detail-stage">
          {/* a few drifting copies in background */}
          <div className="bg-flyer" style={{ top: "15%", left: "10%", animationDuration: "14s" }}>
            <window.Butterfly3D species={species} size={Math.round(80 * bgSize)} flap={300} tilt={20} />
          </div>
          <div className="bg-flyer" style={{ top: "70%", left: "75%", animationDuration: "18s", animationDelay: "-4s" }}>
            <window.Butterfly3D species={species} size={Math.round(70 * bgSize)} flap={340} tilt={20} />
          </div>
          <div className="bg-flyer" style={{ top: "40%", left: "85%", animationDuration: "22s", animationDelay: "-9s" }}>
            <window.Butterfly3D species={species} size={Math.round(60 * bgSize)} flap={360} tilt={20} />
          </div>
          {/* hero butterfly, slowly rotating */}
          <div className="hero-butterfly">
            <window.Butterfly3D species={species} size={heroSize} flap={520} pose="rest" tilt={15} />
          </div>
        </div>

        <div className="detail-meta">
          <div className="dm-eyebrow">Plate № {String(idx + 1).padStart(2, "0")} · {species.family}</div>
          <h1 className="dm-title">{species.common}</h1>
          <div className="dm-sci">{species.scientific}</div>
          {species.endemic && <div className="endemic-badge">⚘ Endemic to Cyprus</div>}
          <p className="dm-blurb">{species.blurb}</p>
        </div>
      </section>

      <section className="datasheet">
        <div className="ds-row">
          <div className="ds-label">Wingspan</div>
          <div className="ds-value">{species.wingspan}</div>
        </div>
        <div className="ds-row">
          <div className="ds-label">Flight season</div>
          <div className="ds-value">{species.season}</div>
        </div>
        <div className="ds-row">
          <div className="ds-label">Habitat</div>
          <div className="ds-value">{species.habitat}</div>
        </div>
        <div className="ds-row">
          <div className="ds-label">Larval foodplant</div>
          <div className="ds-value">{species.foodplant}</div>
        </div>
        <div className="ds-row">
          <div className="ds-label">Conservation status</div>
          <div className="ds-value">{species.status}</div>
        </div>
        <div className="ds-row">
          <div className="ds-label">Family</div>
          <div className="ds-value">{species.family}</div>
        </div>
      </section>

      <section className="palette-strip">
        <div className="ps-label">Wing palette</div>
        <div className="ps-swatches">
          {Object.entries(species.palette).map(([k, v]) => (
            <div key={k} className="swatch">
              <div className="swatch-chip" style={{ background: v }} />
              <div className="swatch-meta">
                <div className="swatch-name">{k}</div>
                <div className="swatch-hex">{v.toUpperCase()}</div>
              </div>
            </div>
          ))}
        </div>
      </section>

      <nav className="detail-nav">
        <button className="dnav prev" onClick={() => onPick(prev)}>
          <span className="dn-label">← Previous</span>
          <span className="dn-name">{prev.common}</span>
        </button>
        <button className="dnav next" onClick={() => onPick(next)}>
          <span className="dn-label">Next →</span>
          <span className="dn-name">{next.common}</span>
        </button>
      </nav>

      <section className="more">
        <h3 className="more-title">Continue exploring</h3>
        <div className="more-grid">
          {others.map((s) => (
            <button key={s.id} className="more-card" onClick={() => onPick(s)}>
              <div className="mc-plate">
                <window.Butterfly3D species={s} size={90} flap={420} pose="rest" tilt={8} />
              </div>
              <div className="mc-name">{s.common}</div>
              <div className="mc-sci">{s.scientific}</div>
            </button>
          ))}
        </div>
      </section>
    </div>
  );
};

// Ambient audio — butterfly garden soundscape: pink-noise wind, distant cicadas,
// soft bird trills, and occasional fluttering wing-beats. WebAudio only, no files.
const AmbientAudio = ({ active, volume }) => {
  const ctxRef = useRef(null);
  const nodesRef = useRef(null);
  const timersRef = useRef([]);
  const loopsRef = useRef({ flutter: null, trill: null, running: false });
  useEffect(() => {
    if (!active) {
      if (nodesRef.current && ctxRef.current) {
        const ctx = ctxRef.current;
        try {
          nodesRef.current.master.gain.cancelScheduledValues(ctx.currentTime);
          nodesRef.current.master.gain.setTargetAtTime(0, ctx.currentTime, 0.3);
        } catch (e) {}
        timersRef.current.forEach(clearTimeout);
        timersRef.current = [];
        loopsRef.current.running = false;
        // suspend after fade to free CPU
        const suspendTimer = setTimeout(() => {
          if (ctx.state === "running") ctx.suspend().catch(() => {});
        }, 600);
        timersRef.current.push(suspendTimer);
      }
      return;
    }
    if (!ctxRef.current) {
      const Ctx = window.AudioContext || window.webkitAudioContext;
      const ctx = new Ctx();
      ctxRef.current = ctx;
      // === Pink noise (breeze through grass) ===
      const bufferSize = 2 * ctx.sampleRate;
      const noiseBuffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
      const output = noiseBuffer.getChannelData(0);
      let b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0;
      for (let i = 0; i < bufferSize; i++) {
        const white = Math.random() * 2 - 1;
        b0 = 0.99886*b0 + white*0.0555179;
        b1 = 0.99332*b1 + white*0.0750759;
        b2 = 0.96900*b2 + white*0.1538520;
        b3 = 0.86650*b3 + white*0.3104856;
        b4 = 0.55000*b4 + white*0.5329522;
        b5 = -0.7616*b5 - white*0.0168980;
        output[i] = (b0+b1+b2+b3+b4+b5+b6+white*0.5362) * 0.09;
        b6 = white * 0.115926;
      }
      const noise = ctx.createBufferSource();
      noise.buffer = noiseBuffer;
      noise.loop = true;
      const filter = ctx.createBiquadFilter();
      filter.type = "lowpass";
      filter.frequency.value = 700;
      const noiseGain = ctx.createGain();
      noiseGain.gain.value = 0.45;
      const master = ctx.createGain();
      master.gain.value = 0;
      noise.connect(filter).connect(noiseGain).connect(master).connect(ctx.destination);
      noise.start();
      // wind LFO
      const lfo = ctx.createOscillator();
      lfo.frequency.value = 0.13;
      const lfoGain = ctx.createGain();
      lfoGain.gain.value = 280;
      lfo.connect(lfoGain).connect(filter.frequency);
      lfo.start();

      // === Cicada drone (high shimmery hum) ===
      const cicada = ctx.createOscillator();
      cicada.type = "sawtooth";
      cicada.frequency.value = 4200;
      const cicadaTrem = ctx.createOscillator();
      cicadaTrem.frequency.value = 18; // fast tremolo
      const cicadaTremGain = ctx.createGain();
      cicadaTremGain.gain.value = 0.012;
      const cicadaGain = ctx.createGain();
      cicadaGain.gain.value = 0.004;
      const cicadaFilter = ctx.createBiquadFilter();
      cicadaFilter.type = "bandpass";
      cicadaFilter.frequency.value = 4200;
      cicadaFilter.Q.value = 8;
      cicadaTrem.connect(cicadaTremGain).connect(cicadaGain.gain);
      cicada.connect(cicadaFilter).connect(cicadaGain).connect(master);
      cicada.start();
      cicadaTrem.start();

      // === Wing-flutter — schedule soft, brief filtered noise bursts ===
      const flutter = () => {
        if (!ctxRef.current || !loopsRef.current.running) return;
        const t = ctx.currentTime;
        const fb = ctx.createBufferSource();
        fb.buffer = noiseBuffer;
        const ff = ctx.createBiquadFilter();
        ff.type = "bandpass";
        ff.frequency.value = 600 + Math.random() * 800;
        ff.Q.value = 3;
        const fg = ctx.createGain();
        fg.gain.setValueAtTime(0, t);
        fg.gain.linearRampToValueAtTime(0.08 + Math.random() * 0.05, t + 0.02);
        fg.gain.exponentialRampToValueAtTime(0.0001, t + 0.18 + Math.random() * 0.15);
        fb.connect(ff).connect(fg).connect(master);
        fb.start(t);
        fb.stop(t + 0.4);
        const next = 1500 + Math.random() * 4500;
        timersRef.current.push(setTimeout(flutter, next));
      };

      // === Bird trill — short pitched warble every now and then ===
      const trill = () => {
        if (!ctxRef.current || !loopsRef.current.running) return;
        const t = ctx.currentTime;
        const base = 1800 + Math.random() * 1400;
        for (let i = 0; i < 3 + Math.floor(Math.random() * 3); i++) {
          const osc = ctx.createOscillator();
          osc.type = "sine";
          const g = ctx.createGain();
          osc.frequency.value = base + i * (200 + Math.random() * 200);
          const ts = t + i * 0.08;
          g.gain.setValueAtTime(0, ts);
          g.gain.linearRampToValueAtTime(0.05, ts + 0.01);
          g.gain.exponentialRampToValueAtTime(0.0001, ts + 0.07);
          osc.connect(g).connect(master);
          osc.start(ts);
          osc.stop(ts + 0.1);
        }
        const next = 7000 + Math.random() * 12000;
        timersRef.current.push(setTimeout(trill, next));
      };

      loopsRef.current.flutter = flutter;
      loopsRef.current.trill = trill;
      nodesRef.current = { ctx, master, filter };
    }
    const { ctx, master } = nodesRef.current;
    if (ctx.state === "suspended") ctx.resume().catch(() => {});
    if (!loopsRef.current.running) {
      loopsRef.current.running = true;
      timersRef.current.push(setTimeout(loopsRef.current.flutter, 800));
      timersRef.current.push(setTimeout(loopsRef.current.trill, 3000));
    }
    master.gain.cancelScheduledValues(ctx.currentTime);
    master.gain.setTargetAtTime(volume, ctx.currentTime, 0.5);
  }, [active, volume]);
  useEffect(() => () => {
    timersRef.current.forEach(clearTimeout);
    timersRef.current = [];
    loopsRef.current.running = false;
    if (ctxRef.current) {
      try { ctxRef.current.close(); } catch (e) {}
      ctxRef.current = null;
      nodesRef.current = null;
    }
  }, []);
  return null;
};

// helper to listen for tweaks panel host messages
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
