/* global React */
// Full-viewport code field. Tiles of code lines fill the background,
// fade on scroll (parallax), and part around the Works logo like water
// whenever it moves. Listens for the `worksLogoMove` custom event with
// detail = { x, y } in screen coords.

const { useEffect: useEffectCF, useRef: useRefCF } = React;

const ACCENT_RE = /(Challenge Accepted|works\.cy|@works|let's talk|—|✓|hello@|automate)/i;

const ALL_LINES = [
  "// — works.cy",
  "// design + engineering studio",
  "// limassol, cyprus",
  "",
  "import { craft, ship } from '@works/studio';",
  "import type { Brief, Outcome } from './types';",
  "import { audit, match } from './lib/audit';",
  "import { automate } from './lib/auto';",
  "",
  "const principles = {",
  "  precision: true,",
  "  taste: 'editorial',",
  "  rush: false,",
  "  shouts: false,",
  "};",
  "",
  "/**",
  " *  Challenge Accepted.",
  " *  — Michael",
  " */",
  "",
  "export async function build(brief) {",
  "  const plan = await audit(brief);",
  "  if (!plan.viable) return reject(plan);",
  "  return craft({",
  "    plan,",
  "    style: 'minimal-confident',",
  "    timeline: plan.weeks,",
  "  });",
  "}",
  "",
  "// 12+ products shipped",
  "// 0 launch-day bugs",
  "// 100% client retention",
  "",
  "function audit(brief) {",
  "  // surface the real problem",
  "  // not just what they asked",
  "  const systems = brief.systems ?? [];",
  "  return systems.map(",
  "    s => match(s, brief),",
  "  );",
  "}",
  "",
  "const approaches = [",
  "  'automate', // remove friction",
  "  'build',    // build missing",
  "  'hybrid',   // both, in order",
  "];",
  "",
  "type Brief = {",
  "  client: string;",
  "  problem: string;",
  "  budget?: number;",
  "};",
  "",
  "interface Outcome {",
  "  shipped: boolean;",
  "  uptime: number;",
  "  bugs: number;",
  "}",
  "",
  "$ npm run build",
  "",
  "> works.cy@1.0.0 build",
  "> next build && next export",
  "",
  "✓ compiled successfully",
  "✓ generating routes (8/8)",
  "✓ finalizing optimization",
  "✓ collecting page data",
  "",
  "route (pages)         size",
  "─────────────────────────",
  "○ /                    0 B",
  "○ /admin               2 kB",
  "○ /work/lemon          1 kB",
  "○ /work/photowall      1 kB",
  "○ /work/pitsillis      1 kB",
  "",
  "> deploy works.cy …",
  "> verifying ssl …",
  "> warming cdn …",
  "> testing edge nodes …",
  "> ready.",
  "",
  "[automate]  cleaning ops",
  "[build]     media moderation",
  "[hybrid]    menu + admin",
  "",
  "# uptime:    100%",
  "# response:  < 80ms",
  "# bugs:      0",
  "",
  "$ git log --oneline",
  "248af release v1.2.0",
  "9b1c2 feat: lemon ops",
  "4d703 feat: photowall",
  "5e918 chore: bump deps",
  "1a902 fix: route caching",
  "",
  "async function ship(plan) {",
  "  await deploy(plan);",
  "  await monitor();",
  "  return { ok: true };",
  "}",
  "",
  "const studio = {",
  "  name: 'works.cy',",
  "  city: 'limassol',",
  "  open: true,",
  "};",
  "",
  "function reject(plan) {",
  "  return { ok: false, plan };",
  "}",
  "",
  "// every line counts",
  "// quietly automating since 2014",
  "// — let's talk —",
  "// hello@works.cy",
  "// — end transmission —",
  "",
];

function CodeField() {
  const fieldRef = useRefCF(null);
  const tilesRef = useRefCF([]);
  const stateRef = useRefCF({ logoX: -9999, logoY: -9999, scrollY: 0 });
  const rafRef = useRefCF(null);

  useEffectCF(() => {
    const field = fieldRef.current;
    if (!field) return undefined;

    // ----- tile factory -----
    const colWidth = 178;
    const lineHeight = 19.6;
    const xPad = 28;
    const yPadTop = 92;   // clear the nav
    const yPadBottom = 32;

    const build = () => {
      // wipe existing
      while (field.firstChild) field.removeChild(field.firstChild);
      tilesRef.current = [];

      const vw = window.innerWidth;
      const vh = window.innerHeight;
      const cols = Math.max(2, Math.ceil((vw - xPad * 2) / colWidth));
      const rows = Math.max(8, Math.ceil((vh - yPadTop - yPadBottom) / lineHeight));

      const tiles = [];
      let lineIdx = 0;
      for (let c = 0; c < cols; c++) {
        // jitter each column's starting offset so the field doesn't look
        // like a perfect grid
        const colOffset = (c * 13) % ALL_LINES.length;
        for (let r = 0; r < rows; r++) {
          const text = ALL_LINES[(colOffset + r) % ALL_LINES.length];
          lineIdx++;
          const baseX = xPad + c * colWidth;
          const baseY = yPadTop + r * lineHeight;
          const el = document.createElement("div");
          el.className = "cf-line" + (ACCENT_RE.test(text) ? " accent" : "");
          el.textContent = text || "\u00a0";
          el.style.left = baseX + "px";
          el.style.top = baseY + "px";
          el.style.width = colWidth - 8 + "px";
          field.appendChild(el);
          tiles.push({
            el,
            baseX,
            baseY,
            // Use a "hot point" on the line that is closer to its visible
            // ink (start of line) so the water effect feels right even
            // for short strings.
            cx: baseX + 60,
            cy: baseY + lineHeight / 2,
            pushed: false,
          });
        }
      }
      tilesRef.current = tiles;
    };

    // ----- update loop -----
    const update = () => {
      rafRef.current = null;
      const { logoX, logoY, scrollY } = stateRef.current;

      // Field-wide parallax + fade
      const opacity = Math.max(0, 1 - scrollY / 720);
      field.style.transform =
        "translate3d(0, " + (-scrollY * 0.4).toFixed(1) + "px, 0)";
      field.style.opacity = String(opacity);

      // Convert logo screen Y to field-local Y by undoing the parallax
      const effLogoY = logoY + scrollY * 0.4;

      const R = 240;            // influence radius
      const R2 = R * R;
      const maxPush = 130;      // max displacement in px

      const tiles = tilesRef.current;
      for (let i = 0; i < tiles.length; i++) {
        const t = tiles[i];
        const dx = t.cx - logoX;
        const dy = t.cy - effLogoY;
        const d2 = dx * dx + dy * dy;

        if (d2 > R2 || d2 < 1) {
          if (t.pushed) {
            t.el.style.transform = "";
            t.pushed = false;
          }
          continue;
        }

        const d = Math.sqrt(d2);
        const u = 1 - d / R;
        // smoothstep falloff — feels watery, not springy
        const push = u * u * (3 - 2 * u) * maxPush;
        const nx = dx / d;
        const ny = dy / d;
        t.el.style.transform =
          "translate3d(" +
          (nx * push).toFixed(1) + "px," +
          (ny * push).toFixed(1) + "px,0)";
        t.pushed = true;
      }
    };

    const schedule = () => {
      if (rafRef.current != null) return;
      rafRef.current = requestAnimationFrame(update);
    };

    // ----- listeners -----
    const onResize = () => {
      build();
      schedule();
    };
    const onScroll = () => {
      stateRef.current.scrollY = window.scrollY;
      schedule();
    };
    const onLogoMove = (e) => {
      if (!e || !e.detail) return;
      stateRef.current.logoX = e.detail.x;
      stateRef.current.logoY = e.detail.y;
      schedule();
    };

    build();
    stateRef.current.scrollY = window.scrollY;
    schedule();

    window.addEventListener("resize", onResize);
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("worksLogoMove", onLogoMove);

    return () => {
      window.removeEventListener("resize", onResize);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("worksLogoMove", onLogoMove);
      if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
      tilesRef.current = [];
    };
  }, []);

  return <div ref={fieldRef} className="code-field" aria-hidden="true" />;
}

Object.assign(window, { CodeField });
