import { expectSaga } from 'redux-saga-test-plan';
import { call } from 'redux-saga/effects';
import { throwError } from 'redux-saga-test-plan/providers';
import * as Api from '../../../services/api';
import * as sagas from '../sagas';
import * as types from '../constants';

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

    beforeEach(() => {
      jest.clearAllMocks();
    });

    it('should handle successful template creation without callback', () => {
      const fakeResponse = {
        status: { code: 200 },
        response: { id: 1, name: 'Test Template' },
      };

      return expectSaga(sagas.createTemplate, { template })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: 200,
          errorMsg: undefined,
        })
        .run();
    });

    it('should handle successful template creation with callback', () => {
      const fakeResponse = {
        status: { code: 201 },
        response: { id: 1, name: 'Test Template' },
      };

      return expectSaga(sagas.createTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
          [call(callback, fakeResponse.response), undefined],
        ])
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: 201,
          errorMsg: undefined,
        })
        .run();
    });

    it('should handle template creation with error message but status < 400', () => {
      const fakeResponse = {
        status: { code: 200 },
        response: { id: 1, name: 'Test Template' },
        message: 'Warning message',
      };

      return expectSaga(sagas.createTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
          [call(callback, fakeResponse.response), undefined],
        ])
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: 200,
          errorMsg: 'Warning message',
        })
        .run();
    });

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

      return expectSaga(sagas.createTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.CREATE_TEMPLATE_FAILURE,
          error: errorMsg,
          errorMsg,
        })
        .run();
    });

    it('should handle template creation failure with status >= 400 and no message', () => {
      const fakeResponse = {
        status: { code: 500 },
      };

      return expectSaga(sagas.createTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.CREATE_TEMPLATE_FAILURE,
          error: undefined,
          errorMsg: undefined,
        })
        .run();
    });

    it('should handle template creation with API exception', () => {
      const error = new Error('Network error');

      return expectSaga(sagas.createTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), throwError(error)],
        ])
        .put({
          type: types.CREATE_TEMPLATE_FAILURE,
          error,
          errorMsg: undefined,
        })
        .run();
    });

    it('should handle template creation without status object', () => {
      const fakeResponse = {
        response: { id: 1, name: 'Test Template' },
      };

      return expectSaga(sagas.createTemplate, { template })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.CREATE_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: '',
          errorMsg: undefined,
        })
        .run();
    });
  });

  describe('clearCreateResponse', () => {
    it('should dispatch CLEAR_CREATE_RESPONSE_SUCCESS action', () => {
      return expectSaga(sagas.clearCreateResponse)
        .put({
          type: types.CLEAR_CREATE_RESPONSE_SUCCESS,
        })
        .run();
    });
  });

  describe('editTemplate', () => {
    const template = { id: 1, name: 'Updated Template', content: 'Updated Content' };
    const callback = jest.fn();

    beforeEach(() => {
      jest.clearAllMocks();
    });

    it('should handle successful template edit without callback', () => {
      const fakeResponse = {
        status: { code: 200 },
        response: { id: 1, name: 'Updated Template' },
      };

      return expectSaga(sagas.editTemplate, { template })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: 200,
          errorMsg: undefined,
        })
        .run();
    });

    it('should handle successful template edit with callback', () => {
      const fakeResponse = {
        status: { code: 200 },
        response: { id: 1, name: 'Updated Template' },
      };

      return expectSaga(sagas.editTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: 200,
          errorMsg: undefined,
        })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(fakeResponse.response, undefined);
        });
    });

    it('should handle template edit with error message but status < 400', () => {
      const fakeResponse = {
        status: { code: 200 },
        response: { id: 1, name: 'Updated Template' },
        message: 'Warning message',
      };

      return expectSaga(sagas.editTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: 200,
          errorMsg: 'Warning message',
        })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(fakeResponse.response, 'Warning message');
        });
    });

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

      return expectSaga(sagas.editTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: errorMsg,
          errorMsg,
        })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(null, errorMsg);
        });
    });

    it('should handle template edit failure with status >= 400 and no callback', () => {
      const errorMsg = 'Bad Request';
      const fakeResponse = {
        status: { code: 500 },
        message: errorMsg,
      };

      return expectSaga(sagas.editTemplate, { template })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error: errorMsg,
          errorMsg,
        })
        .run();
    });

    it('should handle template edit with API exception and callback', () => {
      const error = new Error('Network error');

      return expectSaga(sagas.editTemplate, { template, callback })
        .provide([
          [call(Api.createWebPushTemplate, template), throwError(error)],
        ])
        .put({
          type: types.EDIT_TEMPLATE_FAILURE,
          error,
          errorMsg: undefined,
        })
        .run()
        .then(() => {
          expect(callback).toHaveBeenCalledWith(null, undefined);
        });
    });

    it('should handle template edit with API exception without callback', () => {
      const error = new Error('Network error');

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

    it('should handle template edit without status object', () => {
      const fakeResponse = {
        response: { id: 1, name: 'Updated Template' },
      };

      return expectSaga(sagas.editTemplate, { template })
        .provide([
          [call(Api.createWebPushTemplate, template), fakeResponse],
        ])
        .put({
          type: types.EDIT_TEMPLATE_SUCCESS,
          data: fakeResponse.response,
          statusCode: '',
          errorMsg: undefined,
        })
        .run();
    });
  });

  describe('clearEditResponse', () => {
    it('should dispatch CLEAR_EDIT_RESPONSE_SUCCESS action', () => {
      return expectSaga(sagas.clearEditResponse)
        .put({
          type: types.CLEAR_EDIT_RESPONSE_SUCCESS,
        })
        .run();
    });
  });

  describe('uploadWebPushAsset', () => {
    const file = new Blob(['file contents'], { type: 'image/png' });
    const assetType = 'image';
    const fileParams = { width: 100, height: 100 };
    const templateType = 'rich';

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

      return expectSaga(sagas.uploadWebPushAsset, {
        file,
        assetType,
        fileParams,
        templateType,
      })
        .provide([
          [
            call(Api.uploadFile, {
              file,
              assetType,
              fileParams,
            }),
            fakeResponse,
          ],
        ])
        .put({
          type: types.UPLOAD_WEBPUSH_ASSET_SUCCESS,
          data: fakeResponse.response.asset,
          statusCode: 200,
          templateType,
        })
        .run();
    });

    it('should handle asset upload without templateType', () => {
      const fakeResponse = {
        status: { code: 201 },
        response: {
          asset: {
            id: 1,
            url: 'https://example.com/image.png',
          },
        },
      };

      return expectSaga(sagas.uploadWebPushAsset, {
        file,
        assetType,
        fileParams,
      })
        .provide([
          [
            call(Api.uploadFile, {
              file,
              assetType,
              fileParams,
            }),
            fakeResponse,
          ],
        ])
        .put({
          type: types.UPLOAD_WEBPUSH_ASSET_SUCCESS,
          data: fakeResponse.response.asset,
          statusCode: 201,
          templateType: undefined,
        })
        .run();
    });

    it('should handle asset upload without status object', () => {
      const fakeResponse = {
        response: {
          asset: {
            id: 1,
            url: 'https://example.com/image.png',
          },
        },
      };

      return expectSaga(sagas.uploadWebPushAsset, {
        file,
        assetType,
        fileParams,
        templateType,
      })
        .provide([
          [
            call(Api.uploadFile, {
              file,
              assetType,
              fileParams,
            }),
            fakeResponse,
          ],
        ])
        .put({
          type: types.UPLOAD_WEBPUSH_ASSET_SUCCESS,
          data: fakeResponse.response.asset,
          statusCode: '',
          templateType,
        })
        .run();
    });

    it('should handle asset upload with null response', () => {
      const fakeResponse = {
        status: { code: 200 },
        response: null,
      };

      return expectSaga(sagas.uploadWebPushAsset, {
        file,
        assetType,
        fileParams,
        templateType,
      })
        .provide([
          [
            call(Api.uploadFile, {
              file,
              assetType,
              fileParams,
            }),
            fakeResponse,
          ],
        ])
        .put({
          type: types.UPLOAD_WEBPUSH_ASSET_SUCCESS,
          data: undefined,
          statusCode: 200,
          templateType,
        })
        .run();
    });

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

      return expectSaga(sagas.uploadWebPushAsset, {
        file,
        assetType,
        fileParams,
        templateType,
      })
        .provide([
          [
            call(Api.uploadFile, {
              file,
              assetType,
              fileParams,
            }),
            throwError(error),
          ],
        ])
        .put({
          type: types.UPLOAD_WEBPUSH_ASSET_FAILURE,
          error,
        })
        .run();
    });

    it('should handle asset upload with undefined response', () => {
      const fakeResponse = undefined;

      return expectSaga(sagas.uploadWebPushAsset, {
        file,
        assetType,
        fileParams,
        templateType,
      })
        .provide([
          [
            call(Api.uploadFile, {
              file,
              assetType,
              fileParams,
            }),
            fakeResponse,
          ],
        ])
        .put({
          type: types.UPLOAD_WEBPUSH_ASSET_SUCCESS,
          data: undefined,
          statusCode: '',
          templateType,
        })
        .run();
    });
  });

  describe('watchCreateTemplate', () => {
    it.concurrent('should initialize watchCreateTemplate watcher saga without error', () => {
      return expectSaga(sagas.watchCreateTemplate).run();
    });
  });

  describe('watchClearCreateResponse', () => {
    it.concurrent('should initialize watchClearCreateResponse watcher saga without error', () => {
      return expectSaga(sagas.watchClearCreateResponse).run();
    });
  });

  describe('watchEditTemplate', () => {
    it.concurrent('should initialize watchEditTemplate watcher saga without error', () => {
      return expectSaga(sagas.watchEditTemplate).run();
    });
  });

  describe('watchClearEditResponse', () => {
    it.concurrent('should initialize watchClearEditResponse watcher saga without error', () => {
      return expectSaga(sagas.watchClearEditResponse).run();
    });
  });

  describe('watchUploadWebPushAsset', () => {
    it.concurrent('should initialize watchUploadWebPushAsset watcher saga without error', () => {
      return expectSaga(sagas.watchUploadWebPushAsset).run();
    });
  });

  describe('webPushSagas (default export)', () => {
    it.concurrent('should initialize all webPushSagas watcher sagas without error', () => {
      return expectSaga(sagas.default).run();
    });
  });
});

