// app.jsx — Ken Kelly portfolio prototype
// One long page. Three visual directions.

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

// ───────────────────────────────────────────────────────────── content ──

const BIO = {
  name: "Ken Kelly",
  handle: "kkelly",
  title: "Principal Software Architect",
  location: "South Florida, US",
  yearsExp: 20,
  available: true,
  blurb:
    "I build production frontends and backends that don't fall over. React, TypeScript, Go — 20+ years of shipping software that real customers depend on.",
  links: {
    email: "ken@kkelly.dev",
    github: "https://github.com/kenhkelly",
    linkedin: "https://www.linkedin.com/in/kenhkelly/",
    resume: "/resume.pdf",
  },
};

const NOW = [
  { k: "building",  v: "Replatforming Total Wine's web checkout — React + Go services." },
  { k: "available", v: "Taking on 1–2 consulting engagements for Q3. Backend, frontend, or both." },
  { k: "learning",  v: "Deepening DevOps chops — Kubernetes, Terraform, and CI/CD pipeline design." },
  { k: "location",  v: "South Florida · ET (UTC−5/−4)" },
];

const EXPERIENCE = [
  {
    range: "2018 — present",
    company: "Total Wine & More",
    role: "Principal Software Architect",
    notes: "Architect the consumer web + native apps. Lead frontend platform, drive Go service migrations, mentor a team of 12.",
    stack: ["React", "React Native", "TypeScript", "Go", "GraphQL", "GCP", "Restate", "Redpanda"],
  },
  {
    range: "2014 — 2018",
    company: "Senior Engineer",
    role: "E-commerce & SaaS clients",
    notes: "Full-stack lead on commerce, fulfillment, and CMS rebuilds. Took several products from MVP through scale.",
    stack: ["Node.js", "React", "Postgres", "Redis", "Docker"],
  },
  {
    range: "2008 — 2014",
    company: "Independent / agency",
    role: "Frontend & full-stack",
    notes: "Client work across hospitality, retail, media. Built and ran a small agency for four of those years.",
    stack: ["PHP", "MySQL", "jQuery", "Backbone", "Rails"],
  },
  {
    range: "2005 — 2008",
    company: "First gigs",
    role: "Web developer",
    notes: "Got my hands dirty. Tables-for-layout, Flash, PSD-to-HTML, the works.",
    stack: ["HTML", "CSS", "JS", "PHP"],
  },
];

const PROJECTS = [
  {
    id: "tw-mobile",
    title: "Total Wine & More — Mobile App",
    year: "2019 — present",
    role: "Tech Lead",
    summary:
      "Native iOS & Android storefront. Catalog browse, reorder, in-store pickup, wallet. 4.9★ on iOS with millions of downloads.",
    stack: ["React Native", "Swift", "Kotlin", "GraphQL", "Go"],
    metrics: [
      { v: "4.9★", l: "App Store rating" },
      { v: "8M+",  l: "downloads" },
      { v: "60Hz", l: "scroll on a 5-year-old phone" },
    ],
    href: "https://apps.apple.com/us/app/total-wine-more/id1277318070",
  },
  {
    id: "tw-web",
    title: "Total Wine & More — Web",
    year: "2018 — present",
    role: "Principal Engineer",
    summary:
      "Top-50 US e-commerce site by traffic. Lead frontend architecture, performance, A/B platform, design-system rollout.",
    stack: ["React", "TypeScript", "Next.js", "Go", "GraphQL", "Akamai"],
    metrics: [
      { v: "92",   l: "Lighthouse perf" },
      { v: "<1.2s", l: "median LCP" },
      { v: "1.5B", l: "annual GMV" },
    ],
    href: "https://www.totalwine.com",
  },
];

const SKILLS = {
  Languages:  ["TypeScript", "JavaScript", "Go", "Swift", "Kotlin", "SQL"],
  Frontend:   ["React", "React Native", "Next.js", "Vite", "CSS Architecture"],
  Backend:    ["Go", "Node.js", "GraphQL", "REST", "Postgres", "Redis", "Restate", "Redpanda"],
  Platform:   ["AWS", "GCP", "Docker", "Kubernetes", "Terraform", "CI/CD"],
  "Practice": ["System design", "Mentoring", "Tech strategy", "Code review"],
};

// ───────────────────────────────────────────────────────────── tweaks ──

const TWEAK_DEFAULTS = {
  direction: "terminal",
  accent:    "#7CFFB2",
  density:   "cozy",
  font:      "jetbrains",
};

const FONT_STACKS = {
  jetbrains: '"JetBrains Mono", ui-monospace, "Cascadia Code", Menlo, monospace',
  plex:      '"IBM Plex Mono", ui-monospace, Menlo, monospace',
  geist:     '"Geist Mono", "JetBrains Mono", ui-monospace, monospace',
};

const DIRECTIONS = {
  terminal: {
    label: "Terminal",
    bg: "#06100B",
    bgAlt: "#0A1812",
    surface: "#0E1A14",
    border: "rgba(124,255,178,0.14)",
    text: "#C8E6D3",
    muted: "#5E7E6A",
    accent: "#7CFFB2",
    scanlines: true,
    promptChar: "$",
  },
  console: {
    label: "Console",
    bg: "#0B0E14",
    bgAlt: "#10141C",
    surface: "#141923",
    border: "rgba(127,215,255,0.12)",
    text: "#D8E0EE",
    muted: "#6E7A91",
    accent: "#7FD7FF",
    scanlines: false,
    promptChar: "›",
  },
  brutalist: {
    label: "Brutalist",
    bg: "#000000",
    bgAlt: "#0A0A0A",
    surface: "#0F0F0F",
    border: "rgba(255,180,84,0.18)",
    text: "#F2F2F2",
    muted: "#7A7A7A",
    accent: "#FFB454",
    scanlines: false,
    promptChar: "▌",
  },
};

// ───────────────────────────────────────────────────────────── helpers ──

function useTime() {
  const [t, setT] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setT(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return t;
}

function fmtClock(d) {
  return d.toLocaleTimeString("en-US", {
    hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false,
  });
}

// ───────────────────────────────────────────────────────── terminal hero ──

function TerminalHero({ theme }) {
  const lines = useMemo(() => ([
    { type: "cmd", text: "whoami" },
    { type: "out", text: "ken kelly · principal software engineer · south florida" },
    { type: "cmd", text: "cat ./about.txt" },
    { type: "out", text: BIO.blurb },
    { type: "cmd", text: "ls ~/work | head -3" },
    { type: "out", text: "total-wine-mobile/   total-wine-web/   go-weather/" },
    { type: "cmd", text: "echo $STATUS" },
    { type: "out", text: "available_for_consulting=true" },
  ]), []);

  const [visible, setVisible] = useState([]);
  const [typing, setTyping]   = useState("");
  const [idx, setIdx]         = useState(0);
  const [skipped, setSkipped] = useState(false);

  useEffect(() => {
    if (skipped) { setVisible(lines); return; }
    if (idx >= lines.length) return;
    const cur = lines[idx];
    if (cur.type === "out") {
      const id = setTimeout(() => {
        setVisible(v => [...v, cur]);
        setIdx(i => i + 1);
      }, 180);
      return () => clearTimeout(id);
    }
    let i = 0;
    setTyping("");
    const id = setInterval(() => {
      i++;
      setTyping(cur.text.slice(0, i));
      if (i >= cur.text.length) {
        clearInterval(id);
        setTimeout(() => {
          setVisible(v => [...v, cur]);
          setTyping("");
          setIdx(p => p + 1);
        }, 120);
      }
    }, 28);
    return () => clearInterval(id);
  }, [idx, skipped, lines]);

  const skip = () => setSkipped(true);

  return (
    <div className="term" onClick={skip}>
      <div className="term-bar">
        <span className="term-dot" style={{ background: "#FF5F57" }} />
        <span className="term-dot" style={{ background: "#FEBC2E" }} />
        <span className="term-dot" style={{ background: "#28C840" }} />
        <span className="term-title">ken@kkelly.dev: ~</span>
      </div>
      <div className="term-body">
        {visible.map((ln, i) =>
          ln.type === "cmd" ? (
            <div key={i} className="term-line">
              <span className="term-prompt">{theme.promptChar} </span>
              <span className="term-cmd">{ln.text}</span>
            </div>
          ) : (
            <div key={i} className="term-out">{ln.text}</div>
          )
        )}
        {!skipped && idx < lines.length && lines[idx].type === "cmd" && (
          <div className="term-line">
            <span className="term-prompt">{theme.promptChar} </span>
            <span className="term-cmd">{typing}</span>
            <span className="term-cursor">█</span>
          </div>
        )}
        {(skipped || idx >= lines.length) && (
          <div className="term-line">
            <span className="term-prompt">{theme.promptChar} </span>
            <span className="term-cursor">█</span>
          </div>
        )}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────── cmd-k ──

function CmdPalette({ open, onClose, sections }) {
  const [q, setQ] = useState("");
  const [sel, setSel] = useState(0);
  const inputRef = useRef(null);

  const scrollToId = (id) => {
    const el = document.getElementById(id);
    if (!el) return;
    const y = el.getBoundingClientRect().top + window.scrollY - 70;
    window.scrollTo({ top: y, behavior: "smooth" });
  };

  const items = useMemo(() => {
    const list = [
      ...sections.map(s => ({ kind: "Section", label: s.label, action: () => scrollToId(s.id) })),
      { kind: "Action", label: "Email Ken",       action: () => { window.location.href = `mailto:${BIO.links.email}`; }},
      { kind: "Action", label: "Open GitHub",     action: () => { window.open(BIO.links.github, "_blank"); }},
      { kind: "Action", label: "Open LinkedIn",   action: () => { window.open(BIO.links.linkedin, "_blank"); }},
      { kind: "Action", label: "Copy email address", action: () => {
        navigator.clipboard?.writeText(BIO.links.email);
      }},
      { kind: "Action", label: "Download résumé", action: () => { alert("Résumé download would start here."); }},
    ];
    if (!q.trim()) return list;
    const needle = q.toLowerCase();
    return list.filter(it => it.label.toLowerCase().includes(needle));
  }, [q, sections]);

  useEffect(() => {
    if (open) {
      setQ(""); setSel(0);
      setTimeout(() => inputRef.current?.focus(), 30);
    }
  }, [open]);

  useEffect(() => { setSel(0); }, [q]);

  const onKey = (e) => {
    if (e.key === "Escape") { onClose(); }
    else if (e.key === "ArrowDown") { e.preventDefault(); setSel(s => Math.min(items.length - 1, s + 1)); }
    else if (e.key === "ArrowUp")   { e.preventDefault(); setSel(s => Math.max(0, s - 1)); }
    else if (e.key === "Enter")     { e.preventDefault(); items[sel]?.action(); onClose(); }
  };

  if (!open) return null;
  return (
    <div className="cmdk-scrim" onClick={onClose}>
      <div className="cmdk" onClick={e => e.stopPropagation()}>
        <div className="cmdk-input-row">
          <span className="cmdk-prompt">⌘K</span>
          <input
            ref={inputRef}
            className="cmdk-input"
            placeholder="Search or jump to…"
            value={q}
            onChange={e => setQ(e.target.value)}
            onKeyDown={onKey}
          />
          <span className="cmdk-esc">esc</span>
        </div>
        <div className="cmdk-list">
          {items.length === 0 && (
            <div className="cmdk-empty">No matches.</div>
          )}
          {items.map((it, i) => (
            <div
              key={it.label}
              className={`cmdk-row ${i === sel ? "is-sel" : ""}`}
              onMouseEnter={() => setSel(i)}
              onClick={() => { it.action(); onClose(); }}
            >
              <span className="cmdk-kind">{it.kind}</span>
              <span className="cmdk-label">{it.label}</span>
              {i === sel && <span className="cmdk-enter">↵</span>}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────── sections ──

function Section({ id, label, num, children }) {
  return (
    <section id={id} className="sect" data-screen-label={label}>
      <div className="sect-head">
        <span className="sect-num">{num}</span>
        <span className="sect-label">{label}</span>
        <span className="sect-rule" />
      </div>
      <div className="sect-body">{children}</div>
    </section>
  );
}

function Hero({ theme, now }) {
  return (
    <header className="hero">
      <div className="hero-left">
        <div className="hero-handle">~/kkelly.dev</div>
        <h1 className="hero-name">
          <span className="hero-name-1">Ken</span>{" "}
          <span className="hero-name-2">Kelly</span>
        </h1>
        <p className="hero-sub">
          <span className="tag">{BIO.title}</span>
          <span className="tag">{BIO.yearsExp}+ years</span>
          <span className="tag">{BIO.location}</span>
        </p>
        <p className="hero-blurb">{BIO.blurb}</p>
        <div className="hero-cta">
          <a className="btn btn-primary" href={`mailto:${BIO.links.email}`}>
            <span className="btn-glyph">→</span> Hire for consulting
          </a>
          <button className="btn btn-ghost" onClick={() => {
            const el = document.getElementById("projects");
            if (el) {
              const y = el.getBoundingClientRect().top + window.scrollY - 70;
              window.scrollTo({ top: y, behavior: "smooth" });
            }
          }}>
            See selected work
          </button>
          <span className="kbd-hint">
            press <kbd>⌘</kbd><kbd>K</kbd> anywhere
          </span>
        </div>
      </div>
      <div className="hero-right">
        <TerminalHero theme={theme} />
      </div>
    </header>
  );
}

function AboutSection() {
  return (
    <Section id="about" label="about" num="01">
      <div className="about-grid">
        <div className="about-prose">
          <p>
            I'm <b>Ken</b>. I write production software for a living and have been doing it since
            the early 2000s. These days I split my time between principal-level work at Total Wine
            & a small consulting practice on the side.
          </p>
          <p>
            I'm equally at home in a <b>React</b> codebase and a <b>Go</b> service, which means I
            tend to get pulled into the gnarly seams between the two — performance, type-safety
            across the wire, dev-experience, build systems, and the boring infra that decides
            whether a product can actually scale.
          </p>
          <p>
            I care about <b>shipping</b>, leaving codebases healthier than I found them, and
            mentoring engineers who'll outgrow me. I'm currently picking up 1–2 consulting clients
            for Q3 — happy to talk if you're stuck on something hard.
          </p>
        </div>
        <div className="about-skills">
          {Object.entries(SKILLS).map(([k, vs]) => (
            <div key={k} className="skill-row">
              <div className="skill-key">{k.toLowerCase()}</div>
              <div className="skill-vals">
                {vs.map(v => <span key={v} className="chip">{v}</span>)}
              </div>
            </div>
          ))}
        </div>
      </div>
    </Section>
  );
}

function NowSection() {
  return (
    <Section id="now" label="now" num="02">
      <div className="now-grid">
        {NOW.map(n => (
          <div key={n.k} className="now-card">
            <div className="now-key">$ <b>{n.k}</b></div>
            <div className="now-val">{n.v}</div>
          </div>
        ))}
      </div>
    </Section>
  );
}

function ExperienceSection() {
  return (
    <Section id="experience" label="experience.log" num="03">
      <ol className="exp">
        {EXPERIENCE.map((e, i) => (
          <li key={i} className="exp-row">
            <div className="exp-range">{e.range}</div>
            <div className="exp-body">
              <div className="exp-head">
                <span className="exp-role">{e.role}</span>
                <span className="exp-sep">·</span>
                <span className="exp-company">{e.company}</span>
              </div>
              <div className="exp-notes">{e.notes}</div>
              <div className="exp-stack">
                {e.stack.map(s => <span key={s} className="chip chip-sm">{s}</span>)}
              </div>
            </div>
          </li>
        ))}
      </ol>
    </Section>
  );
}

function ProjectCard({ p }) {
  return (
    <a className="proj" href={p.href} target="_blank" rel="noreferrer">
      <div className="proj-id">id: <code>{p.id}</code></div>
      <h3 className="proj-title">{p.title}</h3>
      <div className="proj-meta">
        <span>{p.year}</span><span>·</span><span>{p.role}</span>
      </div>
      <p className="proj-summary">{p.summary}</p>
      <div className="proj-reveal">
        <div className="proj-metrics">
          {p.metrics.map(m => (
            <div key={m.l} className="proj-metric">
              <div className="proj-metric-v">{m.v}</div>
              <div className="proj-metric-l">{m.l}</div>
            </div>
          ))}
        </div>
        <div className="proj-stack">
          {p.stack.map(s => <span key={s} className="chip chip-sm">{s}</span>)}
        </div>
        <div className="proj-link">visit ↗</div>
      </div>
    </a>
  );
}

function ProjectsSection() {
  return (
    <Section id="projects" label="projects" num="04">
      <div className="proj-hint">hover any card →</div>
      <div className="proj-grid">
        {PROJECTS.map(p => <ProjectCard key={p.id} p={p} />)}
      </div>
    </Section>
  );
}

function ContactSection() {
  const [copied, setCopied] = useState(false);
  const copy = () => {
    navigator.clipboard?.writeText(BIO.links.email);
    setCopied(true);
    setTimeout(() => setCopied(false), 1400);
  };
  return (
    <Section id="contact" label="contact" num="05">
      <div className="contact-card">
        <pre className="contact-pre">
{`$ cat > /var/mail/ken <<EOF
Subject: ____________________
From:    ____________________
Body:    ____________________
EOF`}
        </pre>
        <div className="contact-actions">
          <a className="btn btn-primary" href={`mailto:${BIO.links.email}`}>
            <span className="btn-glyph">✉</span> {BIO.links.email}
          </a>
          <button className="btn btn-ghost" onClick={copy}>
            {copied ? "copied ✓" : "copy address"}
          </button>
          <a className="btn btn-ghost" href={BIO.links.github}   target="_blank" rel="noreferrer">github ↗</a>
          <a className="btn btn-ghost" href={BIO.links.linkedin} target="_blank" rel="noreferrer">linkedin ↗</a>
        </div>
        <div className="contact-foot">
          Best for: consulting engagements, technical advisory, hands-on architecture work.
        </div>
      </div>
    </Section>
  );
}

// ──────────────────────────────────────────────────────────── chrome ──

function TopBar({ onCmdK }) {
  const t = useTime();
  return (
    <div className="topbar">
      <div className="topbar-l">
        <span className="logo">
          <span className="logo-glyph">●</span> kkelly.dev
        </span>
        <span className="path">/ ~ /portfolio</span>
      </div>
      <div className="topbar-r">
        <span className="status">
          <span className="status-dot" /> available for consulting
        </span>
        <span className="clock">{fmtClock(t)} ET</span>
        <button className="cmdk-btn" onClick={onCmdK}>
          <kbd>⌘</kbd><kbd>K</kbd>
        </button>
      </div>
    </div>
  );
}

function Footer() {
  return (
    <footer className="foot">
      <pre className="foot-art">
{` _    _         _ _       
| | _| | _____| | |_   _ 
| |/ / |/ / _ \\ | | | | |
|   <|   <  __/ | | |_| |
|_|\\_\\_|\\_\\___|_|_|\\__, |
                   |___/ `}
      </pre>
      <div className="foot-meta">
        <span>© {new Date().getFullYear()} Ken Kelly</span>
        <span>·</span>
        <span>South Florida</span>
      </div>
    </footer>
  );
}

// ──────────────────────────────────────────────────────────── root ──

function App() {
  const t = TWEAK_DEFAULTS;
  const dir = DIRECTIONS[t.direction] || DIRECTIONS.terminal;
  const [paletteOpen, setPaletteOpen] = useState(false);

  // sections registry for cmd-k + j/k nav
  const sections = useMemo(() => ([
    { id: "about",       label: "01 · about" },
    { id: "now",         label: "02 · now" },
    { id: "experience",  label: "03 · experience" },
    { id: "projects",    label: "04 · projects" },
    { id: "contact",     label: "05 · contact" },
  ]), []);

  // global keymap: cmd-k, j/k nav, g+h home
  useEffect(() => {
    const onKey = (e) => {
      const target = e.target;
      const isTyping =
        target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable);
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setPaletteOpen(o => !o);
        return;
      }
      if (isTyping) return;
      if (e.key === "j" || e.key === "k") {
        e.preventDefault();
        const dirSign = e.key === "j" ? 1 : -1;
        const ids = ["top", ...sections.map(s => s.id)];
        const tops = ids.map(id => {
          const el = document.getElementById(id);
          return el ? el.getBoundingClientRect().top + window.scrollY : Infinity;
        });
        const curY = window.scrollY + 80;
        // current section = highest one whose top <= curY
        let curIdx = 0;
        for (let i = 0; i < tops.length; i++) if (tops[i] <= curY) curIdx = i;
        const nextIdx = Math.max(0, Math.min(ids.length - 1, curIdx + dirSign));
        const el = document.getElementById(ids[nextIdx]);
        if (el) {
          const y = el.getBoundingClientRect().top + window.scrollY - 70;
          window.scrollTo({ top: y, behavior: "smooth" });
        }
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [sections]);

  // apply theme vars
  const styleVars = {
    "--bg":      dir.bg,
    "--bg-alt":  dir.bgAlt,
    "--surface": dir.surface,
    "--border":  dir.border,
    "--text":    dir.text,
    "--muted":   dir.muted,
    "--accent":  t.accent || dir.accent,
    "--font":    FONT_STACKS[t.font] || FONT_STACKS.jetbrains,
    "--pad":     t.density === "compact" ? "10px" : "16px",
    "--gap":     t.density === "compact" ? "14px" : "22px",
    "--sect-y":  t.density === "compact" ? "64px" : "108px",
    "--hero-y":  t.density === "compact" ? "72px" : "128px",
  };

  return (
    <div
      id="top"
      className={`shell shell-${t.direction} density-${t.density} ${dir.scanlines ? "has-scanlines" : ""}`}
      style={styleVars}
    >
      <TopBar onCmdK={() => setPaletteOpen(true)} />
      <main className="page">
        <Hero theme={dir} now={NOW[0]} />
        <AboutSection />
        <NowSection />
        <ExperienceSection />
        <ProjectsSection />
        <ContactSection />
      </main>
      <Footer />
      <CmdPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} sections={sections} />
    </div>
  );
}

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