import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import UsersPage from "../../pages/UsersPage/UsersPage";
import * as api from "../../request/usersRequest";
import { runSaga } from "redux-saga";
import { FETCH_USERS_REQ } from "../../store/actions/users/usersActionConstants";
import { setUsersError } from "../../store/actions/users/usersActions";
import { getUsers, invite } from "../../store/saga/usersSaga";
import InviteDialog from "../../components/MUI/InviteDialog";
import * as userReqs from "../../store/actions/users/usersActions";
import {
  inviteUserSuccess,
  inviteUserError,
} from "../../store/actions/users/usersActions";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

const props = {
  title: "Any",
  subtitle: "Any",
  imgSrc: "Any",
  open: true,
  onClose: jest.fn(),
  maxWidth: "lg",
  fullWidth: true,
  responsive: true,
};

describe("invite dialog reducer tests onSuccess", () => {
  const cont = (
    <redux.Provider store={store}>
      <Router history={history}>
        <InviteDialog {...props} />
      </Router>
    </redux.Provider>
  );

  let spyOnUseSelector;
  let spyOnUseDispatch;
  let mockDispatch;
  let spyHelper;

  beforeEach(() => {
    // Mock useSelector hook
    spyOnUseSelector = jest.spyOn(redux, "useSelector");
    spyOnUseSelector.mockReturnValue({
      invite: {
        isSuccess: true,
        errorMessage: "",
      },
    });

    // Mock useDispatch hook
    spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

    // Mock dispatch function returned from useDispatch
    mockDispatch = jest.fn();
    spyOnUseDispatch.mockReturnValueOnce(mockDispatch);

    // Mock rejectErrorCodeHelper
    spyHelper = jest.spyOn(helper, "rejectErrorCodeHelper")
  });

  afterEach(() => {
    jest.restoreAllMocks();
  });

  it("Should call onClose if success", () => {
    render(cont);
    expect(props.onClose).toHaveBeenCalled();
  });
});

describe("invite reducer tests default", () => {
  const cont = (
    <redux.Provider store={store}>
      <Router history={history}>
        <InviteDialog {...props} />
      </Router>
    </redux.Provider>
  );

  let spyOnUseSelector;
  let spyOnUseDispatch;
  let mockDispatch;

  beforeEach(() => {
    // Mock useSelector hook
    spyOnUseSelector = jest.spyOn(redux, "useSelector");
    spyOnUseSelector.mockReturnValue({
      invite: {
        isSuccess: false,
        errorMessage: "",
      },
    });

    // Mock useDispatch hook
    spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

    // Mock dispatch function returned from useDispatch
    mockDispatch = jest.fn();
    spyOnUseDispatch.mockReturnValue(mockDispatch);
  });

  afterEach(() => {
    jest.restoreAllMocks();
  });

  it("Should dispatch invite action after submit", async () => {
    render(cont);
    // get inputs
    var input = screen.getAllByTestId("invite-input-text");

    // trigger onChange event handler
    fireEvent.change(input[0], { target: { value: "Firstname" } });
    fireEvent.change(input[1], { target: { value: "Lastname" } });
    fireEvent.change(input[2], { target: { value: "admin@dilig.net" } });

    // get submit button and fire click event
    var submitBtn = screen.getAllByRole("button")[0];
    fireEvent.click(submitBtn);

    // or get form and fire submit event
    // var form = screen.getByTestId("invite-form");
    // fireEvent.submit(form)

    // we need waitFor because we await formik to validate our form
    // another way to check form submission is if the submitHandler is
    // a prop
    await waitFor(() => expect(mockDispatch).toHaveBeenCalled());
  });

  it("should run inviteUser saga function with actions", async () => {
    const dispatchedActions = [];

    // we don't want to perform an actual api call in our tests
    // so we will mock the getAllUsers api with jest
    // this will mutate the dependency which we may reset if other tests
    // are dependent on it
    const mockedCall = { data: { message: "Link has been sent!" } };
    api.inviteUserRequest = jest.fn(() => Promise.resolve(mockedCall));

    const fakeStore = {
      getState: () => ({
        invite: {
          isSuccess: false,
          errorMessage: "",
        },
      }),
      dispatch: (action) => dispatchedActions.push(action),
    };

    // wait for saga to complete
    await runSaga(fakeStore, invite, {
      payload: {
        firstName: "st",
        lastName: "Test",
        email: "test@dilig.net",
      },
    }).done;

    expect(api.inviteUserRequest.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(inviteUserSuccess());
  });

  it("should run inviteUser saga function with actions and call response success", async () => {
    const dispatchedActions = [];

    // we don't want to perform an actual api call in our tests
    // so we will mock the getAllUsers api with jest
    // this will mutate the dependency which we may reset if other tests
    // are dependent on it
    const mockedCall = { data: { message: "Link has been sent!" } };
    api.inviteUserRequest = jest.fn(() => Promise.resolve(mockedCall));

    const fakeStore = {
      getState: () => ({
        invite: {
          isSuccess: false,
          errorMessage: "",
        },
      }),
      dispatch: (action) => dispatchedActions.push(action),
    };

    const mock = jest.fn()

    // wait for saga to complete
    await runSaga(fakeStore, invite, {
      payload: {
        invite:{
        firstName: "st",
        lastName: "Test",
        email: "test@dilig.net",},
        handleApiResponseSuccess: mock
      },
    }).done;

    expect(mock).toHaveBeenCalled();
  });

  it("should run inviteUser saga function with error actions in case of api error", async () => {
    const dispatchedActions = [];

    helper.rejectErrorCodeHelper = jest.fn(()=> 'Server error')

    // we don't want to perform an actual api call in our tests
    // so we will mock the getAllUsers api with jest
    // this will mutate the dependency which we may reset if other tests
    // are dependent on it
    const mockError = {
      response: {
        data: { message: "Server error" },
      },
    };
    api.inviteUserRequest = jest.fn(() => Promise.reject(mockError));

    const fakeStore = {
      getState: () => ({
        invite: {
          isSuccess: false,
          errorMessage: "",
        },
      }),
      dispatch: (action) => dispatchedActions.push(action),
    };

    // wait for saga to complete
    await runSaga(fakeStore, invite, {
      payload: {
        firstName: "st",
        lastName: "Test",
        email: "test@dilig.net",
      },
    }).done;

    expect(api.inviteUserRequest.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(
      inviteUserError("Server error")
    );
  });
});
