import { expectSaga } from 'redux-saga-test-plan';
import { throwError } from 'redux-saga-test-plan/providers';
import * as matchers from 'redux-saga-test-plan/matchers';
import { takeLatest } from 'redux-saga/effects';

import * as Api from '../../../services/api';
import * as types from '../constants';
import {
  createTemplate,
  uploadAsset,
  editTemplate,
  getTemplateDetails,
  getIosCtas,
  getMobilepushTemplatesList,
  fetchWeCrmAccounts,
  watchCreateTemplate,
  v2MobilePushSagas,
  watchUploadAsset,
  watchEditTemplate,
  watchGetTemplateDetails,
  watchGetIosCtas,
} from '../sagas';

describe('MobilePushNew Sagas', () => {
  describe('createTemplate Saga', () => {
    const template = {
      name: 'Test Template',
      content: 'Test content',
    };
    const callback = jest.fn();
    const action = { template, callback };

    it('should handle successful template creation', () => {
      const mockResponse = {
        response: { id: '123', name: 'Test Template' },
        status: { code: 200 },
        message: 'Template created successfully',
      };

      return expectSaga(createTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .call(callback, mockResponse.response)
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: mockResponse.response,
          statusCode: 200,
          errorMsg: 'Template created successfully',
        })
        .run();
    });

    it('should handle template creation with error message but successful status', () => {
      jest.setTimeout(10000); // Increase timeout for this test
      const mockResponse = {
        response: { id: '123', name: 'Test Template' },
        status: { code: 200 },
        message: 'Warning: Template created with warnings',
      };

      return expectSaga(createTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .call(callback, mockResponse.response)
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: mockResponse.response,
          statusCode: 200,
          errorMsg: 'Warning: Template created with warnings',
        })
        .run();
    });

    it('should handle template creation failure with error status code', () => {
      const mockResponse = {
        response: null,
        status: { code: 400 },
        message: 'Bad Request',
      };

      return expectSaga(createTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.CREATE_TEMPLATE_FAILURE,
          error: 'Bad Request',
          errorMsg: 'Bad Request',
        })
        .run();
    });

    it('should handle template creation without callback', () => {
      const actionWithoutCallback = { template, callback: null };
      const mockResponse = {
        response: { id: '123', name: 'Test Template' },
        status: { code: 200 },
      };

      return expectSaga(createTemplate, actionWithoutCallback)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .not.call(callback, mockResponse.response)
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: mockResponse.response,
          statusCode: 200,
          errorMsg: undefined,
        })
        .run();
    });

    it('should handle API call failure', () => {
      const error = new Error('Network error');

      return expectSaga(createTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), throwError(error)],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.CREATE_TEMPLATE_FAILURE,
          error,
          errorMsg: undefined,
        })
        .run();
    });

    it('should handle response without status code', () => {
      const mockResponse = {
        response: { id: '123', name: 'Test Template' },
        status: null,
        message: 'Success',
      };

      return expectSaga(createTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: mockResponse.response,
          statusCode: '',
          errorMsg: 'Success',
        })
        .run();
    });
  });

  describe('uploadAsset Saga', () => {
    const action = {
      file: new File(['test'], 'test.jpg'),
      assetType: 'image',
      fileParams: { width: 100, height: 100 },
      index: 0,
      mobilePushParams: { platform: 'android' },
    };

    it('should handle successful asset upload', () => {
      const mockResponse = {
        response: {
          asset: { id: 'asset123', url: 'https://example.com/asset.jpg' },
        },
        status: { code: 200 },
      };

      const expectedUploadParams = {
        file: action.file,
        assetType: action.assetType,
        fileParams: action.fileParams,
        platform: 'android',
      };

      return expectSaga(uploadAsset, action)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
        ])
        .call(Api.uploadFile, expectedUploadParams)
        .put({
          type: types.UPLOAD_ASSET_SUCCESS,
          data: mockResponse.response.asset,
          statusCode: 200,
          index: 0,
        })
        .run();
    });

    it('should handle asset upload without mobilePushParams', () => {
      const actionWithoutMobileParams = {
        file: new File(['test'], 'test.jpg'),
        assetType: 'image',
        fileParams: { width: 100, height: 100 },
        index: 1,
      };

      const mockResponse = {
        response: {
          asset: { id: 'asset123', url: 'https://example.com/asset.jpg' },
        },
        status: { code: 200 },
      };

      const expectedUploadParams = {
        file: actionWithoutMobileParams.file,
        assetType: actionWithoutMobileParams.assetType,
        fileParams: actionWithoutMobileParams.fileParams,
      };

      return expectSaga(uploadAsset, actionWithoutMobileParams)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
        ])
        .call(Api.uploadFile, expectedUploadParams)
        .put({
          type: types.UPLOAD_ASSET_SUCCESS,
          data: mockResponse.response.asset,
          statusCode: 200,
          index: 1,
        })
        .run();
    });

    it('should handle asset upload failure', () => {
      const error = new Error('Upload failed');

      return expectSaga(uploadAsset, action)
        .provide([
          [matchers.call.fn(Api.uploadFile), throwError(error)],
        ])
        .put({
          type: types.UPLOAD_ASSET_FAILURE,
          error,
        })
        .run();
    });

    it('should handle response without status code', () => {
      const mockResponse = {
        response: {
          asset: { id: 'asset123', url: 'https://example.com/asset.jpg' },
        },
        status: null,
      };

      return expectSaga(uploadAsset, action)
        .provide([
          [matchers.call.fn(Api.uploadFile), mockResponse],
        ])
        .put({
          type: types.UPLOAD_ASSET_SUCCESS,
          data: mockResponse.response.asset,
          statusCode: '',
          index: 0,
        })
        .run();
    });
  });

  describe('editTemplate Saga', () => {
    const template = {
      _id: 'template123',
      name: 'Updated Template',
      content: 'Updated content',
    };
    const callback = jest.fn();
    const action = { template, callback };

    it('should handle successful template edit', () => {
      const mockResponse = {
        response: {
          _id: 'template123',
          name: 'Updated Template',
          templateId: { _id: 'template123' },
        },
        status: { code: 200 },
      };

      const expectedResponseData = {
        ...mockResponse.response,
        templateId: 'template123',
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: expectedResponseData,
          statusCode: 200,
          errorMsg: undefined,
        })
        .call(callback, expectedResponseData)
        .run();
    });

    it('should handle template edit with nested templateId structure', () => {
      const mockResponse = {
        response: {
          _id: 'template123',
          name: 'Updated Template',
          templateId: { _id: 'nested-template-id' },
        },
        status: { code: 200 },
      };

      const expectedResponseData = {
        ...mockResponse.response,
        templateId: 'nested-template-id',
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: expectedResponseData,
          statusCode: 200,
          errorMsg: undefined,
        })
        .call(callback, expectedResponseData)
        .run();
    });

    it('should handle template edit without callback', () => {
      const actionWithoutCallback = { template, callback: null };
      const mockResponse = {
        response: { _id: 'template123', name: 'Updated Template' },
        status: { code: 200 },
      };

      const expectedResponseData = {
        ...mockResponse.response,
        templateId: 'template123',
      };

      return expectSaga(editTemplate, actionWithoutCallback)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: expectedResponseData,
          statusCode: 200,
          errorMsg: undefined,
        })
        .not.call(callback, expectedResponseData)
        .run();
    });

    it('should handle template edit failure with error status code', () => {
      const mockResponse = {
        response: null,
        status: { code: 400 },
        message: 'Bad Request',
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: 'Bad Request',
          errorMsg: 'Bad Request',
        })
        .run();
    });

    it('should handle template edit failure with 500 error', () => {
      const mockResponse = {
        response: null,
        status: { code: 500 },
        message: 'Internal Server Error',
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: 'Internal Server Error',
          errorMsg: 'Internal Server Error',
        })
        .run();
    });

    it('should handle API call failure', () => {
      const error = new Error('Network error');

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), throwError(error)],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error,
          errorMsg: undefined,
        })
        .run();
    });

    it('should fallback to template._id when no templateId in response', () => {
      const mockResponse = {
        response: {
          name: 'Updated Template',
          // No _id or templateId in response
        },
        status: { code: 200 },
      };

      const expectedResponseData = {
        ...mockResponse.response,
        templateId: 'template123', // Falls back to template._id
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: expectedResponseData,
          statusCode: 200,
          errorMsg: undefined,
        })
        .call(callback, expectedResponseData)
        .run();
    });

    // Additional test cases for complete coverage of lines 72 and 86

    it('should use HTTP Error fallback when message is null (Line 72)', () => {
      const mockResponse = {
        response: null,
        status: { code: 400 },
        message: null, // This should trigger the fallback
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: 'HTTP Error 400',
          errorMsg: 'HTTP Error 400',
        })
        .run();
    });

    it('should use HTTP Error fallback when message is undefined (Line 72)', () => {
      const mockResponse = {
        response: null,
        status: { code: 404 },
        message: undefined, // This should trigger the fallback
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: 'HTTP Error 404',
          errorMsg: 'HTTP Error 404',
        })
        .run();
    });

    it('should use HTTP Error fallback when message is empty string (Line 72)', () => {
      const mockResponse = {
        response: null,
        status: { code: 422 },
        message: '', // This should trigger the fallback
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: 'HTTP Error 422',
          errorMsg: 'HTTP Error 422',
        })
        .run();
    });

    it('should use HTTP Error fallback when message is false (Line 72)', () => {
      const mockResponse = {
        response: null,
        status: { code: 403 },
        message: false, // This should trigger the fallback
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: 'HTTP Error 403',
          errorMsg: 'HTTP Error 403',
        })
        .run();
    });

    it('should handle success with status having null code (Line 86)', () => {
      const mockResponse = {
        response: {
          _id: 'template123',
          name: 'Updated Template',
        },
        status: { code: null }, // code property is null
      };

      const expectedResponseData = {
        ...mockResponse.response,
        templateId: 'template123',
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: expectedResponseData,
          statusCode: null, // code is null
          errorMsg: undefined,
        })
        .call(callback, expectedResponseData)
        .run();
    });

    it('should handle success with empty status object (Line 86)', () => {
      const mockResponse = {
        response: {
          _id: 'template123',
          name: 'Updated Template',
        },
        status: {}, // Empty object - status.code will be undefined
      };

      const expectedResponseData = {
        ...mockResponse.response,
        templateId: 'template123',
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: expectedResponseData,
          statusCode: undefined, // status.code is undefined
          errorMsg: undefined,
        })
        .call(callback, expectedResponseData)
        .run();
    });

    it('should handle success with status having undefined code (Line 86)', () => {
      const mockResponse = {
        response: {
          _id: 'template123',
          name: 'Updated Template',
        },
        status: { code: undefined }, // code property is explicitly undefined
      };

      const expectedResponseData = {
        ...mockResponse.response,
        templateId: 'template123',
      };

      return expectSaga(editTemplate, action)
        .provide([
          [matchers.call.fn(Api.createMobilePushTemplateV2), mockResponse],
        ])
        .call(Api.createMobilePushTemplateV2, template)
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: expectedResponseData,
          statusCode: undefined, // code is undefined
          errorMsg: undefined,
        })
        .call(callback, expectedResponseData)
        .run();
    });
  });

  describe('getTemplateDetails Saga', () => {
    const templateId = 'template123';

    it('should handle successful template details fetch', () => {
      const mockResponse = {
        response: {
          id: 'template123',
          name: 'Template Details',
          content: 'Template content',
        },
      };

      return expectSaga(getTemplateDetails, templateId)
        .provide([
          [matchers.call.fn(Api.getTemplateDetails), mockResponse],
        ])
        .call(Api.getTemplateDetails, templateId)
        .put({
          type: types.GET_TEMPLATE_DETAILS_SUCCESS,
          data: mockResponse.response,
        })
        .run();
    });

    it('should handle template details fetch failure', () => {
      const error = new Error('Template not found');

      return expectSaga(getTemplateDetails, templateId)
        .provide([
          [matchers.call.fn(Api.getTemplateDetails), throwError(error)],
        ])
        .call(Api.getTemplateDetails, templateId)
        .put({
          type: types.GET_TEMPLATE_DETAILS_FAILURE,
          error,
        })
        .run();
    });
  });

  describe('getIosCtas Saga', () => {
    const licenseCode = 'LICENSE123';

    it('should handle successful iOS CTAs fetch', () => {
      const mockResponse = {
        response: {
          metaEntities: {
            data: [
              { id: 1, name: 'CTA 1' },
              { id: 2, name: 'CTA 2' },
            ],
          },
        },
        status: { code: 200 },
      };

      return expectSaga(getIosCtas, licenseCode)
        .provide([
          [matchers.call.fn(Api.getIosCtas), mockResponse],
        ])
        .call(Api.getIosCtas, licenseCode)
        .put({
          type: types.GET_IOS_CTAS_SUCCESS,
          data: mockResponse.response.metaEntities.data,
          statusCode: 200,
        })
        .run();
    });

    it('should handle iOS CTAs fetch failure', () => {
      const error = new Error('Failed to fetch iOS CTAs');

      return expectSaga(getIosCtas, licenseCode)
        .provide([
          [matchers.call.fn(Api.getIosCtas), throwError(error)],
        ])
        .call(Api.getIosCtas, licenseCode)
        .put({
          type: types.GET_IOS_CTAS_FAILURE,
          error,
        })
        .run();
    });

    it('should handle response without status code', () => {
      const mockResponse = {
        response: {
          metaEntities: {
            data: [{ id: 1, name: 'CTA 1' }],
          },
        },
        status: null,
      };

      return expectSaga(getIosCtas, licenseCode)
        .provide([
          [matchers.call.fn(Api.getIosCtas), mockResponse],
        ])
        .call(Api.getIosCtas, licenseCode)
        .put({
          type: types.GET_IOS_CTAS_SUCCESS,
          data: mockResponse.response.metaEntities.data,
          statusCode: '',
        })
        .run();
    });
  });

  describe('getMobilepushTemplatesList Saga', () => {
    const params = {
      channel: 'mobilepush',
      queryParams: {
        page: 1,
        limit: 10,
      },
    };

    it('should handle successful templates list fetch', () => {
      const mockResponse = {
        response: {
          templates: [
            { id: 1, name: 'Template 1' },
            { id: 2, name: 'Template 2' },
          ],
          mapped: {
            1: { id: 1, mapped: true },
            2: { id: 2, mapped: true },
          },
        },
      };

      return expectSaga(getMobilepushTemplatesList, params)
        .provide([
          [matchers.call.fn(Api.getAllTemplates), mockResponse],
        ])
        .call(Api.getAllTemplates, {
          channel: params.channel,
          queryParams: params.queryParams,
        })
        .put({
          type: types.GET_MOBILEPUSH_TEMPLATES_LIST_SUCCESS,
          data: mockResponse.response.templates,
          templateData: mockResponse.response.mapped,
        })
        .run();
    });

    it('should handle templates list fetch failure', () => {
      const error = new Error('Failed to fetch templates');

      return expectSaga(getMobilepushTemplatesList, params)
        .provide([
          [matchers.call.fn(Api.getAllTemplates), throwError(error)],
        ])
        .call(Api.getAllTemplates, {
          channel: params.channel,
          queryParams: params.queryParams,
        })
        .put({
          type: types.GET_MOBILEPUSH_TEMPLATES_LIST_FAILURE,
          error,
        })
        .run();
    });
  });

  describe('fetchWeCrmAccounts Saga', () => {
    const action = { source: 'WeChat' };

    it('should handle successful WeChat CRM accounts fetch', () => {
      const mockResponse = {
        response: [
          { id: 1, name: 'Account 1' },
          { id: 2, name: 'Account 2' },
        ],
      };

      return expectSaga(fetchWeCrmAccounts, action)
        .provide([
          [matchers.call.fn(Api.fetchWeCrmAccounts), mockResponse],
        ])
        .call(Api.fetchWeCrmAccounts, action.source)
        .put({
          type: types.GET_WECRM_ACCOUNTS_SUCCESS,
          data: mockResponse.response,
        })
        .run();
    });

    it('should handle WeChat CRM accounts fetch failure', () => {
      const error = new Error('Failed to fetch WeChat accounts');

      return expectSaga(fetchWeCrmAccounts, action)
        .provide([
          [matchers.call.fn(Api.fetchWeCrmAccounts), throwError(error)],
        ])
        .call(Api.fetchWeCrmAccounts, action.source)
        .put({
          type: types.GET_WECRM_ACCOUNTS_FAILURE,
          data: error,
        })
        .run();
    });
  });

  describe('Watcher Sagas', () => {
    it('should watch for CREATE_TEMPLATE_REQUEST actions', () => {
      const generator = watchCreateTemplate();
      expect(generator.next().value).toEqual(
        takeLatest(types.CREATE_TEMPLATE_REQUEST, createTemplate)
      );
    });

    it('should watch for EDIT_TEMPLATE_REQUEST actions', () => {
      const generator = watchEditTemplate();
      expect(generator.next().value).toEqual(
        takeLatest(types.EDIT_TEMPLATE_REQUEST, editTemplate)
      );
    });

    it('should watch for UPLOAD_ASSET_REQUEST actions', () => {
      const generator = watchUploadAsset();
      expect(generator.next().value).toEqual(
        takeLatest(types.UPLOAD_ASSET_REQUEST, uploadAsset)
      );
    });

    it('should watch for GET_TEMPLATE_DETAILS_REQUEST actions', () => {
      const generator = watchGetTemplateDetails();
      expect(generator.next().value).toEqual(
        takeLatest(types.GET_TEMPLATE_DETAILS_REQUEST, getTemplateDetails)
      );
    });

    it('should watch for GET_IOS_CTAS actions', () => {
      const generator = watchGetIosCtas();
      expect(generator.next().value).toEqual(
        takeLatest(types.GET_IOS_CTAS, getIosCtas)
      );
    });
  });

  describe('Combined Sagas', () => {
    it('should initialize all v2MobilePushSagas without error', () => expectSaga(v2MobilePushSagas)
      .run({ timeout: 1000 }));
  });
});
