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 * as api from "../../request/candidatesRequest";
import * as api2 from '../../request/usersRequest';
import { runSaga } from "redux-saga";
import { CANDIDATE_FETCH } from "../../store/actions/candidate/candidateActionConstants";
import { FETCH_USERS_REQ } from "../../store/actions/users/usersActionConstants";
import { getSingleCandidate } from "../../store/saga/candidatesSaga";
import {getUsers} from '../../store/saga/usersSaga'
import {
  fetchCandidateSuccess,
  fetchCandidateError,
} from "../../store/actions/candidate/candidateActions";
import {
  setUsers,
  setUsersError
} from "../../store/actions/users/usersActions";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";
import CandidateDetailsPage from "../../pages/CandidatesPage/CandidateDetailsPage";

const mockHistoryPush = jest.fn();

// mock param which we send as part of URL
jest.mock("react-router-dom", () => ({
  ...jest.requireActual("react-router-dom"),
  useHistory: () => ({
    push: mockHistoryPush,
  }),
  useParams: () => ({
    id: 1,
  }),
}));

describe("CandidateDetailsPage render tests", () => {
  var props = {
    history: {
      replace: jest.fn(),
      push: jest.fn(),
      location: {
        pathname: "",
      },
    },
  };
  const cont = (
    <redux.Provider store={store}>
      <Router history={history}>
        <CandidateDetailsPage {...props} />
      </Router>
    </redux.Provider>
  );

  let spyOnUseSelector;
  let spyOnUseDispatch;
  let mockDispatch;

  beforeEach(() => {
    spyOnUseSelector = jest.spyOn(redux, "useSelector");
    spyOnUseSelector
      .mockReturnValueOnce(mockState.users.users)
      .mockReturnValueOnce(mockState.users.user)
      .mockReturnValueOnce(mockState.candidate.candidate);

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

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

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

  it("Should dispatch fetch candidate request when rendered", () => {
    render(cont);
    expect(mockDispatch).toHaveBeenCalledWith({
      payload: { id: 1 },
      type: CANDIDATE_FETCH,
    });
  });

  it("Should dispatch fetch users request when rendered", () => {
    render(cont);
    expect(mockDispatch).toHaveBeenCalledWith({
      type: FETCH_USERS_REQ,
    });
  });

  it("should load and handle candidate in case of success", async () => {
    const dispatchedActions = [];

    const mockedCall = { data: mockState.candidate.candidate };
    api.getCandidate = jest.fn(() => Promise.resolve(mockedCall));

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

    await runSaga(fakeStore, getSingleCandidate, { payload: { id: 1 } }).done;
    expect(api.getCandidate.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(
      fetchCandidateSuccess(mockedCall.data)
    );
  });

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

    const mockedCall = { data: mockState.users.users };
    api2.getAllUsers = jest.fn(() => Promise.resolve(mockedCall));

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

    await runSaga(fakeStore, getUsers).done;
    expect(api2.getAllUsers.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(
      setUsers(mockedCall.data)
    );
  });

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

    helper.rejectErrorCodeHelper = jest.fn(
      () => mockState.candidate.fetchCandidateErrorMessage
    );

    const error = {
      response: {
        data: { message: mockState.candidate.fetchCandidateErrorMessage },
      },
    };
    api.getCandidate = jest.fn(() => Promise.reject(error));

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

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

    expect(api.getCandidate.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(
      fetchCandidateError(error.response.data.message)
    );
  });

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

    helper.rejectErrorCodeHelper = jest.fn(
      () => mockState.users.fetchUsersErrorMessage
    );

    const error = {
      response: {
        data: { message: mockState.users.fetchUsersErrorMessage },
      },
    };
    api2.getAllUsers = jest.fn(() => Promise.reject(error));

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

    await runSaga(fakeStore, getUsers).done;

    expect(api2.getAllUsers.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(
      setUsersError(error.response.data.message)
    );
  });
});
