import { expectSaga, testSaga } from "redux-saga-test-plan";
import * as matchers from "redux-saga-test-plan/matchers";
import { takeLatest, call } from "redux-saga/effects";
import { getMetaTagsDetails, watchGetMetaTagsDetails, v2WhatsappSagas, sendForApprovalCreate, uploadWhatsappAsset } from "../sagas";
import {
  URL_META_TAGS_REQUEST,
  URL_META_TAGS_SUCCESS,
  URL_META_TAGS_FAILURE,
  TEMPLATE_CREATE_SUCCESS,
  TEMPLATE_CREATE_FAILURE,
  UPLOAD_WHATSAPP_ASSET_REQUEST,
  UPLOAD_WHATSAPP_ASSET_SUCCESS,
  UPLOAD_WHATSAPP_ASSET_FAILURE,
  UPLOAD_WHATSAPP_ASSET_PROCESSING,
  UPLOAD_WHATSAPP_ASSET_FAILED,
} from "../constants";
import * as Api from "../../../services/api";
import { pollAssetStatus } from "../../../sagas/assetPolling";
import { createPollingConfig } from "../../../utils/asyncAssetUpload";
import { mockData } from "./mockData";

describe("v2WhatsappSagas Combined", () => {
  it("should initialize all v2WhatsappSagas-related watcher sagas without error", () => {
    return expectSaga(v2WhatsappSagas).run();
  });
});
describe("Whatsapp saga", () => {
  const error = new Error("error");
  const callBack = jest.fn();
  describe("whatsapp test saga", () => {
    it("handle success response from getMetaTagsDetails", () => {
      expectSaga(getMetaTagsDetails, {
        previewUrl: "https://capillarytech.com",
        callBack,
      }).provide([
        [
          matchers.call.fn(Api.getMetaTags),
          {
            success: true,
            response: [],
          },
        ],
      ])
        .put({
          type: URL_META_TAGS_SUCCESS,
          data: [],
        })
        .run();
    });

    it("handle success false response from getMetaTagsDetails", () => {
      expectSaga(getMetaTagsDetails, {
        previewUrl: "https://capillarytech.com",
        callBack,
      }).provide([
        [
          matchers.call.fn(Api.getMetaTags),
          {
            success: false,
            result: [],
            message: 'error',
          },
        ],
      ])
        .put({
          type: URL_META_TAGS_FAILURE,
          error: 'error',
        })
        .run();
    });

    it("handle success false response from getMetaTagsDetails when callback not present", () => {
      expectSaga(getMetaTagsDetails, {
        previewUrl: "https://capillarytech.com",
      }).provide([
        [
          matchers.call.fn(Api.getMetaTags),
          {
            success: false,
            result: [],
            message: 'error',
          },
        ],
      ])
        .put({
          type: URL_META_TAGS_FAILURE,
          error: 'error',
        })
        .run();
    });

    it("handles error thrown", () => {
      testSaga(getMetaTagsDetails, { callBack }).next().throw(error).next().isDone();
    });
  });

  describe("watchGetMetaTagsDetails saga", () => {
    const generator = watchGetMetaTagsDetails();
    it("should call watchers functions", async () => {
      expect(generator.next().value).toEqual(
        takeLatest(URL_META_TAGS_REQUEST, getMetaTagsDetails)
      );
    });
  });

  describe("whatsapp test saga for sendForApprovalCreate", () => {
    // const callBack = jest.fn();
    it("handle success response from sendForApprovalCreate", () => {
      expectSaga(sendForApprovalCreate, {
        payload: mockData.sendForApprovalCreatePayload,
        callBack,
      }).provide([
        [
          matchers.call.fn(Api.createWhatsappTemplate),
          {
            success: true,
            response: "Test data",
            status: { code: 200 },
          },
        ],
      ])
        .put({
          type: TEMPLATE_CREATE_SUCCESS,
          data: "Test data",
          statusCode: 200,
          errorMsg: undefined,
        })
        .run();
    });

    it("handle error response with json string for sendForApprovalCreate", () => {
      expectSaga(sendForApprovalCreate, {
        payload: mockData.sendForApprovalCreatePayload,
        callBack,
      }).provide([
        [
          matchers.call.fn(Api.createWhatsappTemplate),
          mockData.sendForApprovalCreateResult1,
        ],
      ])
        .put({
          type: TEMPLATE_CREATE_FAILURE,
          error: "(#192) Param components[1]['buttons'][0]['phone_number'] is not a valid phone number.",
          errorMsg: "(#192) Param components[1]['buttons'][0]['phone_number'] is not a valid phone number.",
        })
        .run();
    });

    it("handle error response for sendForApprovalCreate even if json parsing fails", () => {
      expectSaga(sendForApprovalCreate, {
        payload: mockData.sendForApprovalCreatePayload,
        callBack,
      }).provide([
        [
          matchers.call.fn(Api.createWhatsappTemplate),
          mockData.sendForApprovalCreateResult2,
        ],
      ])
        .put({
          type: TEMPLATE_CREATE_FAILURE,
          error: {
            name: 'StatusCodeError',
            statusCode: 500,
            message: '500 - Invalid params',
            error: { errorCode: '1004', errorMessage: 'Invalid params' }
          },
          errorMsg: {
            name: 'StatusCodeError',
            statusCode: 500,
            message: '500 - Invalid params',
            error: { errorCode: '1004', errorMessage: 'Invalid params' }
          },
        })
        .run();
    });

    it("handle error response for sendForApprovalCreate", () => {
      expectSaga(sendForApprovalCreate, {
        payload: mockData.sendForApprovalCreatePayload,
        callBack,
      }).provide([
        [
          matchers.call.fn(Api.createWhatsappTemplate),
          {
            success: false,
            status: {
              isError: true,
              code: 500,
            },
            message: 'error',
          },
        ],
      ])
        .put({
          type: TEMPLATE_CREATE_FAILURE,
          error: 'error',
          errorMsg: 'error',
        })
        .run();
    });

    it("handle error response for sendForApprovalCreate with status code if error message empty", () => {
      expectSaga(sendForApprovalCreate, {
        payload: mockData.sendForApprovalCreatePayload,
        callBack,
      }).provide([
        [
          matchers.call.fn(Api.createWhatsappTemplate),
          {
            success: false,
            status: {
              isError: true,
              code: 500,
            },
          },
        ],
      ])
        .put({
          type: TEMPLATE_CREATE_FAILURE,
          error: 500,
          errorMsg: 500,
        })
        .run();
    });

    it("handles error thrown", () => {
      testSaga(sendForApprovalCreate, { callBack }).next().throw(error).next().isDone();
    });
  });

  describe("uploadWhatsappAsset saga", () => {
    const mockAsset = {
      _id: "asset-123",
      type: "IMAGE",
      url: "https://example.com/image.jpg",
    };

    const mockParams = {
      file: new File(["test"], "test.jpg", { type: "image/jpeg" }),
      assetType: "image",
      fileParams: {},
      templateType: 0,
      whatsappParams: {},
    };

    it("should handle async upload flow (202 status code)", () => {
      const mockResponse = {
        status: { code: 202 },
        response: {
          assetId: "asset-123",
          asset: mockAsset,
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [
            matchers.call.fn(pollAssetStatus),
            undefined,
          ],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_PROCESSING,
          payload: {
            assetId: "asset-123",
            asset: mockAsset,
            processingStatus: "processing",
          },
        })
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run();
    });

    it("should handle async upload flow with processingStatus in response", () => {
      const mockResponse = {
        status: { code: 200 },
        response: {
          assetId: "asset-123",
          asset: mockAsset,
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [matchers.call.fn(pollAssetStatus), undefined],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_PROCESSING,
          payload: {
            assetId: "asset-123",
            asset: mockAsset,
            processingStatus: "processing",
          },
        })
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run();
    });

    it("should handle sync upload flow (201 status code)", () => {
      const mockResponse = {
        status: { code: 201 },
        response: {
          asset: mockAsset,
        },
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_SUCCESS,
          data: mockAsset,
          statusCode: 201,
          templateType: 0,
        })
        .not.call(pollAssetStatus)
        .run();
    });

    it("should handle sync upload flow (200 status code)", () => {
      const mockResponse = {
        status: { code: 200 },
        response: {
          asset: mockAsset,
        },
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_SUCCESS,
          data: mockAsset,
          statusCode: 200,
          templateType: 0,
        })
        .not.call(pollAssetStatus)
        .run();
    });

    it("should extract assetId from asset._id when assetId is not present", () => {
      const mockResponse = {
        status: { code: 202 },
        response: {
          asset: { ...mockAsset, _id: "asset-456" },
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [matchers.call.fn(pollAssetStatus), undefined],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_PROCESSING,
          payload: {
            assetId: "asset-456",
            asset: { ...mockAsset, _id: "asset-456" },
            processingStatus: "processing",
          },
        })
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run()
        .then((result) => {
          // Verify the polling config has the correct assetId
          // Find the pollAssetStatus call by checking function name
          const allEffects = result.allEffects || [];
          const pollCall = allEffects.find(effect => 
            effect && effect.CALL && effect.CALL.fn && effect.CALL.fn.name === 'pollAssetStatus'
          );
          expect(pollCall).toBeDefined();
          expect(pollCall.CALL.args[0]).toMatchObject({
            assetId: "asset-456",
          });
        });
    });

    it("should use assetType from params for polling", () => {
      const videoParams = {
        ...mockParams,
        assetType: "video",
      };
      const mockResponse = {
        status: { code: 202 },
        response: {
          assetId: "asset-123",
          asset: { ...mockAsset, type: "VIDEO" },
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, videoParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [matchers.call.fn(pollAssetStatus), undefined],
        ])
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run()
        .then((result) => {
          const allEffects = result.allEffects || [];
          const pollCall = allEffects.find(effect => 
            effect && effect.CALL && effect.CALL.fn && effect.CALL.fn.name === 'pollAssetStatus'
          );
          expect(pollCall).toBeDefined();
          expect(pollCall.CALL.args[0]).toMatchObject({
            type: "video",
          });
        });
    });

    it("should fallback to asset.type from response when assetType not in params", () => {
      const paramsWithoutAssetType = {
        ...mockParams,
        assetType: undefined,
      };
      const mockResponse = {
        status: { code: 202 },
        response: {
          assetId: "asset-123",
          asset: { ...mockAsset, type: "VIDEO" },
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, paramsWithoutAssetType)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [matchers.call.fn(pollAssetStatus), undefined],
        ])
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run()
        .then((result) => {
          const allEffects = result.allEffects || [];
          const pollCall = allEffects.find(effect => 
            effect && effect.CALL && effect.CALL.fn && effect.CALL.fn.name === 'pollAssetStatus'
          );
          expect(pollCall).toBeDefined();
          expect(pollCall.CALL.args[0]).toMatchObject({
            type: "video",
          });
        });
    });

    it("should default to 'image' when assetType is not available", () => {
      const paramsWithoutAssetType = {
        ...mockParams,
        assetType: undefined,
      };
      const mockResponse = {
        status: { code: 202 },
        response: {
          assetId: "asset-123",
          asset: { ...mockAsset, type: undefined },
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, paramsWithoutAssetType)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [matchers.call.fn(pollAssetStatus), undefined],
        ])
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run()
        .then((result) => {
          const allEffects = result.allEffects || [];
          const pollCall = allEffects.find(effect => 
            effect && effect.CALL && effect.CALL.fn && effect.CALL.fn.name === 'pollAssetStatus'
          );
          expect(pollCall).toBeDefined();
          expect(pollCall.CALL.args[0]).toMatchObject({
            type: "image",
          });
        });
    });

    it("should dispatch FAILED action if assetId is missing", () => {
      const mockResponse = {
        status: { code: 202 },
        response: {
          processingStatus: "processing",
          // No assetId or asset._id
        },
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_FAILED,
          payload: {
            assetId: undefined,
            error: 'Asset upload initiated but no asset ID was returned from the server. Unable to track processing status.',
          },
          templateType: 0,
        })
        .not.put({ type: UPLOAD_WHATSAPP_ASSET_PROCESSING })
        .not.call(pollAssetStatus)
        .run();
    });

    it("should handle error during upload", () => {
      const uploadError = new Error("Upload failed");

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [
            matchers.call.fn(Api.uploadFile),
            Promise.reject(uploadError),
          ],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_FAILURE,
          error: uploadError,
        })
        .run();
    });

    it("should pass templateType to polling config", () => {
      const paramsWithTemplateType = {
        ...mockParams,
        templateType: 2,
      };
      const mockResponse = {
        status: { code: 202 },
        response: {
          assetId: "asset-123",
          asset: mockAsset,
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, paramsWithTemplateType)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [matchers.call.fn(pollAssetStatus), undefined],
        ])
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run()
        .then((result) => {
          // Verify that the polling config includes templateType
          const allEffects = result.allEffects || [];
          const pollCall = allEffects.find(effect => 
            effect && effect.CALL && effect.CALL.fn && effect.CALL.fn.name === 'pollAssetStatus'
          );
          expect(pollCall).toBeDefined();
          const pollingConfig = pollCall.CALL.args[0];
          expect(pollingConfig).toMatchObject({
            onCompleted: expect.any(Function),
          });
          const completedAction = pollingConfig.onCompleted({ assetId: "asset-123", asset: mockAsset });
          expect(completedAction.templateType).toBe(2);
        });
    });

    it("should handle statusCode from result.statusCode", () => {
      const mockResponse = {
        statusCode: 202,
        response: {
          assetId: "asset-123",
          asset: mockAsset,
          processingStatus: "processing",
        },
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
          [matchers.call.fn(pollAssetStatus), undefined],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_PROCESSING,
          payload: {
            assetId: "asset-123",
            asset: mockAsset,
            processingStatus: "processing",
          },
        })
        .call.fn(Api.uploadFile)
        .call.fn(pollAssetStatus)
        .run();
    });

    it("should handle empty response gracefully", () => {
      const mockResponse = {
        status: { code: 200 },
        response: {},
      };

      return expectSaga(uploadWhatsappAsset, mockParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
        ])
        .put({
          type: UPLOAD_WHATSAPP_ASSET_SUCCESS,
          data: undefined,
          statusCode: 200,
          templateType: 0,
        })
        .run();
    });
  });

});



