import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render } from "@testing-library/react";
import { App, ConfigProvider } from "antd";
import { mock } from "bun:test";
import { createElement, StrictMode } from "react";
import { MemoryRouter } from "react-router";

import { ErrorBoundary } from "../../src/web/components/ErrorBoundary";

// Mock recharts BEFORE any component imports
void mock.module("recharts", () => ({
  Area: () => null,
  CartesianGrid: () => null,
  Line: () => null,
  LineChart: ({ children }: { children: unknown }) => children,
  ResponsiveContainer: ({ children }: { children: unknown }) => children,
  Tooltip: () => null,
  XAxis: () => null,
  YAxis: () => null,
}));

export interface RenderWithProvidersOptions {
  initialRoute?: string;
}

export function renderWithProviders(ui: React.ReactElement, options?: RenderWithProvidersOptions) {
  const queryClient = createTestQueryClient();
  const initialRoute = options?.initialRoute ?? "/";

  return render(
    createElement(
      StrictMode,
      null,
      createElement(
        ErrorBoundary,
        null,
        createElement(
          QueryClientProvider,
          { client: queryClient },
          createElement(
            MemoryRouter,
            { initialEntries: [initialRoute] },
            createElement(ConfigProvider, null, createElement(App, null, ui)),
          ),
        ),
      ),
    ),
  );
}

function createTestQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
        staleTime: 0,
      },
    },
  });
}

// Custom test helpers (替代 jest-dom matchers)
export const testHelpers = {
  toBeInTheDocument: (element: Element | null) => {
    const pass = element !== null && document.contains(element);
    return {
      message: () => (pass ? "Expected element not to be in document" : "Expected element to be in document"),
      pass,
    };
  },
  toHaveAttribute: (element: Element | null, attr: string, value?: string) => {
    const pass = value === undefined ? (element?.hasAttribute(attr) ?? false) : element?.getAttribute(attr) === value;
    return {
      message: () =>
        pass ? `Expected element not to have attribute "${attr}"` : `Expected element to have attribute "${attr}"`,
      pass,
    };
  },
  toHaveClass: (element: Element | null, className: string) => {
    const pass = element?.classList.contains(className) ?? false;
    return {
      message: () =>
        pass ? `Expected element not to have class "${className}"` : `Expected element to have class "${className}"`,
      pass,
    };
  },
  toHaveTextContent: (element: Element | null, text: RegExp | string) => {
    const content = element?.textContent ?? "";
    const pass = element !== null && (typeof text === "string" ? content.includes(text) : text.test(content));
    return {
      message: () => (pass ? `Expected element not to have text "${text}"` : `Expected element to have text "${text}"`),
      pass,
    };
  },
};
