/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import "../../../tests/web/test-utils";
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, test, vi } from "bun:test";

import { App } from "../../../src/web/app";
import { THEME_MEDIA_QUERY, THEME_PREFERENCE_STORAGE_KEY } from "../../../src/web/hooks/use-theme-preference";

function createDashboardResult(overrides = {}) {
  return {
    data: {
      summary: {
        down: 0,
        incidents: 0,
        lastCheckTime: "2025-01-15T10:00:00.000Z",
        total: 0,
        up: 0,
        window: {
          from: "2025-01-14T10:00:00.000Z",
          label: "24h",
          to: "2025-01-15T10:00:00.000Z",
        },
      },
      targets: [],
    },
    dataUpdatedAt: Date.now(),
    error: null,
    isFetching: false,
    isLoading: false,
    refetch: vi.fn(),
    ...overrides,
  };
}

function installMatchMedia(initialMatches: boolean) {
  const originalMatchMedia = window.matchMedia;
  let matches = initialMatches;
  const listeners = new Set<(event: MediaQueryListEvent) => void>();
  const mediaQueryList = {
    addEventListener: (_type: string, listener: (event: MediaQueryListEvent) => void) => listeners.add(listener),
    addListener: (listener: (event: MediaQueryListEvent) => void) => listeners.add(listener),
    dispatchEvent: () => true,
    get matches() {
      return matches;
    },
    media: THEME_MEDIA_QUERY,
    onchange: null,
    removeEventListener: (_type: string, listener: (event: MediaQueryListEvent) => void) => listeners.delete(listener),
    removeListener: (listener: (event: MediaQueryListEvent) => void) => listeners.delete(listener),
  } as MediaQueryList;

  window.matchMedia = () => mediaQueryList;

  return {
    restore: () => {
      window.matchMedia = originalMatchMedia;
    },
    setMatches: (nextMatches: boolean) => {
      matches = nextMatches;
      listeners.forEach((listener) => listener({ matches, media: THEME_MEDIA_QUERY } as MediaQueryListEvent));
    },
  };
}

// Mock hooks
void vi.mock("../../../src/web/hooks/use-queries", () => ({
  useDashboard: vi.fn(() => createDashboardResult()),
  useMeta: vi.fn(() => ({
    data: { checkerTypes: ["http", "cmd"] },
  })),
}));

void vi.mock("../../../src/web/hooks/use-target-detail", () => ({
  useTargetDetail: vi.fn(() => ({
    activeTab: "overview",
    closeDrawer: vi.fn(),
    handlePageChange: vi.fn(),
    handleTabChange: vi.fn(),
    handleTimeChange: vi.fn(),
    historyData: {
      items: [],
      page: 1,
      pageSize: 20,
      total: 0,
    },
    historyLoading: false,
    metricsData: null,
    metricsLoading: false,
    openDrawer: vi.fn(),
    selectedTarget: null,
    timeFrom: "",
    timeTo: "",
  })),
}));

describe("App", () => {
  let matchMediaController: ReturnType<typeof installMatchMedia>;

  beforeEach(() => {
    const { useDashboard } = require("../../../src/web/hooks/use-queries");
    useDashboard.mockReturnValue(createDashboardResult());
    window.localStorage.clear();
    document.documentElement.removeAttribute("theme-mode");
    matchMediaController = installMatchMedia(false);
  });

  afterEach(() => {
    matchMediaController?.restore();
  });

  test("渲染不崩溃", () => {
    const { container } = render(<App />);
    expect(container.firstChild).not.toBeNull();
  });

  test("loading 状态不崩溃", () => {
    const { useDashboard } = require("../../../src/web/hooks/use-queries");
    useDashboard.mockReturnValue(
      createDashboardResult({
        data: null,
        dataUpdatedAt: 0,
        error: null,
        isFetching: true,
        isLoading: true,
        refetch: vi.fn(),
      }),
    );

    const { container } = render(<App />);
    expect(container.firstChild).not.toBeNull();
  });

  test("错误状态不崩溃", () => {
    const { useDashboard } = require("../../../src/web/hooks/use-queries");
    useDashboard.mockReturnValue(
      createDashboardResult({
        data: null,
        dataUpdatedAt: 0,
        error: { message: "Network error" },
        isFetching: false,
        isLoading: false,
        refetch: vi.fn(),
      }),
    );

    const { container } = render(<App />);
    expect(container.firstChild).not.toBeNull();
  });

  test("有数据状态不崩溃", () => {
    const { useDashboard } = require("../../../src/web/hooks/use-queries");
    useDashboard.mockReturnValue(
      createDashboardResult({
        data: {
          summary: {
            down: 1,
            incidents: 0,
            lastCheckTime: "2025-01-15T10:00:00.000Z",
            total: 2,
            up: 1,
            window: {
              from: "2025-01-14T10:00:00.000Z",
              label: "24h",
              to: "2025-01-15T10:00:00.000Z",
            },
          },
          targets: [],
        },
        dataUpdatedAt: Date.now(),
        error: null,
        isFetching: false,
        isLoading: false,
        refetch: vi.fn(),
      }),
    );

    const { container } = render(<App />);
    expect(container.firstChild).not.toBeNull();
  });

  test("默认渲染主题模式选项并按系统亮色应用主题", async () => {
    render(<App />);
    expect(screen.getByText("系统")).not.toBeNull();
    expect(screen.getByText("明亮")).not.toBeNull();
    expect(screen.getByText("黑暗")).not.toBeNull();
    await waitFor(() => expect(document.documentElement.getAttribute("theme-mode")).toBe("light"));
  });

  test("切换黑暗模式后写入本地存储并应用主题", async () => {
    render(<App />);
    fireEvent.click(screen.getByText("黑暗"));
    expect(window.localStorage.getItem(THEME_PREFERENCE_STORAGE_KEY)).toBe("dark");
    await waitFor(() => expect(document.documentElement.getAttribute("theme-mode")).toBe("dark"));
  });

  test("刷新后恢复已保存的主题偏好", async () => {
    window.localStorage.setItem(THEME_PREFERENCE_STORAGE_KEY, "dark");
    render(<App />);
    await waitFor(() => expect(document.documentElement.getAttribute("theme-mode")).toBe("dark"));
  });

  test("系统模式响应 matchMedia 变化", async () => {
    render(<App />);
    await waitFor(() => expect(document.documentElement.getAttribute("theme-mode")).toBe("light"));
    act(() => matchMediaController.setMatches(true));
    await waitFor(() => expect(document.documentElement.getAttribute("theme-mode")).toBe("dark"));
  });
});
