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

// Stored under this key in localStorage; the app checks for it on load.
const TOKEN_KEY = "lattice_token";

// ===== Brand mark =====
function BrandMark() {
  return (
    <svg width="34" height="34" viewBox="0 0 34 34" aria-hidden="true">
      <g fill="#0a0a0a">
        <rect x="6" y="6" width="6" height="6" transform="rotate(45 9 9)" />
        <rect x="22" y="6" width="6" height="6" transform="rotate(45 25 9)" />
        <rect x="6" y="22" width="6" height="6" transform="rotate(45 9 25)" />
        <rect x="22" y="22" width="6" height="6" transform="rotate(45 25 25)" />
        <rect x="14" y="14" width="6" height="6" transform="rotate(45 17 17)" opacity="0.35" />
      </g>
    </svg>
  );
}

// ===== Nav =====
function Nav({ onSignIn, stats }) {
  return (
    <nav className="nav">
      <div className="brand">
        <BrandMark />
        <div className="brand-text">
          <div className="wordmark">LATTICE</div>
        </div>
      </div>
      <div className="nav-links">
        <a className="nav-link active" href="#">Home</a>
        <a className="nav-link" href="/app#graph">Graph Engine</a>
        <a className="nav-link" href="/app#articles">Feed</a>
        <a className="nav-link" href="/app#readthroughs">Portfolio</a>
      </div>
      <div className="nav-right">
        <span className="pill">Entities <b>{stats.entities.toLocaleString()}</b></span>
        <span className="pill">Relationships <b>{stats.relationships.toLocaleString()}</b></span>
        <span className="pill">Docs <b>{stats.documents.toLocaleString()}</b></span>
        <button className="btn btn-ghost" onClick={onSignIn}>Sign in</button>
        <a className="btn btn-solid" href="#access">Request access</a>
      </div>
    </nav>
  );
}

// ===== Hero =====
function Hero({ stats }) {
  return (
    <section className="hero container">
      <div className="hero-eyebrow">
        <span className="dot" /> Private beta
      </div>
      <h1>
        Real-time semiconductor<br />
        <em>intelligence.</em>
      </h1>
      <p className="hero-sub">
        Lattice agents surface the non-traditional signal — podcasts, forums,
        academic papers — and tie each finding back to the names you cover.
        We do the collection. You make the call.
      </p>
      <div className="hero-stats">
        <div className="stat">
          <div className="k">Entities</div>
          <div className="v">{stats.entities.toLocaleString()}</div>
          <div className="d">across the global semiconductor supply chain</div>
        </div>
        <div className="stat">
          <div className="k">Relationships</div>
          <div className="v">{stats.relationships.toLocaleString()}</div>
          <div className="d">supplier · customer edges, with citations</div>
        </div>
        <div className="stat">
          <div className="k">Documents</div>
          <div className="v">{stats.documents.toLocaleString()}</div>
          <div className="d">filings, transcripts, and trade press</div>
        </div>
        <div className="stat">
          <div className="k">Read-throughs</div>
          <div className="v">{stats.calls.toLocaleString()}</div>
          <div className="d">bullish · bearish · flat, sourced to the article</div>
        </div>
      </div>

      {/* How conviction is calculated — a short, plain explainer that
          sits under the live counts so first-time readers understand
          where the numbers come from. */}
      <div style={{
        marginTop: 36, padding: "28px 32px",
        border: "1px solid var(--rule)", borderRadius: 10,
        background: "var(--paper-2)",
      }}>
        <div style={{
          fontFamily: "var(--mono)", fontSize: 11, color: "var(--mute)",
          letterSpacing: "0.08em", textTransform: "uppercase",
          marginBottom: 12,
        }}>How conviction is calculated</div>
        <p style={{ fontSize: 14.5, lineHeight: 1.6, color: "var(--ink-2)", maxWidth: 820 }}>
          Every claim in the graph carries a citation. A read-through aggregates
          the underlying claims for one name, weighting each by the source's
          historical reliability, the recency of the claim, and how directly the
          edge connects the source document to the named entity. The conviction
          score (0–1) reflects how much agreement the underlying evidence shows;
          the direction (bullish · bearish · flat) reflects the net sign of the
          weighted edges. Click any number to see the contributing claims and
          jump straight to the source article.
        </p>
      </div>
    </section>
  );
}

// ===== Demo player =====
// Embeds the standalone Lattice product video at /static/lattice-video.html.
// The iframe handles its own play/pause/scrubber/animation — keeping it
// self-contained means we can iterate on the video without touching the
// React shell. Aspect ratio locked to 16:9.
function DemoPlayer() {
  return (
    <section className="demo container" id="demo">
      <div className="demo-head">
        <h2>Read-throughs from this morning's news.</h2>
        <div className="meta">// 1:40 — one DigiTimes scoop, six sourced read-throughs</div>
      </div>
      <div style={{
        position: "relative",
        width: "100%",
        aspectRatio: "16 / 9",
        borderRadius: "10px",
        overflow: "hidden",
        background: "#0a0a0a",
        boxShadow: "0 30px 60px -30px rgba(10,10,10,0.4)",
      }}>
        <iframe
          src="/static/lattice-video.html"
          title="Lattice product video"
          style={{
            position: "absolute",
            inset: 0,
            width: "100%",
            height: "100%",
            border: "none",
            background: "transparent",
          }}
          loading="lazy"
        />
      </div>
    </section>
  );
}

// ===== Mini visuals =====
function MiniGraph() {
  const pts = useMemo(() => Array.from({ length: 18 }, () => ({ x: 10 + Math.random() * 80, y: 10 + Math.random() * 80 })), []);
  const lines = useMemo(() => {
    const arr = [];
    for (let i = 0; i < 22; i++) {
      const a = Math.floor(Math.random() * pts.length);
      const b = Math.floor(Math.random() * pts.length);
      if (a !== b) arr.push([a, b]);
    }
    return arr;
  }, [pts]);
  return (
    <div className="mini-graph">
      <svg viewBox="0 0 100 100" preserveAspectRatio="none">
        {lines.map(([a, b], i) => (
          <line key={i} x1={pts[a].x} y1={pts[a].y} x2={pts[b].x} y2={pts[b].y} />
        ))}
      </svg>
      {pts.map((p, i) => (
        <div key={i} className={"n " + (i % 5 === 0 ? "b" : i % 3 === 0 ? "s" : "")} style={{ left: p.x + "%", top: p.y + "%" }} />
      ))}
    </div>
  );
}

function MiniArticles() {
  const rows = [
    { title: "Chip Industry Week In Review", score: "1.00" },
    { title: "Semiconductor Engineering · Trade Press", score: "1.00" },
    { title: "CoWoS Production Capacity 2026", score: "0.99" },
    { title: "NVIDIA Headcount Breakdown", score: "0.99" },
    { title: "TSMC ASE FOPLP Joint Hub", score: "0.98" },
  ];
  return (
    <div className="mini-articles">
      {rows.map((r, i) => (
        <div key={i} className="mini-article-row">
          <div className={"bar " + (i > 1 ? (i > 3 ? "b3" : "b2") : "")} />
          <div className="ttl" />
          <div className="num">{r.score}</div>
        </div>
      ))}
    </div>
  );
}

function MiniCalls() {
  const calls = [
    { name: "TSMC", tk: "TSM", dir: "LONG +16" },
    { name: "SK Hynix", tk: "000660.KS", dir: "LONG +10" },
    { name: "ASML", tk: "ASML", dir: "LONG +5" },
  ];
  return (
    <div className="mini-calls">
      {calls.map((c, i) => (
        <div key={i} className="mini-call">
          <div>
            <span className="nm">{c.name}</span>
            <span className="tk">{c.tk}</span>
          </div>
          <span className="badge">{c.dir}</span>
        </div>
      ))}
    </div>
  );
}

function ViewsPreview() {
  const items = [
    {
      num: "01", label: "Graph Engine", href: "/app#graph",
      desc: "View the plumbing behind the filtering engine. See how a single name connects to its suppliers, customers, and second-order exposures. Click any edge to the source document.",
      visual: <MiniGraph />,
    },
    {
      num: "02", label: "Feed", href: "/app#articles",
      desc: "Every filing, transcript, and trade-press article from the last 24 hours, ranked by how much it should change your view. Read the top ten in fifteen minutes.",
      visual: <MiniArticles />,
    },
    {
      num: "03", label: "Portfolio", href: "/app#readthroughs",
      desc: "For every name on your watchlist: the read-through from this week's filings, earnings, and trade press, with sourced edges and conviction.",
      visual: <MiniCalls />,
    },
  ];
  return (
    <section className="views container" id="views">
      <div className="views-head">
        <h2>Three filters:</h2>
      </div>
      <div className="view-grid">
        {items.map((it, i) => (
          <a key={i} className="view-card" href={it.href}>
            <div className="num">{it.num} / 03</div>
            <div className="visual">{it.visual}</div>
            <div className="label">{it.label}</div>
            <div className="desc">{it.desc}</div>
            <div className="arrow">open {it.label.toLowerCase()} →</div>
          </a>
        ))}
      </div>
    </section>
  );
}


// ===== Request-access form (POSTs to backend) =====
function RequestForm() {
  const [vals, setVals] = useState({ email: "", name: "", company: "", role: "", tracking: "" });
  const [errs, setErrs] = useState({});
  const [submitting, setSubmitting] = useState(false);
  const [done, setDone] = useState(false);
  const [serverErr, setServerErr] = useState("");
  const set = (k) => (e) => setVals(v => ({ ...v, [k]: e.target.value }));

  const submit = async (e) => {
    e.preventDefault();
    const err = {};
    // Same bypass passphrase as the sign-in card (see SignInCard.submit)
    const isBypass = (vals.email || "").toLowerCase().includes("ycombinator");
    if (!isBypass && !vals.email.match(/^[^@\s]+@[^@\s]+\.[^@\s]+$/)) err.email = "needs to look like an email";
    if (!vals.name.trim()) err.name = "required";
    if (!vals.company.trim()) err.company = "required";
    if (!vals.role) err.role = "pick one";
    setErrs(err);
    if (Object.keys(err).length) return;

    setSubmitting(true); setServerErr("");
    try {
      const r = await fetch("/api/auth/request", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(vals),
      });
      if (!r.ok) {
        const data = await r.json().catch(() => ({}));
        setServerErr(data.detail || "Request failed");
      } else {
        setDone(true);
      }
    } catch (e2) {
      setServerErr(String(e2.message || e2));
    } finally {
      setSubmitting(false);
    }
  };

  if (done) {
    return (
      <div className="form-success">
        <div className="check">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>
        </div>
        <div>
          <h3>Request received.</h3>
          <p>We review applications on Tuesdays and Fridays. You'll hear back within a week, with a sandbox link if approved.</p>
          <button className="btn btn-ghost" style={{ marginTop: 16 }} onClick={() => { setDone(false); setVals({ email: "", name: "", company: "", role: "", tracking: "" }); }}>Submit another</button>
        </div>
      </div>
    );
  }

  return (
    <form className="request" onSubmit={submit} noValidate>
      <div className="field-row">
        <div className={"field" + (errs.name ? " error" : "")}>
          <label>Name</label>
          <input value={vals.name} onChange={set("name")} placeholder="Ada Lovelace" />
          {errs.name && <span className="err">{errs.name}</span>}
        </div>
        <div className={"field" + (errs.email ? " error" : "")}>
          <label>Work email</label>
          <input value={vals.email} onChange={set("email")} placeholder="ada@firm.com" />
          {errs.email && <span className="err">{errs.email}</span>}
        </div>
      </div>
      <div className="field-row">
        <div className={"field" + (errs.company ? " error" : "")}>
          <label>Firm</label>
          <input value={vals.company} onChange={set("company")} placeholder="Babbage Capital" />
          {errs.company && <span className="err">{errs.company}</span>}
        </div>
        <div className={"field" + (errs.role ? " error" : "")}>
          <label>Role</label>
          <select value={vals.role} onChange={set("role")}>
            <option value="">Select…</option>
            <option>Buyside analyst</option>
            <option>Sellside analyst</option>
            <option>Portfolio manager</option>
            <option>Quant researcher</option>
            <option>Other</option>
          </select>
          {errs.role && <span className="err">{errs.role}</span>}
        </div>
      </div>
      <div className="field">
        <label>What names do you cover?</label>
        <textarea value={vals.tracking} onChange={set("tracking")} placeholder="e.g. NVDA, AMD, TSM, ASML, MU, MRVL, AVGO, plus the supply chain around HBM and CoWoS…" />
      </div>
      <div className="submit-row">
        <button className="btn btn-solid" type="submit" disabled={submitting}>
          {submitting ? "Submitting…" : "Request free account →"}
        </button>
      </div>
      {serverErr && <div className="err" style={{ fontFamily: "var(--mono)", fontSize: 12, color: "#b91c1c" }}>{serverErr}</div>}
    </form>
  );
}

// ===== Sign-in card (POSTs to backend, redirects on success) =====
function SignInCard() {
  const [email, setEmail] = useState("");
  const [err, setErr] = useState("");
  const [submitting, setSubmitting] = useState(false);

  const submit = async (e) => {
    e.preventDefault();
    // "ycombinator" is the demo bypass passphrase — the backend
    // accepts it without an allowlist check. Skip the email-format
    // gate so users don't have to type a fake @ in the box.
    const isBypass = email.toLowerCase().includes("ycombinator");
    if (!isBypass && !email.match(/^[^@\s]+@[^@\s]+\.[^@\s]+$/)) { setErr("needs to look like an email"); return; }
    setErr(""); setSubmitting(true);
    try {
      const r = await fetch("/api/auth/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email }),
      });
      const data = await r.json();
      if (!r.ok) {
        setErr(data.detail || "Login failed");
      } else {
        localStorage.setItem(TOKEN_KEY, data.token);
        window.location.href = "/app";
      }
    } catch (e2) {
      setErr(String(e2.message || e2));
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="signin-card">
      <h3>Sign in.</h3>
      <form onSubmit={submit}>
        <div className={"field" + (err ? " error" : "")}>
          <label>Email</label>
          <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="ada@firm.com" />
          {err && <span className="err">{err}</span>}
        </div>
        <button className="btn btn-solid" type="submit" disabled={submitting}>
          {submitting ? "Signing in…" : "Sign in →"}
        </button>
      </form>
    </div>
  );
}

function AccessSection() {
  return (
    <section className="access" id="access">
      <div className="container access-grid">
        <div>
          <div className="access-eyebrow">FREE ACCESS · INVITE-ONLY BETA</div>
          <RequestForm />
        </div>
        <SignInCard />
      </div>
    </section>
  );
}

// ===== Sign-in modal (from nav) =====
function SignInModal({ open, onClose }) {
  const [email, setEmail] = useState("");
  const [err, setErr] = useState("");
  const [submitting, setSubmitting] = useState(false);

  useEffect(() => {
    if (!open) { setEmail(""); setErr(""); }
    const onEsc = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onEsc);
    return () => window.removeEventListener("keydown", onEsc);
  }, [open, onClose]);

  const submit = async (e) => {
    e.preventDefault();
    // "ycombinator" is the demo bypass passphrase — the backend
    // accepts it without an allowlist check. Skip the email-format
    // gate so users don't have to type a fake @ in the box.
    const isBypass = email.toLowerCase().includes("ycombinator");
    if (!isBypass && !email.match(/^[^@\s]+@[^@\s]+\.[^@\s]+$/)) { setErr("needs to look like an email"); return; }
    setErr(""); setSubmitting(true);
    try {
      const r = await fetch("/api/auth/login", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email }),
      });
      const data = await r.json();
      if (!r.ok) {
        setErr(data.detail || "Login failed");
      } else {
        localStorage.setItem(TOKEN_KEY, data.token);
        window.location.href = "/app";
      }
    } catch (e2) {
      setErr(String(e2.message || e2));
    } finally {
      setSubmitting(false);
    }
  };

  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, background: "rgba(10,10,10,0.45)", backdropFilter: "blur(4px)",
      zIndex: 100, display: "grid", placeItems: "center", padding: 20,
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background: "var(--paper)", border: "1px solid var(--rule-2)", borderRadius: 12,
        padding: 36, width: "min(440px, 100%)", boxShadow: "0 30px 80px -20px rgba(0,0,0,0.4)",
      }}>
        <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--mute)", letterSpacing: ".06em" }}>// SIGN IN</div>
        <h3 style={{ fontFamily: "var(--serif)", fontSize: 30, fontWeight: 400, marginTop: 8 }}>Welcome back.</h3>
        <p style={{ color: "var(--mute)", fontSize: 14, marginTop: 6, lineHeight: 1.5 }}>
          Enter your email — approved addresses get instant access.
        </p>
        <form onSubmit={submit} style={{ marginTop: 24, display: "flex", flexDirection: "column", gap: 14 }}>
          <div className={"field" + (err ? " error" : "")}>
            <label>Email</label>
            <input value={email} onChange={e => setEmail(e.target.value)} placeholder="ada@firm.com" autoFocus />
            {err && <span className="err">{err}</span>}
          </div>
          <button className="btn btn-solid" type="submit" disabled={submitting}>
            {submitting ? "Signing in…" : "Sign in"}
          </button>
        </form>
        <div style={{ marginTop: 24, paddingTop: 20, borderTop: "1px solid var(--rule)", fontSize: 13, color: "var(--mute)" }}>
          No account yet? <a href="#access" onClick={onClose} style={{ color: "var(--ink)", textDecoration: "underline" }}>Request free access</a>.
        </div>
      </div>
    </div>
  );
}

// ===== Footer =====
function Footer() {
  return (
    <footer>
      <div className="container">
        <div className="foot-grid">
          <div className="foot-left">
            <BrandMark />
            <span className="foot-meta">LATTICE · Semiconductor Intelligence</span>
          </div>
          <div className="foot-links">
            <a href="/docs">API</a>
            <a href="#about">About</a>
            <a href="#access">Request access</a>
          </div>
        </div>
        <div className="foot-bottom">
          <span>© 2026 LATTICE INTEL, INC.</span>
          <span>v0.4.2 · all systems nominal</span>
        </div>
      </div>
    </footer>
  );
}

// ===== App =====
function App() {
  const [signOpen, setSignOpen] = useState(false);
  const [stats, setStats] = useState({ entities: 16000, relationships: 30000, documents: 9000, calls: 0 });

  // Pull live counts so the homepage hero numbers match the actual graph
  useEffect(() => {
    Promise.all([
      fetch("/api/stats").then(r => r.json()).catch(() => null),
      fetch("/api/readthroughs/targets?days=30&min_conviction=0.5&limit=200").then(r => r.json()).catch(() => null),
    ]).then(([s, t]) => {
      const next = {
        entities: s?.entities ?? 16000,
        relationships: s?.relationships ?? 30000,
        documents: s?.documents ?? 9000,
        calls: (t?.targets || []).reduce((acc, x) => acc + (x.n_total || 0), 0),
      };
      setStats(next);
    });
  }, []);

  // If already signed in, jump straight to /app
  useEffect(() => {
    if (localStorage.getItem(TOKEN_KEY)) {
      // Don't auto-redirect — just keep the option, in case they want to re-read the home page.
      // Could add a small banner here in the future.
    }
  }, []);

  return (
    <>
      <Nav onSignIn={() => setSignOpen(true)} stats={stats} />
      <Hero stats={stats} />
      <DemoPlayer />
      <ViewsPreview />
      <AccessSection />
      <Footer />
      <SignInModal open={signOpen} onClose={() => setSignOpen(false)} />
    </>
  );
}

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