import * as redux from "react-redux";
import { mockState } from "../../mockState";
import * as api from "../../request/candidatesRequest";
import {
  filterCandidatesError,
  filterCandidatesSuccess,
  fetchCandidateOptionsSuccess,
  fetchCandidateOptionsError,
  fetchInitProcessError,
  fetchInitProcessSuccess,
} from "../../store/actions/candidates/candidatesActions";
import {
  fetchCandidateError,
  fetchCandidateSuccess,
  deleteCandidateError,
  deleteCandidateSuccess,
  createCandidateComment,
  createCandidateCommentError,
  createCandidateCommentSuccess,
} from "../../store/actions/candidate/candidateActions";
import { runSaga } from "redux-saga";
import {
  filterCandidates as k,
  getSingleCandidate,
  deleteSingleCandidate,
  getOptions,
  initializeProcess,
  addComment,
} from "../../store/saga/candidatesSaga";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

describe("candidatesSaga reducer tests", () => {
  let spyOnUseSelector;
  let spyOnUseDispatch;
  let mockDispatch;

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

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

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

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

  // filterCandidates

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

    api.getFilteredCandidates = jest.fn(() => Promise.resolve({}));

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

    await runSaga(fakeStore, k, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.getFilteredCandidates.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(filterCandidatesSuccess());
  });

  it("should call handleApiResponseSuccess inside filterCandidates saga function", async () => {
    const dispatchedActions = [];

    api.getFilteredCandidates = jest.fn(() => Promise.resolve({}));

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

    const mockFn = jest.fn();

    await runSaga(fakeStore, k, {
      payload: {
        data: {},
        handleApiResponseSuccess: mockFn,
      },
    }).done;

    expect(mockFn).toHaveBeenCalled();
  });

  it("should handle error inside filterCandidates saga function", async () => {
    const dispatchedActions = [];

    const error = { response: { data: { message: "Error" } } };
    helper.rejectErrorCodeHelper = jest.fn(() => "Error");
    api.getFilteredCandidates = jest.fn(() => Promise.reject(error));

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

    await runSaga(fakeStore, k, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.getFilteredCandidates.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(filterCandidatesError("Error"));
  });

  // getSingleCandidate

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

    api.getCandidate = jest.fn(() => Promise.resolve({}));

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

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

    expect(api.getCandidate.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(fetchCandidateSuccess());
  });

  it("should handle error inside getSingleCandidate saga function", async () => {
    const dispatchedActions = [];

    const error = { response: { data: { message: "Error" } } };
    helper.rejectErrorCodeHelper = jest.fn(() => "Error");
    api.getCandidate = jest.fn(() => Promise.reject(error));

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

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

    expect(api.getCandidate.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(fetchCandidateError("Error"));
  });

  // addComment

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

  //     api.createComment = jest.fn(() =>
  //       Promise.resolve({ payload: { user: {} } })
  //     );

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

  //     await runSaga(fakeStore, addComment, {
  //       payload: { user: {} },
  //     }).done;

  //     expect(api.createComment.mock.calls.length).toBe(1);
  //     expect(dispatchedActions).toContainEqual(createCandidateCommentSuccess());
  //   });

  it("should handle error inside getSingleCandidate saga function", async () => {
    const dispatchedActions = [];

    const error = { response: { data: { message: "Error" } } };
    helper.rejectErrorCodeHelper = jest.fn(() => "Error");
    api.createComment = jest.fn(() => Promise.reject(error));

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

    await runSaga(fakeStore, addComment, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.createComment.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(
      createCandidateCommentError("Error")
    );
  });

  // deleteSingleCandidate

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

    api.deleteCandidate = jest.fn(() => Promise.resolve({}));

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

    await runSaga(fakeStore, deleteSingleCandidate, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.deleteCandidate.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(deleteCandidateSuccess());
  });

  it("should call handleApiResponseSuccess inside filterCandidates saga function", async () => {
    const dispatchedActions = [];

    api.deleteCandidate = jest.fn(() => Promise.resolve({}));

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

    const mockFn = jest.fn();

    await runSaga(fakeStore, deleteSingleCandidate, {
      payload: {
        data: {},
        handleApiResponseSuccess: mockFn,
      },
    }).done;

    expect(mockFn).toHaveBeenCalled();
  });

  it("should handle error inside deleteSingleCandidate saga function", async () => {
    const dispatchedActions = [];

    const error = { response: { data: { message: "Error" } } };
    helper.rejectErrorCodeHelper = jest.fn(() => "Error");
    api.deleteCandidate = jest.fn(() => Promise.reject(error));

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

    await runSaga(fakeStore, deleteSingleCandidate, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.deleteCandidate.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(deleteCandidateError("Error"));
  });

  // getOptions

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

    api.getCandidateOptions = jest.fn(() => Promise.resolve({}));

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

    await runSaga(fakeStore, getOptions, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.getCandidateOptions.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(fetchCandidateOptionsSuccess());
  });

  it("should handle error inside getOptions saga function", async () => {
    const dispatchedActions = [];

    const error = { response: { data: { message: "Error" } } };
    helper.rejectErrorCodeHelper = jest.fn(() => "Error");
    api.getCandidateOptions = jest.fn(() => Promise.reject(error));

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

    await runSaga(fakeStore, getOptions, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.getCandidateOptions.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(
      fetchCandidateOptionsError("Error")
    );
  });

  // initializeProcess

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

    api.initializeProcessRequest = jest.fn(() => Promise.resolve({}));

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

    await runSaga(fakeStore, initializeProcess, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.initializeProcessRequest.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(fetchInitProcessSuccess());
  });

  it("should handle error inside initializeProcess saga function", async () => {
    const dispatchedActions = [];

    const error = { response: { data: { message: "Error" } } };
    helper.rejectErrorCodeHelper = jest.fn(() => "Error");
    api.initializeProcessRequest = jest.fn(() => Promise.reject(error));

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

    await runSaga(fakeStore, initializeProcess, {
      payload: {
        data: {},
      },
    }).done;

    expect(api.initializeProcessRequest.mock.calls.length).toBe(1);
    expect(dispatchedActions).toContainEqual(fetchInitProcessError("Error"));
  });
});
