import userEvent from "@testing-library/user-event";
import fetchMock from "fetch-mock";

import {
  setupAdhocQueryMetadataEndpoint,
  setupCardQueryMetadataEndpoint,
  setupCardsEndpoints,
  setupCollectionsEndpoints,
  setupDatabasesEndpoints,
  setupRecentViewsEndpoints,
} from "__support__/server-mocks";
import {
  mockGetBoundingClientRect,
  mockScrollBy,
  renderWithProviders,
  screen,
  waitFor,
} from "__support__/ui";
import { ROOT_COLLECTION } from "metabase/entities/collections";
import type { GroupTableAccessPolicy } from "metabase-types/api";
import {
  createMockCard,
  createMockCardQueryMetadata,
  createMockCollection,
} from "metabase-types/api/mocks";
import {
  createSampleDatabase,
  PEOPLE,
  PEOPLE_ID,
  SAMPLE_DB_ID,
} from "metabase-types/api/mocks/presets";

import EditSandboxingModal from "./EditSandboxingModal";

const attributes = ["foo", "bar"];
const params = {
  groupId: "1",
  tableId: String(PEOPLE_ID),
};

const EDITABLE_ROOT_COLLECTION = createMockCollection({
  ...ROOT_COLLECTION,
  can_write: true,
});

const TEST_CARD = createMockCard({
  id: 1,
  name: "sandbox question",
  can_write: true,
  collection_id: null,
  dataset_query: {
    type: "query",
    database: SAMPLE_DB_ID,
    query: {
      "source-table": PEOPLE_ID,
    },
  },
});

const setup = ({
  shouldMockQuestions = false,
  policy = undefined,
}: {
  shouldMockQuestions?: boolean;
  policy?: GroupTableAccessPolicy;
} = {}) => {
  mockGetBoundingClientRect();
  mockScrollBy();
  const database = createSampleDatabase();

  setupDatabasesEndpoints([database]);
  setupCollectionsEndpoints({
    collections: [EDITABLE_ROOT_COLLECTION],
    rootCollection: EDITABLE_ROOT_COLLECTION,
  });
  setupRecentViewsEndpoints([]);
  setupAdhocQueryMetadataEndpoint(
    createMockCardQueryMetadata({ databases: [database] }),
  );

  fetchMock.post("path:/api/mt/gtap/validate", 204);
  fetchMock.get("path:/api/permissions/group/1", {});

  if (shouldMockQuestions) {
    fetchMock.get("path:/api/collection/root/items", {
      data: [{ id: TEST_CARD.id, name: TEST_CARD.name, model: "card" }],
    });
    fetchMock.get("path:/api/collection/1/items", {
      data: [],
    });
    fetchMock.get("path:/api/collection/1", EDITABLE_ROOT_COLLECTION);
    setupCardsEndpoints([TEST_CARD]);
    setupCardQueryMetadataEndpoint(
      TEST_CARD,
      createMockCardQueryMetadata({
        databases: [database],
      }),
    );
  }

  const onSave = jest.fn();

  renderWithProviders(
    <EditSandboxingModal
      onCancel={jest.fn()}
      onSave={onSave}
      attributes={attributes}
      params={params}
      policy={policy}
    />,
  );

  return { onSave };
};

describe("EditSandboxingModal", () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  describe("EditSandboxingModal", () => {
    describe("creating new policy", () => {
      it("should allow creating a new policy", async () => {
        const { onSave } = setup();

        expect(
          screen.getByText("Restrict access to this table"),
        ).toBeInTheDocument();

        expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();

        await userEvent.click(await screen.findByText("Pick a column"));
        await userEvent.click(await screen.findByText("ID"));

        await userEvent.click(screen.getByText("Pick a user attribute"));
        await userEvent.click(await screen.findByText("foo"));

        await userEvent.click(screen.getByText("Save"));

        await waitFor(() =>
          expect(onSave).toHaveBeenCalledWith({
            attribute_remappings: {
              foo: [
                "dimension",
                ["field", PEOPLE.ID, { "base-type": "type/BigInteger" }],
              ],
            },
            card_id: null,
            group_id: 1,
            table_id: PEOPLE_ID,
          }),
        );
      });

      it("should allow creating a new policy based on a card", async () => {
        const { onSave } = setup({ shouldMockQuestions: true });

        expect(
          screen.getByText("Restrict access to this table"),
        ).toBeInTheDocument();

        expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();

        await userEvent.click(
          screen.getByText(
            "Use a saved question to create a custom view for this table",
          ),
        );

        await userEvent.click(await screen.findByText("Select a question"));
        await screen.findByTestId("entity-picker-modal");
        await userEvent.click(
          await screen.findByRole("button", { name: /sandbox question/i }),
        );

        await userEvent.click(screen.getByText("Save"));

        await waitFor(() => {
          expect(screen.queryByText("Saving...")).not.toBeInTheDocument();
        });

        expect(onSave).toHaveBeenCalledWith({
          attribute_remappings: {},
          card_id: 1,
          group_id: 1,
          table_id: PEOPLE_ID,
        });
      });
    });
  });

  describe("editing policies", () => {
    it("should allow editing an existing policy", async () => {
      const { onSave } = setup({
        shouldMockQuestions: true,
        policy: {
          id: 1,
          table_id: 1,
          group_id: 1,
          card_id: null,
          permission_id: 50,
          attribute_remappings: {
            foo: ["dimension", ["field", 13, null]],
          },
        },
      });

      expect(
        screen.getByText("Restrict access to this table"),
      ).toBeInTheDocument();

      expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();

      await userEvent.click(
        screen.getByText(
          "Use a saved question to create a custom view for this table",
        ),
      );

      await userEvent.click(await screen.findByText("Select a question"));
      await screen.findByTestId("entity-picker-modal");
      await userEvent.click(
        await screen.findByRole("button", { name: /sandbox question/i }),
      );

      await userEvent.click(screen.getByText("Save"));

      await waitFor(() => {
        expect(screen.queryByText("Saving...")).not.toBeInTheDocument();
      });

      expect(onSave).toHaveBeenCalledWith({
        id: 1,
        attribute_remappings: {
          foo: ["dimension", ["field", 13, null]],
        },
        permission_id: 50,
        card_id: 1,
        group_id: 1,
        table_id: 1,
      });
    });
  });
});