import type { LabMetaV2 } from "@/components/labs/hooks-v2";
import type { LabMetaExtra } from "@/lib/api/labs";
import {
  mockedV2GetLabMeta,
  mockedV2GetLabParts,
  mockLabsApi,
} from "@/testutils/mock-lab";
import { mockedGetMe, normalTestUser } from "@/testutils/mock-me-context";
import {
  mockedAppRouterInstance,
  mockedUseSearchParams,
  mockNextNavigationModule,
} from "@/testutils/mock-next-navigation";
import { ProvidersWrapper } from "@/testutils/providers";
import { enableActx, useActx } from "@/testutils/use-actx";
import { render } from "@testing-library/react";
import { expect, suite, test } from "vitest";

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

suite("<LabPreviewV2>", () => {
  test("Steps Navigating", async () => {
    const actx = useActx();
    const { default: LabPreviewV2 } = await import("./lab-preview-v2");

    const meta: LabMetaV2 & LabMetaExtra = {
      authors: [],
      can_start: true,
      tidb_environments: ["TiDB Cloud Starter"],
      estimated_duration_minutes: 180,
      lab_code: "test_001",
      regions: { aws: ["singapore", "tokyo"] },
    } as Partial<LabMetaV2 & LabMetaExtra> as any as LabMetaV2 & LabMetaExtra;

    mockedUseSearchParams.mockReturnValue(
      new URLSearchParams({ step: "1" }) as any
    );
    mockedGetMe.mockReturnValue(normalTestUser);
    mockedV2GetLabMeta.mockReturnValue(Promise.resolve(meta));
    mockedV2GetLabParts.mockReturnValue(
      Promise.resolve([
        {
          title: "Guide",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
        {
          title: "Module 1",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
        {
          title: "Module 2",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
      ])
    );

    const ui = render(<LabPreviewV2 lab={meta} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(async () => {});

    await actx(async () => {
      ui.getByRole("button", { name: "Next" }).click();
    });
    expect(mockedAppRouterInstance.push).toHaveBeenCalledWith(
      "/lab?preview=test_001&step=2"
    );

    await actx(async () => {
      ui.getByRole("button", { name: "Previous" }).click();
    });
    expect(mockedAppRouterInstance.push).toHaveBeenCalledWith(
      "/lab?preview=test_001"
    );
  });

  test("Serverless Lab", async () => {
    const actx = useActx();
    const { default: LabPreviewV2 } = await import("./lab-preview-v2");

    const meta: LabMetaV2 & LabMetaExtra = {
      authors: [],
      can_start: true,
      tidb_environments: ["TiDB Cloud Starter"],
      estimated_duration_minutes: 180,
      lab_code: "test_001",
      regions: { aws: ["singapore", "tokyo"] },
    } as Partial<LabMetaV2 & LabMetaExtra> as any as LabMetaV2 & LabMetaExtra;

    mockedGetMe.mockReturnValue(normalTestUser);
    mockedV2GetLabMeta.mockReturnValue(Promise.resolve(meta));
    mockedV2GetLabParts.mockReturnValue(
      Promise.resolve([
        {
          title: "Guide",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
      ])
    );

    const ui = render(<LabPreviewV2 lab={meta} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});

    // Needs authorized to cloud first
    expect(ui.getByRole("button", { name: "Start" })).toBeDisabled();
  });

  test("Dedicated Lab", async () => {
    const actx = useActx();
    const { default: LabPreviewV2 } = await import("./lab-preview-v2");

    const meta: LabMetaV2 & LabMetaExtra = {
      authors: [],
      can_start: true,
      tidb_environments: ["TiDB Cloud Dedicated"],
      estimated_duration_minutes: 180,
      lab_code: "test_001",
      regions: { aws: ["singapore", "tokyo"] },
    } as Partial<LabMetaV2 & LabMetaExtra> as any as LabMetaV2 & LabMetaExtra;

    mockedGetMe.mockReturnValue(normalTestUser);
    mockedV2GetLabMeta.mockReturnValue(Promise.resolve(meta));
    mockedV2GetLabParts.mockReturnValue(
      Promise.resolve([
        {
          title: "Guide",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
      ])
    );

    const ui = render(<LabPreviewV2 lab={meta} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});

    expect(ui.getByRole("button", { name: "Start" })).not.toBeDisabled();
  });

  test("Self-Managed Lab", async () => {
    const actx = useActx();
    const { default: LabPreviewV2 } = await import("./lab-preview-v2");

    const meta: LabMetaV2 & LabMetaExtra = {
      authors: [],
      can_start: true,
      tidb_environments: ["TiDB Self-Managed"],
      estimated_duration_minutes: 180,
      lab_code: "test_001",
      tidb_versions: ["0.0.1", "0.0.2"],
      regions: { aws: ["tokyo"] },
    } as Partial<LabMetaV2 & LabMetaExtra> as any as LabMetaV2 & LabMetaExtra;

    mockedGetMe.mockReturnValue(normalTestUser);
    mockedV2GetLabMeta.mockReturnValue(Promise.resolve(meta));
    mockedV2GetLabParts.mockReturnValue(
      Promise.resolve([
        {
          title: "Guide",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
      ])
    );

    const ui = render(<LabPreviewV2 lab={meta} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});

    expect(ui.getByRole("button", { name: "Start" })).not.toBeDisabled();
  });

  test("Not Available", async () => {
    const actx = useActx();
    const { default: LabPreviewV2 } = await import("./lab-preview-v2");

    const meta: LabMetaV2 & LabMetaExtra = {
      authors: [],
      can_start: false,
      suggestion: "SUGGESTION",
      tidb_environments: ["TiDB Cloud Starter"],
      estimated_duration_minutes: 180,
      lab_code: "test_001",
      regions: { aws: ["singapore", "tokyo"] },
    } as Partial<LabMetaV2 & LabMetaExtra> as any as LabMetaV2 & LabMetaExtra;

    mockedGetMe.mockReturnValue(normalTestUser);
    mockedV2GetLabMeta.mockReturnValue(Promise.resolve(meta));
    mockedV2GetLabParts.mockReturnValue(
      Promise.resolve([
        {
          title: "Guide",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
      ])
    );

    const ui = render(<LabPreviewV2 lab={meta} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});

    expect(ui.queryByRole("button", { name: "Start" })).toBeNull();
    expect(
      ui.getByText("Lab is not available").nextElementSibling
    ).toHaveTextContent("SUGGESTION");
  });

  test("Need Redeem", async () => {
    const actx = useActx();
    const { default: LabPreviewV2 } = await import("./lab-preview-v2");

    const meta: LabMetaV2 & LabMetaExtra = {
      authors: [],
      can_start: false,
      suggestion: "SUGGESTION",
      credit_redeemable: true,
      credits: 5,
      tidb_environments: ["TiDB Cloud Starter"],
      estimated_duration_minutes: 180,
      lab_code: "test_001",
      regions: { aws: ["singapore", "tokyo"] },
    } as Partial<LabMetaV2 & LabMetaExtra> as any as LabMetaV2 & LabMetaExtra;

    mockedGetMe.mockReturnValue(normalTestUser);
    mockedV2GetLabMeta.mockReturnValue(Promise.resolve(meta));
    mockedV2GetLabParts.mockReturnValue(
      Promise.resolve([
        {
          title: "Guide",
          public: true,
          content: "Test",
          type: "markdown",
          file: "foo/bar.md",
        },
      ])
    );

    const ui = render(<LabPreviewV2 lab={meta} />, {
      wrapper: ProvidersWrapper,
    });
    await actx(() => {});

    expect(ui.queryByRole("button", { name: "Start" })).toBeDisabled();
    expect(ui.queryByRole("button", { name: "Redeem" })).not.toBeDisabled();

    await actx(() => {
      ui.getByRole("button", { name: "Redeem" }).click();
    });
    expect(ui.getByRole("alertdialog", { name: "Redeem" })).toBeVisible();
  });
});
