// SuperFun - stupid easter eggs to mess with your coworkers :D

declare global {
    interface Window {
        SuperFun?: () => void;
        SuperFun2?: () => void;
        SuperFun3?: () => void;
        SuperFun4?: () => void;
        SuperFunAll?: () => void;
    }
}

interface SuperFunEgg {
    isActive(): boolean;
    enable(): void;
    disable(): void;
}

// New eggs just push themselves here and SuperFunAll() picks them up for free.
const superFunEggs: SuperFunEgg[] = [];

function superFunToggle(egg: SuperFunEgg) {
    if (egg.isActive()) egg.disable();
    else egg.enable();
}

// --- SuperFun: Flip the screen and text

let superFunActive = false;
// Maps each touched text node back to its untouched content so we can restore it.
const superFunOriginals = new Map<Text, string>();

function superFunReverse(input: string): string {
    return [...input].reverse().join("");
}

function superFunTextNodes(root: Node): Text[] {
    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
        acceptNode(node) {
            if (!node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;
            const tag = node.parentElement?.tagName;
            if (tag === "SCRIPT" || tag === "STYLE" || tag === "NOSCRIPT") return NodeFilter.FILTER_REJECT;
            return NodeFilter.FILTER_ACCEPT;
        },
    });

    const nodes: Text[] = [];
    let node: Node | null;
    while ((node = walker.nextNode())) nodes.push(node as Text);
    return nodes;
}

// The flip lives on <body> while the 3D tilt (SuperFun2) lives on <html>, so the
// two transforms stack instead of clobbering each other when both are running.
const superFunFlip: SuperFunEgg = {
    isActive: () => superFunActive,
    enable() {
        document.body.style.transform = "rotate(180deg)";
        for (const node of superFunTextNodes(document.body)) {
            superFunOriginals.set(node, node.nodeValue ?? "");
            node.nodeValue = superFunReverse(node.nodeValue ?? "");
        }
        superFunActive = true;
    },
    disable() {
        document.body.style.transform = "";
        for (const [node, original] of superFunOriginals) node.nodeValue = original;
        superFunOriginals.clear();
        superFunActive = false;
    },
};
superFunEggs.push(superFunFlip);

// --- SuperFun 2: Rotate viewport in 3d

let superFun2Active = false;
let superFun2Timer = 0;
const SUPER_FUN_2_INTERVAL = 1800;
const SUPER_FUN_2_MAX_TILT = 12; // degrees

function superFun2Angle(): number {
    return (Math.random() * 2 - 1) * SUPER_FUN_2_MAX_TILT;
}

function superFun2Tumble() {
    // perspective() inside the transform gives the page real depth as it tilts.
    document.documentElement.style.transform =
        `perspective(1200px) rotateX(${superFun2Angle().toFixed(2)}deg) ` +
        `rotateY(${superFun2Angle().toFixed(2)}deg) rotateZ(${superFun2Angle().toFixed(2)}deg)`;
}

const superFun3d: SuperFunEgg = {
    isActive: () => superFun2Active,
    enable() {
        document.documentElement.style.transition = `transform ${SUPER_FUN_2_INTERVAL}ms ease-in-out`;
        superFun2Tumble();
        superFun2Timer = window.setInterval(superFun2Tumble, SUPER_FUN_2_INTERVAL);
        superFun2Active = true;
    },
    disable() {
        window.clearInterval(superFun2Timer);
        superFun2Timer = 0;
        document.documentElement.style.transition = "";
        document.documentElement.style.transform = "";
        superFun2Active = false;
    },
};
superFunEggs.push(superFun3d);

// --- SuperFun3: freecam - fly around a 3D version of the page ---
// Treats <body> as a flat plane floating in 3D and drives a virtual camera over
// it. Mouse (pointer-locked) looks, WASD moves relative to where you're facing,
// Space/Shift go up/down. The page transform each frame is the inverse of the
// camera's pose, which is what makes it feel like you're the one moving.

let superFun3Active = false;
let superFun3Raf = 0;
let superFun3Hud: HTMLDivElement | null = null;
const superFun3Keys: Record<string, boolean> = {};
const superFun3Cam = { x: 0, y: 0, z: 0, yaw: 0, pitch: 0 };
const SUPER_FUN_3_SPEED = 14;        // px moved per frame while a key is held
const SUPER_FUN_3_SENS = 0.15;       // degrees of look per px of mouse movement
const SUPER_FUN_3_PERSPECTIVE = 1200;

function superFun3IsMoveKey(code: string): boolean {
    return code === "KeyW" || code === "KeyA" || code === "KeyS" || code === "KeyD"
        || code === "Space" || code === "ShiftLeft" || code === "ShiftRight";
}

function superFun3KeyDown(e: KeyboardEvent) {
    // F toggles camera control: grab the pointer for look/move, or release it
    // back to the cursor so you can actually click on the page.
    if (e.code === "KeyF") {
        e.preventDefault();
        if (document.pointerLockElement == null) document.body.requestPointerLock();
        else document.exitPointerLock();
        return;
    }

    // Only hijack movement keys while we hold camera control; otherwise let the
    // page handle them so the freed cursor can scroll/type/click normally.
    if (document.pointerLockElement == null) return;
    if (superFun3IsMoveKey(e.code)) e.preventDefault();
    superFun3Keys[e.code] = true;
}

function superFun3KeyUp(e: KeyboardEvent) {
    superFun3Keys[e.code] = false;
}

function superFun3MouseMove(e: MouseEvent) {
    if (document.pointerLockElement == null) return;
    superFun3Cam.yaw -= e.movementX * SUPER_FUN_3_SENS;
    superFun3Cam.pitch += e.movementY * SUPER_FUN_3_SENS;
    superFun3Cam.pitch = Math.max(-89, Math.min(89, superFun3Cam.pitch));
}

function superFun3UpdateHud() {
    if (!superFun3Hud) return;
    superFun3Hud.textContent = document.pointerLockElement != null
        ? "FREECAM  -  WASD move  ·  mouse looks  ·  Space / Shift up / down  ·  F releases the cursor  ·  SuperFun3() exits"
        : "FREECAM  -  press F to grab the camera  ·  cursor is free to click  ·  SuperFun3() exits";
}

function superFun3LockChange() {
    // Releasing control (via F or Esc) shouldn't leave movement keys stuck on.
    if (document.pointerLockElement == null) {
        for (const code in superFun3Keys) superFun3Keys[code] = false;
    }
    superFun3UpdateHud();
}

function superFun3Frame() {
    // A rotation-only matrix gives us the camera's facing/right vectors (w=0 so
    // the translation part is ignored) to move relative to where we're looking.
    const rot = new DOMMatrix();
    rot.rotateSelf(0, superFun3Cam.yaw, 0);
    rot.rotateSelf(superFun3Cam.pitch, 0, 0);
    const fwd = rot.transformPoint(new DOMPoint(0, 0, -1, 0));
    const right = rot.transformPoint(new DOMPoint(1, 0, 0, 0));

    const s = SUPER_FUN_3_SPEED;
    if (superFun3Keys["KeyW"]) { superFun3Cam.x += fwd.x * s; superFun3Cam.y += fwd.y * s; superFun3Cam.z += fwd.z * s; }
    if (superFun3Keys["KeyS"]) { superFun3Cam.x -= fwd.x * s; superFun3Cam.y -= fwd.y * s; superFun3Cam.z -= fwd.z * s; }
    if (superFun3Keys["KeyD"]) { superFun3Cam.x += right.x * s; superFun3Cam.y += right.y * s; superFun3Cam.z += right.z * s; }
    if (superFun3Keys["KeyA"]) { superFun3Cam.x -= right.x * s; superFun3Cam.y -= right.y * s; superFun3Cam.z -= right.z * s; }
    if (superFun3Keys["Space"]) superFun3Cam.y -= s; // y grows downward in CSS, so up = subtract
    if (superFun3Keys["ShiftLeft"] || superFun3Keys["ShiftRight"]) superFun3Cam.y += s;

    // World transform = inverse of the camera pose T(cam) * Ry(yaw) * Rx(pitch).
    const pose = new DOMMatrix();
    pose.translateSelf(superFun3Cam.x, superFun3Cam.y, superFun3Cam.z);
    pose.rotateSelf(0, superFun3Cam.yaw, 0);
    pose.rotateSelf(superFun3Cam.pitch, 0, 0);

    document.body.style.transform = `perspective(${SUPER_FUN_3_PERSPECTIVE}px) ${pose.inverse().toString()}`;
    superFun3Raf = window.requestAnimationFrame(superFun3Frame);
}

function superFun3MakeHud(): HTMLDivElement {
    const hud = document.createElement("div");
    hud.textContent =
        "FREECAM  -  press F to grab the camera  ·  cursor is free to click  ·  SuperFun3() exits";
    hud.style.cssText = [
        "position:fixed", "left:50%", "bottom:16px", "transform:translateX(-50%)",
        "z-index:2147483647", "padding:8px 14px", "border-radius:8px",
        "background:rgba(0,0,0,0.78)", "color:#fff", "font:13px/1.4 system-ui,sans-serif",
        "pointer-events:none", "white-space:nowrap",
    ].join(";");
    return hud;
}

const superFun3Freecam: SuperFunEgg = {
    isActive: () => superFun3Active,
    enable() {
        superFun3Cam.x = 0; superFun3Cam.y = 0; superFun3Cam.z = 0;
        superFun3Cam.yaw = 0; superFun3Cam.pitch = 0;

        // Pivot the world around the current viewport center, and kill scrolling
        // and per-frame transitions so the camera responds instantly.
        document.body.style.transition = "none";
        document.body.style.transformOrigin =
            `${window.scrollX + window.innerWidth / 2}px ${window.scrollY + window.innerHeight / 2}px`;
        document.documentElement.style.overflow = "hidden";

        // The HUD hangs off <html> so the body's 3D transform doesn't fly it away.
        superFun3Hud = superFun3MakeHud();
        document.documentElement.appendChild(superFun3Hud);

        window.addEventListener("keydown", superFun3KeyDown);
        window.addEventListener("keyup", superFun3KeyUp);
        window.addEventListener("mousemove", superFun3MouseMove);
        document.addEventListener("pointerlockchange", superFun3LockChange);

        superFun3Active = true;
        superFun3Frame();
    },
    disable() {
        window.cancelAnimationFrame(superFun3Raf);
        superFun3Raf = 0;

        window.removeEventListener("keydown", superFun3KeyDown);
        window.removeEventListener("keyup", superFun3KeyUp);
        window.removeEventListener("mousemove", superFun3MouseMove);
        document.removeEventListener("pointerlockchange", superFun3LockChange);
        for (const code in superFun3Keys) superFun3Keys[code] = false;

        if (document.pointerLockElement != null) document.exitPointerLock();
        superFun3Hud?.remove();
        superFun3Hud = null;

        document.body.style.transform = "";
        document.body.style.transition = "";
        document.body.style.transformOrigin = "";
        document.documentElement.style.overflow = "";
        superFun3Active = false;
    },
};
superFunEggs.push(superFun3Freecam);

// --- SuperFun4: endlessly cycle the whole page through messed-up colors ---
// Uses an animated CSS filter on <html>. filter is its own property, so this
// layers on top of any of the transform-based eggs without fighting them.

let superFun4Active = false;
let superFun4Style: HTMLStyleElement | null = null;

const superFun4Egg: SuperFunEgg = {
    isActive: () => superFun4Active,
    enable() {
        const style = document.createElement("style");
        style.textContent = `@keyframes superfun4-colors {
            0%   { filter: hue-rotate(0deg) saturate(1.6); }
            50%  { filter: hue-rotate(180deg) saturate(2.4) invert(0.15); }
            100% { filter: hue-rotate(360deg) saturate(1.6); }
        }`;
        document.head.appendChild(style);
        superFun4Style = style;
        document.documentElement.style.animation = "superfun4-colors 4s linear infinite";
        superFun4Active = true;
    },
    disable() {
        document.documentElement.style.animation = "";
        superFun4Style?.remove();
        superFun4Style = null;
        superFun4Active = false;
    },
};
superFunEggs.push(superFun4Egg);

// --- console entry points ---

window.SuperFun = () => superFunToggle(superFunFlip);
window.SuperFun2 = () => superFunToggle(superFun3d);
window.SuperFun3 = () => superFunToggle(superFun3Freecam);
window.SuperFun4 = () => superFunToggle(superFun4Egg);

// Marks this file as a module so the `declare global` Window augmentation above
// is honored. The bundler strips local exports, so nothing ships at runtime.
export {};
