import type { LabMetaV2 } from "@/components/labs/hooks-v2";
import { getFiberHook } from "@/testutils/fiber";
import {
  createServerlessLabMeta,
  createTestSelfManagedLabMeta,
  mockedGetLabs,
  mockLabsApi,
} from "@/testutils/mock-lab";
import {
  employeeTestUser,
  mockedGetMe,
  normalTestUser,
} from "@/testutils/mock-me-context";
import {
  mockedUseSearchParams,
  mockNextNavigationModule,
} from "@/testutils/mock-next-navigation";
import { ProvidersWrapper } from "@/testutils/providers";
import { enableActx, useActx } from "@/testutils/use-actx";
import { act, render } from "@testing-library/react";
import { ReadonlyURLSearchParams } from "next/navigation";
import { beforeEach, expect, suite, test } from "vitest";

mockNextNavigationModule();
mockLabsApi();
enableActx();

suite("<LabsLabsList />", () => {
  beforeEach(() => {
    mockedGetLabs.mockReturnValue(
      Promise.resolve([
        createServerlessLabMeta({
          title_en: "Foo Test",
          lab_code: "test_001",
          description_en: "Foo",
          category_en: ["Category 1", "Category 2"],
          delivery_type: "Instructor-Led",
          target_audience: ["DBA", "Architect"],
          splash_image_en: "about:blank",
        }),
        createTestSelfManagedLabMeta({
          title_en: "Bar Test",
          lab_code: "test_002",
          description_en: "Bar",
          category_en: "Category 1",
          delivery_type: "Self-Paced",
          target_audience: ["Developer", "DBA"],
          splash_image_en: "about:blank",
          employee_only: true,
        }),
      ] satisfies LabMetaV2[])
    );
  });

  test("Filters", async () => {
    const actx = useActx();

    const { LabsLabsList } = await import("./labs-list");

    mockedGetMe.mockReturnValue(employeeTestUser);

    const ui = render(<LabsLabsList initialLabs={await mockedGetLabs()} />, {
      wrapper: ProvidersWrapper,
    });

    await actx(() => {});

    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.getByText("Bar Test")).toBeVisible();

    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({ target: "Developer" }) as ReadonlyURLSearchParams
    );
    ui.rerender(<LabsLabsList initialLabs={await mockedGetLabs()} />);
    await actx(() => {});
    expect(ui.queryByText("Foo Test")).toBeNull();
    expect(ui.getByText("Bar Test")).toBeVisible();

    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({ category: "Category 2" }) as ReadonlyURLSearchParams
    );
    ui.rerender(<LabsLabsList initialLabs={await mockedGetLabs()} />);
    await actx(() => {});
    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.queryByText("Bar Test")).toBeNull();

    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({
        product: "TiDB Cloud Starter",
      }) as ReadonlyURLSearchParams
    );
    ui.rerender(<LabsLabsList initialLabs={await mockedGetLabs()} />);
    await actx(() => {});
    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.queryByText("Bar Test")).toBeNull();

    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({
        delivery_type: "Self-Paced",
      }) as ReadonlyURLSearchParams
    );
    ui.rerender(<LabsLabsList initialLabs={await mockedGetLabs()} />);
    await actx(() => {});
    expect(ui.queryByText("Foo Test")).toBeNull();
    expect(ui.getByText("Bar Test")).toBeVisible();
  });

  test("Employee only", async () => {
    const actx = useActx();

    const { LabsLabsList } = await import("./labs-list");

    mockedGetMe.mockReturnValue(normalTestUser);

    let ui = render(<LabsLabsList initialLabs={await mockedGetLabs()} />, {
      wrapper: ProvidersWrapper,
    });

    await actx(() => {});

    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.queryByText("Bar Test")).toBeNull();

    mockedGetMe.mockReturnValue(employeeTestUser);
    ui.unmount();
    ui = render(<LabsLabsList initialLabs={await mockedGetLabs()} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});
    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.getByText("Bar Test")).toBeVisible();

    ui.unmount();
    mockedGetMe.mockReturnValue(null);
    ui = render(<LabsLabsList initialLabs={await mockedGetLabs()} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});
    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.queryByText("Bar Test")).toBeNull();
  });

  test("Search", async () => {
    const actx = useActx();

    const { LabsLabsList } = await import("./labs-list");

    mockedGetMe.mockReturnValue(employeeTestUser);

    const ui = render(<LabsLabsList initialLabs={await mockedGetLabs()} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});

    act(() => {
      const hook = getFiberHook(ui, LabsLabsList, 6);
      hook.queue.dispatch("foo");
    });

    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.queryByText("Bar Test")).toBeNull();
  });

  test("Level Filters", async () => {
    const actx = useActx();

    const { LabsLabsList } = await import("./labs-list");

    // Override labs with explicit levels
    mockedGetLabs.mockReturnValue(
      Promise.resolve([
        createServerlessLabMeta({
          title_en: "Foo Test",
          lab_code: "test_001",
          description_en: "Foo",
          category_en: ["Category 1", "Category 2"],
          delivery_type: "Instructor-Led",
          target_audience: ["DBA", "Architect"],
          splash_image_en: "about:blank",
          lab_level: "Beginner",
        }),
        createTestSelfManagedLabMeta({
          title_en: "Bar Test",
          lab_code: "test_002",
          description_en: "Bar",
          category_en: "Category 1",
          delivery_type: "Self-Paced",
          target_audience: ["Developer", "DBA"],
          splash_image_en: "about:blank",
          employee_only: true,
          lab_level: "Advanced",
        }),
      ] satisfies LabMetaV2[])
    );

    mockedGetMe.mockReturnValue(employeeTestUser);

    const ui = render(<LabsLabsList initialLabs={await mockedGetLabs()} />, {
      wrapper: ProvidersWrapper,
    });

    await actx(() => {});

    // Default, both visible
    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.getByText("Bar Test")).toBeVisible();

    // Filter Beginner only
    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({ level_min: "0", level_max: "0" }) as ReadonlyURLSearchParams
    );
    ui.rerender(<LabsLabsList initialLabs={await mockedGetLabs()} />);
    await actx(() => {});
    expect(ui.getByText("Foo Test")).toBeVisible();
    expect(ui.queryByText("Bar Test")).toBeNull();

    // Filter Advanced only
    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({ level_min: "2", level_max: "2" }) as ReadonlyURLSearchParams
    );
    ui.rerender(<LabsLabsList initialLabs={await mockedGetLabs()} />);
    await actx(() => {});
    expect(ui.queryByText("Foo Test")).toBeNull();
    expect(ui.getByText("Bar Test")).toBeVisible();

    // Non-default filter hides labs without level
    mockedGetLabs.mockReturnValue(
      Promise.resolve([
        createServerlessLabMeta({ title_en: "No Level", lab_code: "test_003", lab_level: undefined }),
      ])
    );
    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({ level_min: "1", level_max: "1" }) as ReadonlyURLSearchParams
    );
    ui.rerender(<LabsLabsList initialLabs={await mockedGetLabs()} />);
    await actx(() => {});
    expect(ui.queryByText("No Level")).toBeNull();
  });
});
