const KEYBOARD_MAP = {
  ArrowUp: "up",
  ArrowDown: "down",
  ArrowLeft: "left",
  ArrowRight: "right",
  KeyY: "y",
  KeyX: "clear",
  KeyQ: "l1",
  KeyE: "r1",
  KeyZ: "l2",
  KeyC: "r2",
  KeyG: "guide",
  Enter: "accept",
  Escape: "back",
  Backspace: "back",
};

const BUTTON_MAP = {
  0: "accept",
  1: "back",
  2: "clear",
  3: "y",
  8: "menu",
  9: "menu",
  4: "l1",
  5: "r1",
  6: "l2",
  7: "r2",
  12: "up",
  13: "down",
  14: "left",
  15: "right",
};

const createFallbackEmitter = () => {
  const listeners = new Set();
  return {
    emit: (payload) => listeners.forEach((listener) => listener(payload)),
    on: (listener) => {
      listeners.add(listener);
      return () => listeners.delete(listener);
    },
  };
};

export const createInputManager = ({ onAction, actions }) => {
  const heldButtons = new Set();
  const heldVirtual = new Set();
  let rafId = 0;
  let mapper = null;
  let unsubscribeMapper = null;
  let axisLatched = false;
  let lastAxisEmitAt = 0;
  let active = false;

  const emitter = createFallbackEmitter();

  const emitAction = (action) => {
    if (!actions.includes(action)) {
      return;
    }
    onAction(action);
    emitter.emit(action);
  };

  const mapKeyboard = async () => {
    try {
      const coreInput = await import("@nebulaproject/core/input");
      if (typeof coreInput.createActionMapper !== "function") {
        return;
      }

      mapper = coreInput.createActionMapper({
        bindings: {
          up: [
            { source: "keyboard", control: "ArrowUp" },
            { source: "gamepad", control: "dpad-up" },
            { source: "gamepad", control: "axis-up" },
          ],
          down: [
            { source: "keyboard", control: "ArrowDown" },
            { source: "gamepad", control: "dpad-down" },
            { source: "gamepad", control: "axis-down" },
          ],
          left: [
            { source: "keyboard", control: "ArrowLeft" },
            { source: "gamepad", control: "dpad-left" },
            { source: "gamepad", control: "axis-left" },
          ],
          right: [
            { source: "keyboard", control: "ArrowRight" },
            { source: "gamepad", control: "dpad-right" },
            { source: "gamepad", control: "axis-right" },
          ],
          accept: [
            { source: "keyboard", control: "Enter" },
            { source: "gamepad", control: "a" },
          ],
          back: [
            { source: "keyboard", control: "Escape" },
            { source: "keyboard", control: "Backspace" },
            { source: "gamepad", control: "b" },
          ],
          menu: [
            { source: "keyboard", control: "KeyM" },
            { source: "gamepad", control: "start" },
          ],
          guide: [
            { source: "keyboard", control: "KeyG" },
          ],
          clear: [
            { source: "keyboard", control: "KeyX" },
            { source: "gamepad", control: "x" },
          ],
          y: [
            { source: "keyboard", control: "KeyY" },
            { source: "gamepad", control: "y" },
          ],
          l1: [{ source: "gamepad", control: "lb" }],
          r1: [{ source: "gamepad", control: "rb" }],
          l2: [
            { source: "keyboard", control: "KeyZ" },
            { source: "gamepad", control: "lt" },
          ],
          r2: [
            { source: "keyboard", control: "KeyC" },
            { source: "gamepad", control: "rt" },
          ],
        },
      });

      unsubscribeMapper = mapper.onAction((update) => {
        if (update?.active && update.action) {
          emitAction(update.action);
        }
      });
    } catch (_error) {
      mapper = null;
    }
  };

  const mapEventToMapper = (event) => {
    mapper?.mapEvent?.(event);
  };

  const onKeyDown = (event) => {
    const action =
      KEYBOARD_MAP[event.code] ??
      (event.code === "KeyM" ? "menu" : event.code === "KeyG" ? "guide" : null);
    if (!action) {
      return;
    }
    event.preventDefault();

    if (mapper) {
      mapEventToMapper({
        source: "keyboard",
        control: event.code,
        type: "pressed",
        value: 1,
      });
    } else if (!heldVirtual.has(event.code)) {
      heldVirtual.add(event.code);
      emitAction(action);
    }
  };

  const onKeyUp = (event) => {
    if (mapper) {
      mapEventToMapper({
        source: "keyboard",
        control: event.code,
        type: "released",
        value: 0,
      });
    }
    heldVirtual.delete(event.code);
  };

  const processAxis = (axisX, axisY) => {
    const threshold = 0.6;
    const now = performance.now();
    let axisAction = null;

    if (Math.abs(axisX) > Math.abs(axisY)) {
      if (axisX <= -threshold) axisAction = "left";
      if (axisX >= threshold) axisAction = "right";
    } else {
      if (axisY <= -threshold) axisAction = "up";
      if (axisY >= threshold) axisAction = "down";
    }

    if (!axisAction) {
      axisLatched = false;
      return;
    }

    if (axisLatched || now - lastAxisEmitAt < 120) {
      return;
    }

    if (mapper) {
      mapEventToMapper({
        source: "gamepad",
        control: `axis-${axisAction}`,
        type: "axis",
        value: 1,
      });
    } else {
      emitAction(axisAction);
    }

    axisLatched = true;
    lastAxisEmitAt = now;
  };

  const pollGamepad = () => {
    if (!active) {
      return;
    }

    const [pad] = navigator.getGamepads?.() ?? [];
    if (pad) {
      pad.buttons.forEach((button, index) => {
        const action = BUTTON_MAP[index];
        if (!action) {
          return;
        }

        if (button.pressed && !heldButtons.has(index)) {
          heldButtons.add(index);

          if (mapper) {
            const controlMap = {
              accept: "a",
              back: "b",
              clear: "x",
              y: "y",
              menu: "start",
              l1: "lb",
              r1: "rb",
              l2: "lt",
              r2: "rt",
              up: "dpad-up",
              down: "dpad-down",
              left: "dpad-left",
              right: "dpad-right",
            };

            mapEventToMapper({
              source: "gamepad",
              control: controlMap[action] ?? action,
              type: "pressed",
              value: 1,
            });
          } else {
            emitAction(action);
          }
        }

        if (!button.pressed && heldButtons.has(index)) {
          heldButtons.delete(index);
          if (mapper) {
            const releaseControlMap = {
              accept: "a",
              back: "b",
              clear: "x",
              y: "y",
              menu: "start",
              l1: "lb",
              r1: "rb",
              l2: "lt",
              r2: "rt",
              up: "dpad-up",
              down: "dpad-down",
              left: "dpad-left",
              right: "dpad-right",
            };

            mapEventToMapper({
              source: "gamepad",
              control: releaseControlMap[action] ?? action,
              type: "released",
              value: 0,
            });
          }
        }
      });

      processAxis(pad.axes?.[0] ?? 0, pad.axes?.[1] ?? 0);
    }

    rafId = requestAnimationFrame(pollGamepad);
  };

  const start = async () => {
    if (active) {
      return;
    }
    active = true;
    await mapKeyboard();
    window.addEventListener("keydown", onKeyDown);
    window.addEventListener("keyup", onKeyUp);
    rafId = requestAnimationFrame(pollGamepad);
  };

  const stop = () => {
    active = false;
    window.removeEventListener("keydown", onKeyDown);
    window.removeEventListener("keyup", onKeyUp);
    if (rafId) {
      cancelAnimationFrame(rafId);
      rafId = 0;
    }
    unsubscribeMapper?.();
    unsubscribeMapper = null;
  };

  return {
    start,
    stop,
    onAction: emitter.on,
  };
};
