/* ENKII — main application. */
/* global React, ReactDOM, Warp, ENKII, ENKIICards */
const { useState, useEffect, useRef, useCallback, useLayoutEffect } = React;
const { Card, Fullscreen } = ENKIICards;
const { UI, FLAGS, LANG_ORDER, CARDS } = ENKII;

const LS = {
  get(k, d) { try { const v = localStorage.getItem("enkii_" + k); return v == null ? d : JSON.parse(v); } catch (e) { return d; } },
  set(k, v) { try { localStorage.setItem("enkii_" + k, JSON.stringify(v)); } catch (e) {} },
};

function detectLang() {
  const saved = LS.get("lang", null);
  if (saved && LANG_ORDER.includes(saved)) return saved;
  const n = (navigator.language || "en").slice(0, 2).toLowerCase();
  return LANG_ORDER.includes(n) ? n : "en";
}
function detectTheme() {
  const saved = LS.get("theme", null);
  if (saved === "light" || saved === "dark") return saved;
  return (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) ? "light" : "dark";
}

/* ===== ENKII wordmark (ENK + two bars for II) ===== */
function Wordmark({ size = "hero" }) {
  return (
    <div className={"wordmark wm-" + size}>
      <span className="wm-text">ENK</span>
      <span className="wm-bar wm-bar-a" />
      <span className="wm-bar wm-bar-b" />
    </div>
  );
}

/* ===== Top bar controls ===== */
function LangMenu({ lang, setLang }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("pointerdown", h);
    return () => document.removeEventListener("pointerdown", h);
  }, []);
  return (
    <div className="langmenu" ref={ref}>
      <button className="lang-btn" onClick={() => setOpen(o => !o)}>
        <span className="lang-flag">{FLAGS[lang]}</span>
        <span className="lang-code">{lang.toUpperCase()}</span>
        <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"><path d="M6 9l6 6 6-6" /></svg>
      </button>
      <div className={"lang-pop" + (open ? " show" : "")}>
        {LANG_ORDER.map(l => (
          <button key={l} className={"lang-item" + (l === lang ? " on" : "")}
            onClick={() => { setLang(l); setOpen(false); }}>
            <span className="lang-flag">{FLAGS[l]}</span>{UI[l].langName}
          </button>
        ))}
      </div>
    </div>
  );
}

function ThemeToggle({ theme, setTheme }) {
  return (
    <button className="theme-toggle" onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
      aria-label="theme">
      <span className={"tt-track tt-" + theme}>
        <span className="tt-knob">
          {theme === "dark"
            ? <svg viewBox="0 0 24 24" width="13" height="13" fill="currentColor"><path d="M12 3a9 9 0 109 9 7 7 0 01-9-9z" /></svg>
            : <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="4" /><path d="M12 2v2M12 20v2M2 12h2M20 12h2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19" strokeLinecap="round" /></svg>}
        </span>
      </span>
    </button>
  );
}

/* ===== Intro ===== */
function Intro({ ui, onEnter, fading }) {
  return (
    <div className={"intro" + (fading ? " fading" : "")}>
      <div className="intro-center">
        <Wordmark size="hero" />
        <div className="intro-systems">{ui.systems}</div>
        <div className="intro-stack">{ui.stack}</div>
        <div className="intro-lines">
          <span>{ui.line1}</span><span>{ui.line2}</span>
        </div>
      </div>
      <button className="scroll-cue" onClick={onEnter}>
        <span className="scroll-ring"><span className="scroll-dot" /></span>
        <span className="scroll-label">{ui.scroll}</span>
      </button>
      <div className="intro-corner tl">ENKII</div>
      <div className="intro-corner tr">SYS.STATUS&nbsp;<b>{ui.online}</b></div>
      <div className="intro-corner bl">LAT 48.9373&nbsp;&nbsp;LON 2.2900</div>
      <div className="intro-corner br">v2.6 — {ui.booting}</div>
    </div>
  );
}

/* ===== Dashboard ===== */
function Dashboard({ ui, lang, theme, setLang, setTheme, order, setOrder, openId, setOpenId, enter }) {
  const cardRefs = useRef({});
  const drag = useRef(null);
  const [draggingId, setDraggingId] = useState(null);
  const gridRef = useRef(null);
  const ghostRef = useRef(null);
  const demoBusy = useRef(false);
  const pendingClear = useRef(null);
  const openRef = useRef(openId);
  openRef.current = openId;

  const registerRef = useCallback((id, el) => { if (el) cardRefs.current[id] = el; }, []);

  // After a demo swap commits, clear inline transforms BEFORE paint → seamless (FLIP-style).
  useLayoutEffect(() => {
    if (pendingClear.current) {
      const { a, b } = pendingClear.current;
      [a, b].forEach((el) => { if (el) { el.style.transform = ""; el.style.zIndex = ""; el.classList.remove("demo-lift"); } });
      pendingClear.current = null;
    }
  }, [order]);

  // Auto-demo: every 2 min a ghost cursor grabs the iOS card and swaps it with Scalable Architecture.
  useEffect(() => {
    const reduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduced) return;

    const tween = (dur, fn) => new Promise((res) => {
      const t0 = performance.now();
      const step = (now) => {
        const p = Math.min(1, (now - t0) / dur);
        const e = p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2; // easeInOutQuad
        fn(e);
        if (p < 1) requestAnimationFrame(step); else res();
      };
      requestAnimationFrame(step);
    });
    const moveGhost = (x, y) => { const g = ghostRef.current; if (g) { g.style.left = x + "px"; g.style.top = y + "px"; } };

    async function runDemo() {
      if (demoBusy.current) return;
      if (openRef.current) return;                 // a module is open
      if (drag.current && drag.current.moved) return; // user is dragging
      const a = cardRefs.current["mobile"];        // iOS & Android
      const b = cardRefs.current["architecture"];  // Scalable Architecture
      const g = ghostRef.current;
      if (!a || !b || !g) return;
      demoBusy.current = true;

      const ra = a.getBoundingClientRect(), rb = b.getBoundingClientRect();
      const ca = { x: ra.left + ra.width / 2, y: ra.top + ra.height / 2 };
      const cb = { x: rb.left + rb.width / 2, y: rb.top + rb.height / 2 };
      const startX = window.innerWidth - 120, startY = window.innerHeight - 90;

      moveGhost(startX, startY);
      g.classList.add("show");
      await tween(750, (e) => moveGhost(startX + (ca.x - startX) * e, startY + (ca.y - startY) * e));

      // grab
      g.classList.add("grab");
      a.classList.add("demo-lift");
      a.style.zIndex = "60";
      await tween(320, () => {});

      // carry iOS card to architecture slot; architecture slides the other way
      const dx = cb.x - ca.x, dy = cb.y - ca.y;
      await tween(1250, (e) => {
        a.style.transform = `translate(${dx * e}px, ${dy * e}px) scale(1.05)`;
        b.style.transform = `translate(${-dx * e}px, ${-dy * e}px)`;
        moveGhost(ca.x + dx * e, ca.y + dy * e);
      });

      // release: commit the real swap; useLayoutEffect clears transforms before paint
      g.classList.remove("grab");
      pendingClear.current = { a, b };
      setOrder((prev) => {
        const next = prev.slice();
        const i = next.indexOf("mobile"), j = next.indexOf("architecture");
        if (i >= 0 && j >= 0) { next[i] = "architecture"; next[j] = "mobile"; }
        return next;
      });

      await tween(260, () => {});
      await tween(560, (e) => moveGhost(cb.x + (startX - cb.x) * e, cb.y + (startY - cb.y) * e));
      g.classList.remove("show");
      demoBusy.current = false;
    }

    const id = setInterval(runDemo, 60000); // every 1 minute
    window.__enkiiDemo = runDemo; // manual trigger (testing)
    return () => clearInterval(id);
  }, [setOrder]);

  const onDragStart = useCallback((e, id) => {
    if (e.button === 1 || e.button === 2) return;
    const el = cardRefs.current[id];
    if (!el) return;
    const isTouch = e.pointerType === "touch";
    const grid = gridRef.current;

    const d = {
      id, startX: e.clientX, startY: e.clientY, moved: false,
      el, t0: performance.now(), isTouch, armed: false, canceled: false, lpTimer: 0,
    };
    drag.current = d;

    // On touch, only block native scroll once a drag is actually armed (long-press).
    const blockTouch = (ev) => { if (drag.current && drag.current.armed && ev.cancelable) ev.preventDefault(); };

    const activate = () => {
      const dd = drag.current; if (!dd || dd.canceled || dd.armed) return;
      dd.armed = true; dd.moved = true;
      setDraggingId(id);
      dd.el.style.zIndex = 60;
      if (grid) grid.classList.add("drag-lock");
      if (isTouch && navigator.vibrate) { try { navigator.vibrate(14); } catch (er) {} }
    };

    // Touch: arm a drag only after a deliberate press-and-hold; mouse: arm on small move.
    if (isTouch) d.lpTimer = setTimeout(activate, 340);

    const move = (ev) => {
      const dd = drag.current; if (!dd) return;
      const dx = ev.clientX - dd.startX, dy = ev.clientY - dd.startY;
      const dist = Math.hypot(dx, dy);

      if (!dd.armed) {
        if (isTouch) {
          // Movement before the hold completes = the user is scrolling → cancel the drag.
          if (dist > 12 && !dd.canceled) { dd.canceled = true; clearTimeout(dd.lpTimer); }
          return; // let native scroll happen
        } else {
          if (dist > 7) activate(); else return;
        }
      }

      dd.el.style.transform = `translate(${dx}px, ${dy}px) scale(1.04)`;
      let overId = null;
      for (const cid of Object.keys(cardRefs.current)) {
        if (cid === id) continue;
        const r = cardRefs.current[cid].getBoundingClientRect();
        if (ev.clientX >= r.left && ev.clientX <= r.right && ev.clientY >= r.top && ev.clientY <= r.bottom) {
          overId = cid; break;
        }
      }
      if (overId) {
        setOrder(prev => {
          const from = prev.indexOf(id), to = prev.indexOf(overId);
          if (from === to || from < 0 || to < 0) return prev;
          const next = prev.slice();
          next.splice(from, 1); next.splice(to, 0, id);
          return next;
        });
      }
    };
    const up = () => {
      const dd = drag.current;
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", up);
      window.removeEventListener("pointercancel", up);
      document.removeEventListener("touchmove", blockTouch);
      if (dd) {
        clearTimeout(dd.lpTimer);
        dd.el.style.transform = "";
        dd.el.style.zIndex = "";
        if (grid) grid.classList.remove("drag-lock");
        // Tap to open: never armed, never turned into a scroll, and quick.
        if (!dd.armed && !dd.canceled && performance.now() - dd.t0 < 600) setOpenId(id);
      }
      setDraggingId(null);
      drag.current = null;
    };
    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", up);
    window.addEventListener("pointercancel", up);
    if (isTouch) document.addEventListener("touchmove", blockTouch, { passive: false });
  }, [setOrder, setOpenId]);

  const byId = Object.fromEntries(CARDS.map(c => [c.id, c]));
  const openCard = openId ? byId[openId] : null;

  return (
    <div className="dash">
      <header className="dash-bar">
        <div className="db-left" onClick={enter} style={{ cursor: "pointer" }}>
          <Wordmark size="mini" />
          <div className="db-os">
            <span className="db-os-name">{ui.osName}</span>
            <span className="db-os-sub">{ui.osSub}</span>
          </div>
        </div>
        <div className="db-center">
          <span className="db-live"><i className="pulse" />{ui.live}</span>
          <span className="db-mod">{order.length} {ui.modules}</span>
        </div>
        <div className="db-right">
          <button className="db-reset" onClick={() => setOrder(CARDS.map(c => c.id))}>{ui.reset}</button>
          <LangMenu lang={lang} setLang={setLang} />
          <ThemeToggle theme={theme} setTheme={setTheme} />
        </div>
      </header>

      <div className="dash-hint">{ui.drag}</div>

      <main className="grid" ref={gridRef}>
        {order.map(id => (
          <Card key={id} data={byId[id]} lang={lang} ui={ui}
            onOpen={setOpenId} registerRef={registerRef}
            onDragStart={onDragStart} dragging={draggingId === id} />
        ))}
      </main>

      <footer className="dash-foot">
        <span className="df-side">ENKII OS · {ui.osSub}</span>
        <nav className="df-legal">
          <a href="legal.html#mentions">{({ fr: "Mentions légales", en: "Legal notice", pt: "Menções legais", es: "Aviso legal" })[lang]}</a>
          <span className="df-sep">·</span>
          <a href="legal.html#privacy">{({ fr: "Confidentialité", en: "Privacy", pt: "Privacidade", es: "Privacidad" })[lang]}</a>
          <span className="df-sep">·</span>
          <a href="legal.html#cookies">Cookies</a>
          <span className="df-sep">·</span>
          <a href="legal.html#terms">{({ fr: "CGU", en: "Terms", pt: "Termos", es: "Términos" })[lang]}</a>
        </nav>
        <span className="df-side df-right">LAT 48.9373 · LON 2.2900 · SYS.STATUS <b>{ui.online}</b></span>
      </footer>

      {openCard && <Fullscreen data={openCard} lang={lang} ui={ui} onClose={() => setOpenId(null)} />}

      <div className="demo-ghost" ref={ghostRef} aria-hidden="true">
        <svg viewBox="0 0 24 24" width="26" height="26"><path d="M5 3l15 9-6.5 1.5L10 20 5 3z" fill="#fff" stroke="#0a0c14" strokeWidth="1.2" strokeLinejoin="round" /></svg>
        <span className="demo-ring" />
      </div>
    </div>
  );
}

/* ===== Cookie consent banner ===== */
function CookieBanner({ lang }) {
  const c = (ENKII.COOKIE && ENKII.COOKIE[lang]) || ENKII.COOKIE.en;
  const [show, setShow] = useState(false);
  const [leaving, setLeaving] = useState(false);
  useEffect(() => {
    let decided = null;
    try { decided = localStorage.getItem("enkii_consent"); } catch (e) {}
    if (!decided) {
      const t = setTimeout(() => setShow(true), 1400);
      return () => clearTimeout(t);
    }
  }, []);
  useEffect(() => {
    document.body.classList.toggle("has-cookie", show && !leaving);
    return () => document.body.classList.remove("has-cookie");
  }, [show, leaving]);
  const decide = (choice) => {
    try { localStorage.setItem("enkii_consent", choice); } catch (e) {}
    setLeaving(true);
    setTimeout(() => setShow(false), 460);
  };
  if (!show) return null;
  return (
    <div className={"cookie" + (leaving ? " leaving" : "")} role="dialog" aria-label={c.title}>
      <div className="cookie-ic">
        <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
          <path d="M12 3a9 9 0 109 9 4 4 0 01-4-4 3 3 0 01-3-3 5 5 0 01-2-2z" /><circle cx="9" cy="13" r="1" fill="currentColor" /><circle cx="14" cy="15.5" r="1" fill="currentColor" /><circle cx="14.5" cy="9.5" r="1" fill="currentColor" />
        </svg>
      </div>
      <div className="cookie-body">
        <div className="cookie-title">{c.title}</div>
        <p className="cookie-msg">{c.msg} <a href="legal.html#cookies">{c.more} ↗</a></p>
      </div>
      <div className="cookie-actions">
        <button className="cookie-refuse" onClick={() => decide("refused")}>{c.refuse}</button>
        <button className="cookie-accept" onClick={() => decide("accepted")}>{c.accept}</button>
      </div>
    </div>
  );
}

/* ===== Root ===== */
function App() {
  const [lang, setLangState] = useState(detectLang);
  const [theme, setThemeState] = useState(detectTheme);
  const [phase, setPhase] = useState("intro");
  const [fading, setFading] = useState(false);
  const [openId, setOpenId] = useState(null);
  const [order, setOrderState] = useState(() => {
    const saved = LS.get("order", null);
    const ids = CARDS.map(c => c.id);
    if (Array.isArray(saved) && saved.length === ids.length && ids.every(i => saved.includes(i))) return saved;
    return ids;
  });
  const transitioning = useRef(false);

  const setLang = (l) => { setLangState(l); LS.set("lang", l); };
  const setTheme = (t) => { setThemeState(t); LS.set("theme", t); };
  const setOrder = (u) => setOrderState(prev => { const n = typeof u === "function" ? u(prev) : u; LS.set("order", n); return n; });

  // keep <html lang> + document title in sync with the active language (a11y + SEO)
  useEffect(() => {
    document.documentElement.setAttribute("lang", lang);
    const titles = {
      fr: "ENKII — Systèmes Intelligents · Apps Web & Mobile, Agents IA",
      en: "ENKII — Intelligent Systems · Web & Mobile Apps, AI Agents",
      pt: "ENKII — Sistemas Inteligentes · Apps Web & Mobile, Agentes de IA",
      es: "ENKII — Sistemas Inteligentes · Apps Web y Móviles, Agentes de IA",
    };
    if (titles[lang]) document.title = titles[lang];
  }, [lang]);

  useEffect(() => { document.documentElement.setAttribute("data-theme", theme); if (window.Warp) Warp.setTheme(theme); }, [theme]);
  useEffect(() => { window.__enkii = { open: setOpenId, enter: () => { setPhase("dash"); if (window.Warp) { Warp.setConverge(0); Warp.setIntensity(0.34); } } }; }, []);

  const ui = UI[lang];

  const enterSystem = useCallback(() => {
    if (phase === "dash" || transitioning.current) return;
    transitioning.current = true;
    setFading(true);
    if (window.Warp) { Warp.setConverge(1); Warp.setIntensity(2.6); }
    setTimeout(() => {
      setPhase("dash");
      if (window.Warp) { Warp.setConverge(0); Warp.setIntensity(0.34); }
      setTimeout(() => { transitioning.current = false; }, 400);
    }, 1150);
  }, [phase]);

  // intro scroll / touch / key triggers
  useEffect(() => {
    if (phase !== "intro") return;
    const trigger = (e) => { enterSystem(); };
    const onWheel = (e) => { if (Math.abs(e.deltaY) > 4) trigger(); };
    const onKey = (e) => { if (["ArrowDown", "PageDown", " ", "Enter", "Spacebar"].includes(e.key)) trigger(); };
    let ty = 0;
    const onTS = (e) => { ty = e.touches[0].clientY; };
    const onTM = (e) => { if (ty - e.touches[0].clientY > 18) trigger(); };
    window.addEventListener("wheel", onWheel, { passive: true });
    window.addEventListener("keydown", onKey);
    window.addEventListener("touchstart", onTS, { passive: true });
    window.addEventListener("touchmove", onTM, { passive: true });
    return () => {
      window.removeEventListener("wheel", onWheel);
      window.removeEventListener("keydown", onKey);
      window.removeEventListener("touchstart", onTS);
      window.removeEventListener("touchmove", onTM);
    };
  }, [phase, enterSystem]);

  return (
    <>
      {phase === "intro" && <Intro ui={ui} onEnter={enterSystem} fading={fading} />}
      {phase === "dash" && (
        <Dashboard ui={ui} lang={lang} theme={theme} setLang={setLang} setTheme={setTheme}
          order={order} setOrder={setOrder} openId={openId} setOpenId={setOpenId} enter={enterSystem} />
      )}
      <CookieBanner lang={lang} />
    </>
  );
}

// boot
(function () {
  const canvas = document.getElementById("warp");
  if (window.Warp && canvas) Warp.init(canvas);
  Warp.setTheme(document.documentElement.getAttribute("data-theme") || "dark");
  ReactDOM.createRoot(document.getElementById("root")).render(<App />);
})();
