/* Shared data + components for all pages */
const { useState, useEffect, useRef, useMemo } = React;

/* ---------- Data ---------- */
window.EXPERIENCE = [
  {
    date: "Aug 2024 — Present",
    role: "Co-Founder & CTO",
    org: "Electron Hub",
    loc: "Remote",
    bullets: [
      "Architected multi-provider REST gateway handling 30M+ total requests with sub-200ms p95 latency and 99.9% uptime SLA using FastAPI, MongoDB, Redis, and Pinecone.",
      "Reduced developer integration time by 75% through a unified interface covering 600+ AI models across 32+ providers.",
      "Cut AI inference costs by 40% and grew ARR to $120K via RAG-based intelligent routing and caching.",
      "Decreased operational overhead by 60% with LLMOps pipelines for automated deployment, load balancing, and failover."
    ]
  },
  {
    date: "May 2023 — Dec 2024",
    role: "Full Stack Developer",
    org: "Nha Anh Ngu",
    loc: "Remote",
    bullets: [
      "Improved platform performance by 30% on page-load times and sustained 99.5% uptime across 200+ concurrent users.",
      "Drove a 15% lift in user engagement across 200+ students through data-driven React UI/UX redesigns.",
      "Sole engineer on a full LMS — shipped course access, instructor tools, progress tracking, and auth in under 12 months."
    ]
  },
  {
    date: "Nov 2023 — Jun 2024",
    role: "Technical Advisor",
    org: "Zaiwen",
    loc: "Remote",
    bullets: [
      "Reduced average API response time by 40% by re-engineering the Poe API integration layer and optimizing GraphQL batching.",
      "Improved platform stability for 100K+ student users via a test automation framework reaching 85% coverage.",
      "Eliminated manual releases entirely by co-designing GitHub Actions CI/CD pipelines with a 95% deployment success rate."
    ]
  }
];

window.PROJECTS = [
  {
    num: "01", title: "Electron Hub", jp: "電子の巣",
    meta: "AI Gateway · Active", year: "2024 — Present",
    blurb: "Multi-provider LLM gateway. A unified REST interface across 600+ AI models and 32+ providers, with intelligent routing that selects the cheapest viable path per request.",
    bullets: [
      "30M+ total requests · 99.9% uptime SLA",
      "$120K ARR with RAG-based intelligent routing",
      "Sub-200ms p95 across 32+ providers",
      "60% reduction in operational overhead"
    ],
    tags: ["FastAPI", "MongoDB", "Redis", "Pinecone", "RAG", "LLMOps"]
  },
  {
    num: "02", title: "SentinelHive", jp: "番犬",
    meta: "AI Cybersecurity", year: "2026",
    blurb: "A 7-agent async swarm with adversarial peer review for malware analysis.",
    bullets: [
      "Triage, RE, Behavioral, Threat-Intel, Detection, Remediation, Director",
      "RAG via ChromaDB + MongoDB Atlas, MITRE ATT&CK mapping",
      "Docker sandboxing — seccomp + capability drop + no-network",
      "YARA/Sigma auto-generation, LIEF/Capstone/FLOSS analysis"
    ],
    tags: ["FastAPI", "Next.js", "ChromaDB", "Docker"]
  },
  {
    num: "03", title: "Poe API Wrapper", jp: "媒体",
    meta: "OSS · 1.1k stars · 350k+ DL", year: "2023 — Present",
    blurb: "Open-source Python library for programmatic access to 10+ frontier AI models.",
    bullets: [
      "ChatGPT, Claude, Llama 2, PaLM 2 via WebSockets + GraphQL",
      "350,000+ PyPI downloads from the global NLP community",
      "Resolved 200+ community issues, full doc maintenance",
      "Enabled hundreds of devs to integrate models without official APIs"
    ],
    tags: ["Python", "WebSockets", "GraphQL", "OSS"]
  },
  {
    num: "04", title: "coDestress", jp: "薬",
    meta: "AI Mental Health", year: "2022 — 2023",
    blurb: "Full-stack mental-health platform with AI stress analysis and music therapy.",
    bullets: [
      "OpenCV stress-level analysis from facial cues",
      "Real-time WebSocket therapy sessions",
      "Spotify, YouTube Music, ResponsiveVoice TTS integration",
      "Django REST + PostgreSQL backend"
    ],
    tags: ["Django", "OpenCV", "PostgreSQL", "WebSockets"]
  },
  {
    num: "05", title: "Nha Anh Ngu LMS", jp: "学",
    meta: "Learning Platform", year: "2023 — 2024",
    blurb: "Sole-engineer LMS serving 200+ students with zero-downtime delivery.",
    bullets: [
      "Course access, instructor tools, progress tracking, auth",
      "Django REST + React with Froala rich-text editor",
      "30% page-load improvement, 99.5% uptime",
      "User research → 15% engagement lift"
    ],
    tags: ["Django", "React", "Redis", "PostgreSQL"]
  },
  {
    num: "06", title: "High School MS", jp: "学校",
    meta: "School Platform", year: "2022",
    blurb: "Responsive school management with role-based modules.",
    bullets: [
      "Student / result management modules",
      "Nested-comment blog and document library",
      "Role-based authentication system",
      "Django REST + React + SQLite"
    ],
    tags: ["Django REST", "React", "SQLite"]
  }
];

window.SKILLS = {
  "Languages": { jp: "言語", primary: ["Python", "TypeScript", "JavaScript"], rest: ["C", "HTML", "CSS / SCSS", "LaTeX"] },
  "Frameworks": { jp: "枠", primary: ["FastAPI", "Next.js", "React"], rest: ["Django", "Flask", "LangChain", "NumPy", "Pandas", "scikit-learn", "TensorFlow", "OpenCV"] },
  "Databases": { jp: "蔵", primary: ["MongoDB", "PostgreSQL", "Redis"], rest: ["MySQL", "SQLite", "ChromaDB", "Pinecone", "Weaviate", "Firebase"] },
  "Cloud & DevOps": { jp: "雲", primary: ["Docker", "GitHub Actions"], rest: ["Vercel", "Heroku", "Koyeb", "PythonAnywhere", "Unix / Linux"] },
  "AI / LLMOps": { jp: "知", primary: ["RAG", "Vector Search", "LLM Load Balancing"], rest: ["Prompt Engineering", "Multi-provider Orchestration", "Ollama"] },
  "Tools": { jp: "具", primary: ["Git"], rest: ["Postman", "Insomnia", "Jira", "Trello", "Jupyter", "VS Code"] }
};

window.AWARDS = [
  { date: "Oct 2024", title: "Best Use of API", org: "SASEhack Fall 2024", rank: "Winner" },
  { date: "Aug 2023", title: "29th National Young Informatics Contest", org: "Vietnam", rank: "4th" },
  { date: "Jul 2023", title: "29th Regional Young Informatics Contest", org: "Vietnam", rank: "1st" },
  { date: "Oct 2022", title: "Youth Makers Robotics Competition", org: "NTU Singapore × TRANSINEX", rank: "Champion" }
];

window.ANILIST_DATA = {
  watching: [
    { title: "Bungo Stray Dogs", jp: "文", meta: "S5 · Spring 2026", prog: 11, of: 13, score: 9.0, status: "WATCHING" },
    { title: "Frieren: Beyond Journey's End", jp: "葬", meta: "Spring 2024 · Madhouse", prog: 28, of: 28, score: 9.4, status: "COMPLETED" },
    { title: "Vinland Saga", jp: "海", meta: "S2 · Wit/MAPPA", prog: 24, of: 24, score: 9.2, status: "COMPLETED" },
    { title: "Mushishi", jp: "蟲", meta: "Artland · Rewatching", prog: 8, of: 26, score: 9.5, status: "REWATCHING" },
    { title: "Monogatari Series", jp: "物", meta: "Shaft · 2009 — Present", prog: 96, of: 105, score: 8.8, status: "WATCHING" },
    { title: "Mononoke", jp: "化", meta: "Toei · 2007", prog: 12, of: 12, score: 9.1, status: "COMPLETED" }
  ],
  completed: [
    { title: "Texhnolyze", jp: "機", meta: "Madhouse · 2003", prog: 22, of: 22, score: 9.3, status: "COMPLETED" },
    { title: "Lain", jp: "層", meta: "Triangle · 1998", prog: 13, of: 13, score: 9.4, status: "COMPLETED" },
    { title: "Ergo Proxy", jp: "代", meta: "Manglobe · 2006", prog: 23, of: 23, score: 8.9, status: "COMPLETED" },
    { title: "Cowboy Bebop", jp: "宇", meta: "Sunrise · 1998", prog: 26, of: 26, score: 9.5, status: "COMPLETED" }
  ],
  planning: [
    { title: "Pluto", jp: "鉄", meta: "Studio M2 · 2023", prog: 0, of: 8, score: null, status: "PLANNING" },
    { title: "Devilman Crybaby", jp: "悪", meta: "Science Saru · 2018", prog: 0, of: 10, score: null, status: "PLANNING" }
  ]
};

window.MUSIC_QUEUE = [
  { title: "Drown",                artist: "Lewis Capaldi · feat. Clementine Douglas", album: "Broken by Desire", dur: "4:28", jp: "音", current: true },
  { title: "Daylight",             artist: "David Kushner",                  album: "Daydream",              dur: "3:32", jp: "光" },
  { title: "Liability",            artist: "Lorde",                          album: "Melodrama",             dur: "2:52", jp: "罪" },
  { title: "About You",            artist: "The 1975",                       album: "Being Funny in a Foreign Language", dur: "5:30", jp: "君" },
  { title: "Aoi Hitomi",           artist: "Yorushika",                      album: "Plagiarism",            dur: "4:11", jp: "蒼" },
  { title: "Re:You",               artist: "Yorushika",                      album: "Magic Lantern",         dur: "3:54", jp: "貴" },
  { title: "Yume Tourou",          artist: "RADWIMPS",                       album: "Your Name OST",         dur: "3:18", jp: "夢" },
  { title: "Cinderella",           artist: "Mrs. GREEN APPLE",               album: "Antenna",               dur: "4:02", jp: "灰" },
  { title: "Snow Halation",        artist: "μ's",                            album: "Love Live",             dur: "5:01", jp: "雪" },
  { title: "Stay",                 artist: "Hikaru Utada",                   album: "Bad Mode",              dur: "4:39", jp: "残" },
  { title: "First Love",           artist: "Hikaru Utada",                   album: "First Love",            dur: "4:18", jp: "初" },
  { title: "Lemon",                artist: "Kenshi Yonezu",                  album: "STRAY SHEEP",           dur: "4:15", jp: "檸" }
];

window.GALLERY_ITEMS = [
  { jp: "海", label: "yokohama bay",   code: "01.jpg", ang: "30deg",  cls: "",     date: "2025.08.14" },
  { jp: "月", label: "long exposure",  code: "02.jpg", ang: "120deg", cls: "",     date: "2025.09.02" },
  { jp: "雨", label: "tampa rain",     code: "03.jpg", ang: "60deg",  cls: "tall", date: "2025.07.21" },
  { jp: "灯", label: "lantern",        code: "04.jpg", ang: "-30deg", cls: "",     date: "2025.10.11" },
  { jp: "書", label: "notebook",       code: "05.jpg", ang: "75deg",  cls: "",     date: "2025.11.03" },
  { jp: "車", label: "drive home",     code: "06.jpg", ang: "-50deg", cls: "wide", date: "2026.01.18" },
  { jp: "桜", label: "april",          code: "07.jpg", ang: "15deg",  cls: "",     date: "2026.04.05" },
  { jp: "霧", label: "morning fog",    code: "08.jpg", ang: "100deg", cls: "",     date: "2026.02.27" },
  { jp: "影", label: "shadow study",   code: "09.jpg", ang: "-15deg", cls: "",     date: "2026.03.14" },
  { jp: "雲", label: "clouds",         code: "10.jpg", ang: "45deg",  cls: "",     date: "2025.06.30" },
  { jp: "港", label: "harbor at dusk", code: "11.jpg", ang: "-60deg", cls: "wide", date: "2025.05.12" },
  { jp: "町", label: "old town",       code: "12.jpg", ang: "20deg",  cls: "",     date: "2025.04.08" }
];

/* ---------- Hooks ---------- */
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll('.reveal, .bandage-divider');
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.15 });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, []);
}

function useTypewriter(lines, speed = 22) {
  const [text, setText] = useState("");
  const [done, setDone] = useState(false);
  useEffect(() => {
    let i = 0;
    const full = lines.join("\n");
    const t = setInterval(() => {
      i++;
      setText(full.slice(0, i));
      if (i >= full.length) { clearInterval(t); setDone(true); }
    }, speed);
    return () => clearInterval(t);
  }, [lines.join("|")]);
  return { text, done };
}

function useInkTrail(enabled) {
  useEffect(() => {
    if (!enabled) return;
    let last = 0;
    const onMove = (e) => {
      const now = performance.now();
      if (now - last < 50) return;
      last = now;
      const dot = document.createElement('div');
      dot.className = 'ink-dot';
      const size = 4 + Math.random() * 6;
      dot.style.width = dot.style.height = size + 'px';
      dot.style.left = (e.clientX - size/2) + 'px';
      dot.style.top  = (e.clientY - size/2) + 'px';
      dot.style.opacity = (0.3 + Math.random() * 0.4).toString();
      document.body.appendChild(dot);
      requestAnimationFrame(() => {
        dot.style.transform = `translate(${(Math.random()-0.5)*40}px, ${20 + Math.random()*40}px) scale(${0.3 + Math.random()*0.6})`;
        dot.style.opacity = '0';
      });
      setTimeout(() => dot.remove(), 1300);
    };
    window.addEventListener('mousemove', onMove);
    return () => window.removeEventListener('mousemove', onMove);
  }, [enabled]);
}

/* ---------- Shared components ---------- */
function Marquee({ text = "WE ARE NOT FINISHED", reverse = false, count = 12 }) {
  const items = Array.from({ length: count });
  return (
    <div className="marquee" data-dir={reverse ? "reverse" : "forward"}>
      <div className="marquee-track">
        <span>
          {items.map((_, i) => (
            <React.Fragment key={i}>
              {text}
              <span className="dot">//</span>
            </React.Fragment>
          ))}
        </span>
        <span>
          {items.map((_, i) => (
            <React.Fragment key={i}>
              {text}
              <span className="dot">//</span>
            </React.Fragment>
          ))}
        </span>
      </div>
    </div>
  );
}

function Nav({ active = "home" }) {
  const links = [
    { id: "home",     label: "HOME",     href: "/",           num: "01" },
    { id: "projects", label: "PROJECTS", href: "/projects/",  num: "02" },
    { id: "gallery",  label: "GALLERY",  href: "/gallery/",   num: "03" },
    { id: "anilist",  label: "ANILIST",  href: "/anilist/",   num: "04" },
    { id: "music",    label: "MUSIC",    href: "/music/",     num: "05" },
    { id: "easter",   label: "FILES",    href: "/easter/",    num: "06", small: true }
  ];
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    if (open) document.body.style.overflow = 'hidden';
    else document.body.style.overflow = '';
    return () => { document.body.style.overflow = ''; };
  }, [open]);
  return (
    <nav className={"nav" + (open ? " mobile-open" : "")}>
      <a className="nav-brand" href="/">
        <span>BAO NGO</span>
        <span className="jp">呉宝</span>
      </a>
      <button
        className="nav-burger"
        aria-label={open ? "Close menu" : "Open menu"}
        aria-expanded={open}
        onClick={() => setOpen(o => !o)}
      >
        <span></span><span></span><span></span>
      </button>
      <div className="nav-links">
        {links.map(l => (
          <a key={l.id} href={l.href} className={(active === l.id ? "active" : "") + (l.small ? " small" : "")} onClick={() => setOpen(false)}>
            <span className="nav-num">{l.num}</span>{l.label}
          </a>
        ))}
        <ThemeSwitcher />
      </div>
      {open && <div className="nav-scrim" onClick={() => setOpen(false)} />}
    </nav>
  );
}

function Footer({ page = "" }) {
  return (
    <footer className="footer">
      <div>© 2026 BAO NGO · TAMPA, FL</div>
      <div className="jp">人間失格ではない · NOT NO-LONGER-HUMAN</div>
      <div>
        {page ? `PAGE · ${page.toUpperCase()}` : "BUILT WITH REACT"}
        <span
          className="doppo-mark"
          title="Doppo Kunikida's notebook"
          onClick={(e) => {
            e.stopPropagation();
            try {
              const c = JSON.parse(localStorage.getItem('clues-found') || '{}');
              c.doppo = true;
              localStorage.setItem('clues-found', JSON.stringify(c));
              window.dispatchEvent(new Event('clues-updated'));
              const t = document.createElement('div');
              t.className = 'konami-message';
              t.textContent = '国木田の手帳 · CLUE LOGGED';
              t.style.fontSize = 'clamp(20px, 3vw, 36px)';
              document.body.appendChild(t);
              setTimeout(() => t.remove(), 2400);
            } catch {}
          }}
        >📓</span>
      </div>
    </footer>
  );
}

function PaperLayer() {
  return (
    <>
      <div className="paper-grain"></div>
      <div className="vignette"></div>
    </>
  );
}

function PageHeader({ num, title, jp, sub, meta }) {
  return (
    <header className="page-header">
      <div className="page-header-meta">
        <span>FILE · {num}</span>
        <span>{meta}</span>
      </div>
      <h1 className="page-header-title">
        {title}<span className="jp">{jp}</span>
      </h1>
      <p className="page-header-sub">{sub}</p>
      <div className="bandage-divider in"></div>
    </header>
  );
}

/* ---------- Era + tweaks (shared default) ---------- */
window.SHARED_TWEAK_DEFAULTS = {
  era: "agency",
  density: "comfortable",
  showInkTrail: true
};

function applyEra(era, density) {
  document.body.dataset.era = era;
  document.body.dataset.density = density;
  if (density === 'compact')   document.documentElement.style.setProperty('--gut', 'clamp(16px, 3vw, 40px)');
  else if (density === 'airy') document.documentElement.style.setProperty('--gut', 'clamp(28px, 6vw, 96px)');
  else                          document.documentElement.style.setProperty('--gut', 'clamp(20px, 4vw, 64px)');
}

/* Theme: 'light' | 'dark' | 'auto' (follows OS) */
function applyTheme(theme) {
  let resolved = theme;
  if (theme === 'auto') {
    resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
  }
  document.body.dataset.theme = resolved;
  document.body.dataset.themePref = theme;
}
window.applyTheme = applyTheme;

/* Listen to OS theme changes when in auto mode */
if (window.matchMedia) {
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
    if (document.body.dataset.themePref === 'auto') applyTheme('auto');
  });
}

/* Theme switcher component (used in Nav) */
function ThemeSwitcher() {
  const [theme, setTheme] = useState(() => {
    try { return localStorage.getItem('theme') || 'light'; } catch { return 'light'; }
  });
  useEffect(() => {
    applyTheme(theme);
    try { localStorage.setItem('theme', theme); } catch {}
  }, [theme]);
  // cycle: light → dark → auto → light
  const cycle = () => {
    setTheme(t => t === 'light' ? 'dark' : t === 'dark' ? 'auto' : 'light');
  };
  const icon = theme === 'light' ? '☀' : theme === 'dark' ? '☾' : '◐';
  const label = theme === 'light' ? 'AGENCY' : theme === 'dark' ? 'PORT MAFIA' : 'AUTO';
  return (
    <button
      className={"theme-switcher theme-" + theme}
      onClick={cycle}
      title={"Theme: " + label + " (click to cycle)"}
      aria-label={"Theme: " + label}
    >
      <span className="ts-icon">{icon}</span>
      <span className="ts-label">{label}</span>
    </button>
  );
}
window.ThemeSwitcher = ThemeSwitcher;

/* Global Konami listener — installed on every page via shared.jsx.
   Mounts a tiny invisible component to the document body that watches
   for the sequence and toggles dark mode + a flashy reveal. */
(function installGlobalKonami() {
  if (window.__konamiInstalled) return;
  window.__konamiInstalled = true;
  document.addEventListener('DOMContentLoaded', () => {
    const seq = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];
    let pos = 0;
    window.addEventListener('keydown', (e) => {
      const k = e.key.length === 1 ? e.key.toLowerCase() : e.key;
      if (k === seq[pos]) {
        pos++;
        if (pos === seq.length) {
          pos = 0;
          // toggle to dark mode
          try { localStorage.setItem('theme', 'dark'); } catch {}
          if (window.applyTheme) window.applyTheme('dark');
          // Show a brief reveal
          const flash = document.createElement('div');
          flash.className = 'konami-flash';
          document.body.appendChild(flash);
          const msg = document.createElement('div');
          msg.className = 'konami-message';
          msg.textContent = 'Port Mafia · 港マフィア';
          document.body.appendChild(msg);
          // Stamp a flag so user gets a discovery credit (used by Easter page)
          try {
            const found = JSON.parse(localStorage.getItem('clues-found') || '{}');
            found.konami = true;
            localStorage.setItem('clues-found', JSON.stringify(found));
          } catch {}
          setTimeout(() => { flash.remove(); msg.remove(); }, 2600);
        }
      } else {
        pos = (k === seq[0]) ? 1 : 0;
      }
    });
  });
})();

/* persist era across pages via localStorage */
window.loadEra = () => {
  try {
    const era = localStorage.getItem('era') || 'agency';
    const density = localStorage.getItem('density') || 'comfortable';
    const theme = localStorage.getItem('theme') || 'light';
    applyEra(era, density);
    applyTheme(theme);
    return { era, density, theme };
  } catch(e) { applyEra('agency','comfortable'); applyTheme('light'); return { era:'agency', density:'comfortable', theme:'light'}; }
};
window.saveEra = (era, density) => {
  try {
    localStorage.setItem('era', era);
    localStorage.setItem('density', density);
  } catch(e){}
  applyEra(era, density);
};

/* Export to global scope for other Babel files */
Object.assign(window, {
  useReveal, useTypewriter, useInkTrail,
  Marquee, Nav, Footer, PaperLayer, PageHeader,
  applyEra, ThemeSwitcher
});
