/**
 * 全局测试配置
 * 主要为后端测试提供基础环境
 * 组件测试使用各自的 test-utils.tsx
 */

// Set up jsdom for ALL tests (both backend and frontend)
import { JSDOM } from "jsdom";

const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
  pretendToBeVisual: true,
  url: "http://localhost",
});

globalThis.document = dom.window.document;
globalThis.window = dom.window as unknown as typeof globalThis & Window;
globalThis.navigator = dom.window.navigator;
globalThis.HTMLElement = dom.window.HTMLElement;
globalThis.Element = dom.window.Element;
globalThis.getComputedStyle = dom.window.getComputedStyle;

// Ensure document.body exists
if (!globalThis.document.body) {
  const body = globalThis.document.createElement("body");
  globalThis.document.documentElement.appendChild(body);
}

// CRITICAL: Set up polyfills BEFORE any other imports
// This ensures @testing-library/react sees these when it loads

// IE-style event handling polyfill (React fallback)
const nodeProto = dom.window.Node.prototype;
const elementProto = dom.window.Element.prototype;
const htmlElementProto = dom.window.HTMLElement.prototype;

const noop = () => undefined;

const attachEventFn = noop;
const detachEventFn = noop;

Object.defineProperty(nodeProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true });
Object.defineProperty(nodeProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true });
Object.defineProperty(elementProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true });
Object.defineProperty(elementProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true });
Object.defineProperty(htmlElementProto, "attachEvent", { configurable: true, value: attachEventFn, writable: true });
Object.defineProperty(htmlElementProto, "detachEvent", { configurable: true, value: detachEventFn, writable: true });

// Other polyfills
globalThis.ResizeObserver = class {
  disconnect() {
    return undefined;
  }

  observe() {
    return undefined;
  }

  unobserve() {
    return undefined;
  }
};

globalThis.MutationObserver = class {
  disconnect() {
    return undefined;
  }

  observe() {
    return undefined;
  }

  takeRecords() {
    return [];
  }

  unobserve() {
    return undefined;
  }
};

globalThis.IntersectionObserver = class {
  disconnect() {
    return undefined;
  }

  observe() {
    return undefined;
  }

  takeRecords() {
    return [];
  }

  unobserve() {
    return undefined;
  }
} as unknown as typeof IntersectionObserver;

globalThis.requestAnimationFrame = (cb: FrameRequestCallback) => setTimeout(cb, 16);
globalThis.cancelAnimationFrame = (id: number) => clearTimeout(id);

Object.defineProperty(dom.window, "matchMedia", {
  value: (query: string) => ({
    addEventListener: noop,
    addListener: noop,
    dispatchEvent: () => true,
    matches: false,
    media: query,
    onchange: null,
    removeEventListener: noop,
    removeListener: noop,
  }),
  writable: true,
});

dom.window.Element.prototype.scrollTo = noop;
dom.window.Element.prototype.scrollIntoView = noop;

Object.defineProperty(dom.window, "customElements", {
  value: {
    define: noop,
    get: () => undefined,
  },
  writable: true,
});

globalThis.customElements = dom.window.customElements;

// Mock @number-flow/react globally (custom elements not supported in jsdom)
import { afterEach, mock } from "bun:test";
import { createElement } from "react";

void mock.module("@number-flow/react", () => {
  const NumberFlow = () => createElement("span", { "data-testid": "number-flow" });
  const NumberFlowGroup = ({ children }: { children: unknown }) => children;
  return { default: NumberFlow, NumberFlowGroup };
});

afterEach(() => {
  document.body.innerHTML = "";
});
