import { expectSaga } from "redux-saga-test-plan";

import * as matchers from "redux-saga-test-plan/matchers";
import * as Api from "../../../services/api";
import {
  v2RcsSagas,
  uploadRcsAsset,
  createRCSTemplate,
  editTemplate,
  getTemplateDetails,
} from "../sagas";
import {
  UPLOAD_RCS_ASSET_SUCCESS,
  UPLOAD_RCS_ASSET_FAILURE,
  CREATE_RCS_TEMPLATE_SUCCESS,
  CREATE_RCS_TEMPLATE_FAILURE,
  CLEAR_RCS_TEMPLATE_SUCCESS,
  CLEAR_RCS_TEMPLATE_FAILURE,
  EDIT_RCS_TEMPLATE_SUCCESS,
  EDIT_RCS_TEMPLATE_FAILURE,
  GET_RCS_TEMPLATE_DETAILS_SUCCESS,
  GET_RCS_TEMPLATE_DETAILS_FAILURE,
} from "../constants";

describe("rcs Sagas", () => {
  describe("v2RcsSagas Combined", () => {
    it.concurrent("should initialize all rcs-related watcher sagas without error", () => {
      return expectSaga(v2RcsSagas).run();
    });
  });

  describe("uploadRcsAsset saga", () => {
    it("handles success", () => {
      const file = "file";
      const assetType = "IMAGE";
      const fileParams = {};
      const index = 0;
      const asset = { url: "test" };
      const apiResult = { response: { asset }, status: { code: 200 } };
      return expectSaga(uploadRcsAsset, { file, assetType, fileParams, index })
        .provide([[matchers.call.fn(Api.uploadFile), apiResult]])
        .put({ type: UPLOAD_RCS_ASSET_SUCCESS, data: asset, index, statusCode: 200 })
        .returns(asset)
        .run();
    });

    it("handles invalid response shape and throws", async () => {
      const file = "file";
      const assetType = "IMAGE";
      const fileParams = {};
      const index = 0;
      const badResult = { response: {} }; // missing response.asset
      await expect(
        expectSaga(uploadRcsAsset, { file, assetType, fileParams, index })
          .provide([[matchers.call.fn(Api.uploadFile), badResult]])
          .put({ type: UPLOAD_RCS_ASSET_FAILURE, error: 'Invalid response format' })
          .run()
      ).rejects.toThrow('Invalid response format');
    });

    it("handles API error and rethrows", async () => {
      const file = "file";
      const assetType = "IMAGE";
      const fileParams = {};
      const index = 0;
      const apiError = new Error('boom');
      await expect(
        expectSaga(uploadRcsAsset, { file, assetType, fileParams, index })
          .provide([[matchers.call.fn(Api.uploadFile), Promise.reject(apiError)]])
          .put({ type: UPLOAD_RCS_ASSET_FAILURE, error: 'boom' })
          .run()
      ).rejects.toThrow('boom');
    });

    it("handles undefined index (defaults to 0)", () => {
      const file = "file";
      const assetType = "IMAGE";
      const fileParams = {};
      const asset = { url: "test" };
      const apiResult = { response: { asset }, status: { code: 200 } };
      return expectSaga(uploadRcsAsset, { file, assetType, fileParams })
        .provide([[matchers.call.fn(Api.uploadFile), apiResult]])
        .put({ type: UPLOAD_RCS_ASSET_SUCCESS, data: asset, index: 0, statusCode: 200 })
        .returns(asset)
        .run();
    });

    it("handles undefined status code (defaults to empty string)", () => {
      const file = "file";
      const assetType = "IMAGE";
      const fileParams = {};
      const index = 1;
      const asset = { url: "test" };
      const apiResult = { response: { asset } }; // no status
      return expectSaga(uploadRcsAsset, { file, assetType, fileParams, index })
        .provide([[matchers.call.fn(Api.uploadFile), apiResult]])
        .put({ type: UPLOAD_RCS_ASSET_SUCCESS, data: asset, index: 1, statusCode: '' })
        .returns(asset)
        .run();
    });

    it("handles error without message (defaults to 'Upload failed')", async () => {
      const file = "file";
      const assetType = "IMAGE";
      const fileParams = {};
      const index = 0;
      const apiError = new Error(); // no message
      apiError.message = undefined;
      await expect(
        expectSaga(uploadRcsAsset, { file, assetType, fileParams, index })
          .provide([[matchers.call.fn(Api.uploadFile), Promise.reject(apiError)]])
          .put({ type: UPLOAD_RCS_ASSET_FAILURE, error: 'Upload failed' })
          .run()
      ).rejects.toThrow();
    });

    it("handles success with status but no code (defaults to empty string)", () => {
      const file = "file";
      const assetType = "IMAGE";
      const fileParams = {};
      const index = 1;
      const asset = { url: "test" };
      const apiResult = { response: { asset }, status: {} }; // status exists but no code
      return expectSaga(uploadRcsAsset, { file, assetType, fileParams, index })
        .provide([[matchers.call.fn(Api.uploadFile), apiResult]])
        .put({ type: UPLOAD_RCS_ASSET_SUCCESS, data: asset, index: 1, statusCode: '' })
        .returns(asset)
        .run();
    });
  });

  describe("createRCSTemplate saga", () => {
    it("handles success", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { response: { id: 1 }, status: { code: 200 } };
      return expectSaga(createRCSTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_SUCCESS, data: { id: 1 }, statusCode: 200, errorMsg: undefined })
        .run();
    });
    it("handles failure", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { status: { code: 400 }, message: "fail" };
      return expectSaga(createRCSTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_FAILURE, error: "fail", errorMsg: "fail" })
        .run();
    });

    it("handles success without callback", () => {
      const template = {};
      const apiResult = { response: { id: 1 }, status: { code: 200 } };
      return expectSaga(createRCSTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_SUCCESS, data: { id: 1 }, statusCode: 200, errorMsg: undefined })
        .run();
    });

    it("handles failure without callback", () => {
      const template = {};
      const apiResult = { status: { code: 400 }, message: "fail" };
      return expectSaga(createRCSTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_FAILURE, error: "fail", errorMsg: "fail" })
        .run();
    });

    it("handles failure picking status.message when message is absent", () => {
      const template = {};
      const apiResult = { status: { code: 500, message: "Nested Msg" } };
      return expectSaga(createRCSTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_FAILURE, error: "Nested Msg", errorMsg: "Nested Msg" })
        .run();
    });

    it("handles failure picking result.status.code when others are absent", () => {
      const template = {};
      const apiResult = { status: { code: 500 } };
      return expectSaga(createRCSTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_FAILURE, error: "Error: 500", errorMsg: "Error: 500" })
        .run();
    });

    it("handles callback with errorMsg", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { status: { code: 500, message: "Nested Msg" } };
      return expectSaga(createRCSTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_FAILURE, error: "Nested Msg", errorMsg: "Nested Msg" })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(null, "Nested Msg");
        });
    });

    it("handles callback with error object when errorMsg is undefined", () => {
      const template = {};
      const callback = jest.fn();
      const apiError = new Error("Network Error");
      return expectSaga(createRCSTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), Promise.reject(apiError)]])
        .put({ type: CREATE_RCS_TEMPLATE_FAILURE, error: apiError, errorMsg: undefined })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(null, apiError);
        });
    });

    it("handles status code exactly 400 (boundary condition)", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { status: { code: 400 }, message: "Bad Request" };
      return expectSaga(createRCSTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_FAILURE, error: "Bad Request", errorMsg: "Bad Request" })
        .run();
    });

    it("handles success with status but no code (defaults to empty string)", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { response: { id: 1 }, status: {} }; // status exists but no code
      return expectSaga(createRCSTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: CREATE_RCS_TEMPLATE_SUCCESS, data: { id: 1 }, statusCode: '', errorMsg: undefined })
        .run();
    });
  });

  describe("editTemplate saga", () => {
    it("handles success", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { response: { id: 1 }, status: { code: 200 } };
      return expectSaga(editTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_SUCCESS, data: { id: 1 }, statusCode: 200, errorMsg: undefined })
        .run();
    });
    it("handles failure", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { status: { code: 400 }, message: "fail" };
      return expectSaga(editTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_FAILURE, error: "fail", errorMsg: "fail" })
        .run();
    });

    it("handles success without callback", () => {
      const template = {};
      const apiResult = { response: { id: 1 }, status: { code: 200 } };
      return expectSaga(editTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_SUCCESS, data: { id: 1 }, statusCode: 200, errorMsg: undefined })
        .run();
    });

    it("handles failure without callback", () => {
      const template = {};
      const apiResult = { status: { code: 400 }, message: "fail" };
      return expectSaga(editTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_FAILURE, error: "fail", errorMsg: "fail" })
        .run();
    });

    it("handles failure picking status.message when message is absent", () => {
      const template = {};
      const apiResult = { status: { code: 500, message: "Nested Msg" } };
      return expectSaga(editTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_FAILURE, error: "Nested Msg", errorMsg: "Nested Msg" })
        .run();
    });

    it("handles failure picking result.status.code when others are absent", () => {
      const template = {};
      const apiResult = { status: { code: 500 } };
      return expectSaga(editTemplate, { template })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_FAILURE, error: "Error: 500", errorMsg: "Error: 500" })
        .run();
    });

    it("handles callback with errorMsg", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { status: { code: 500, message: "Nested Msg" } };
      return expectSaga(editTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_FAILURE, error: "Nested Msg", errorMsg: "Nested Msg" })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(null, "Nested Msg");
        });
    });

    it("handles callback with error object when errorMsg is undefined", () => {
      const template = {};
      const callback = jest.fn();
      const apiError = new Error("Network Error");
      return expectSaga(editTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), Promise.reject(apiError)]])
        .put({ type: EDIT_RCS_TEMPLATE_FAILURE, error: apiError, errorMsg: undefined })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(null, apiError);
        });
    });

    it("handles status code exactly 400 (boundary condition)", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { status: { code: 400 }, message: "Bad Request" };
      return expectSaga(editTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_FAILURE, error: "Bad Request", errorMsg: "Bad Request" })
        .run();
    });

    it("handles success with status but no code (defaults to empty string)", () => {
      const template = {};
      const callback = jest.fn();
      const apiResult = { response: { id: 1 }, status: {} }; // status exists but no code
      return expectSaga(editTemplate, { template, callback })
        .provide([[matchers.call.fn(Api.createRcsTemplate), apiResult]])
        .put({ type: EDIT_RCS_TEMPLATE_SUCCESS, data: { id: 1 }, statusCode: '', errorMsg: undefined })
        .run();
    });
  });

  describe("getTemplateDetails saga", () => {
    it("handles success", () => {
      const id = "id";
      const callback = jest.fn();
      const apiResult = { response: { id: 1 } };
      return expectSaga(getTemplateDetails, { id, callback })
        .provide([[matchers.call.fn(Api.getTemplateDetails), apiResult]])
        .put({ type: GET_RCS_TEMPLATE_DETAILS_SUCCESS, data: { id: 1 } })
        .run();
    });
    it("handles failure", () => {
      const id = "id";
      const callback = jest.fn();
      const error = new Error("fail");
      return expectSaga(getTemplateDetails, { id, callback })
        .provide([[matchers.call.fn(Api.getTemplateDetails), Promise.reject(error)]])
        .put({ type: GET_RCS_TEMPLATE_DETAILS_FAILURE, error })
        .run();
    });

    it("handles success without callback", () => {
      const id = "id";
      const apiResult = { response: { id: 1 } };
      return expectSaga(getTemplateDetails, { id })
        .provide([[matchers.call.fn(Api.getTemplateDetails), apiResult]])
        .put({ type: GET_RCS_TEMPLATE_DETAILS_SUCCESS, data: { id: 1 } })
        .run();
    });
  });
});
