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 { render } 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 {
  deleteStateUser,
  deleteUserError,
  setEnableUsers,
  setEnableUsersError,
  setUsers,
  setUsersError,
  toggleSingleUser,
} from "../../store/actions/users/usersActions";
import { deleteUser, enableUser, getUsers } from "../../store/saga/usersSaga";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";
describe("User management reducer tests", () => {
  const cont = (
    <redux.Provider store={store}>
      <Router history={history}>
        <UsersPage />
      </Router>
    </redux.Provider>
  );

  let spyOnUseSelector;
  let spyOnUseDispatch;
  let mockDispatch;

  beforeEach(() => {
    // Mock useSelector hook
    spyOnUseSelector = jest.spyOn(redux, "useSelector");
    spyOnUseSelector.mockReturnValueOnce(mockState.users);

    // 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 get users request when rendered", () => {
    render(cont);
    expect(mockDispatch).toHaveBeenCalledWith({
      type: FETCH_USERS_REQ,
    });
  });

  it("should handle users load errors in case of failure", async () => {
    const dispatchedActions = [];

    const error = {
      response: {
        data: { message: mockState.selections.fetchSelectionsErrorMessage },
      },
    };
    api.getAllUsers = jest.fn(() => Promise.reject(error));

    const fakeStore = {
      getState: () => mockState.users.users,
      dispatch: (action) => dispatchedActions.push(action),
    };

    await runSaga(fakeStore, getUsers).done;

    expect(api.getAllUsers.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(setUsersError(undefined));
  });

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

    api.getAllUsers = jest
      .fn()
      .mockResolvedValue({ data: mockState.users.users });

    const fakeStore = {
      getState: () => mockState.users,
      dispatch: (action) => dispatchedActions.push(action),
    };

    // wait for saga to complete
    await runSaga(fakeStore, getUsers).done;

    expect(api.getAllUsers.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(setUsers(mockState.users.users));
  });

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

    api.enableUserRequest = jest.fn(() => Promise.resolve(1));

    const fakeStore = {
      getState: () => mockState.users,
      dispatch: (action) => dispatchedActions.push(action),
    };

    const mock = jest.fn();

    await runSaga(fakeStore, enableUser, {
      payload: {
        id: 1,
        handleApiResponseSuccess: mock,
      },
    }).done;

    expect(api.enableUserRequest.mock.calls.length).toBe(1);
    expect(mock).toHaveBeenCalled();
    expect(dispatchedActions).toContainEqual(setEnableUsers());
    expect(dispatchedActions).toContainEqual(toggleSingleUser());
  });

  it("should handle enableUser saga errors", async () => {
    const dispatchedActions = [];
    helper.rejectErrorCodeHelper = jest.fn(() => "Server error");

    api.enableUserRequest = jest.fn(() =>
      Promise.reject({ response: { data: { message: "Server error" } } })
    );
    
    const fakeStore = {
      getState: () => mockState.users,
      dispatch: (action) => dispatchedActions.push(action),
    };

    await runSaga(fakeStore, enableUser, {
      payload: {
        id: 1,
      },
    }).done;

    expect(dispatchedActions).toContainEqual(setEnableUsersError("Server error"));
  });

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

    api.deleteUserRequest = jest.fn(() => Promise.resolve({ data: 1 }));

    const fakeStore = {
      getState: () => mockState.users,
      dispatch: (action) => dispatchedActions.push(action),
    };

    await runSaga(fakeStore, deleteUser, {
      payload: {
        id: 1,
      },
    }).done;

    expect(api.deleteUserRequest.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(deleteStateUser(1));
  });

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

    api.deleteUserRequest = jest.fn(() => Promise.resolve({ data: 1 }));

    const fakeStore = {
      getState: () => mockState.users,
      dispatch: (action) => dispatchedActions.push(action),
    };

    const mock = jest.fn();

    await runSaga(fakeStore, deleteUser, {
      payload: {
        id: 1,
        handleApiResponseSuccess: mock,
      },
    }).done;

    expect(api.deleteUserRequest.mock.calls.length).toBe(1);
    expect(mock).toHaveBeenCalled();
  });

  it("should handle error when deleteUser runs", async () => {
    const dispatchedActions = [];

    helper.rejectErrorCodeHelper = jest.fn(() => "Server error");

    api.deleteUserRequest = jest.fn(() =>
      Promise.reject({ response: { data: { message: "Server error" } } })
    );

    const fakeStore = {
      getState: () => mockState.users,
      dispatch: (action) => dispatchedActions.push(action),
    };

    await runSaga(fakeStore, deleteUser, {
      payload: {
        id: 1,
      },
    }).done;

    expect(dispatchedActions).toContainEqual(deleteUserError("Server error"));
  });
});
