import {
  initializeUrlState,
  updateDateTime,
  updateEnvironments,
  updateParams,
  updateParamsWithoutHistory,
  updateProjects,
} from 'app/actionCreators/globalSelection';
import GlobalSelectionActions from 'app/actions/globalSelectionActions';
import localStorage from 'app/utils/localStorage';

jest.mock('app/utils/localStorage');

describe('GlobalSelection ActionCreators', function () {
  const organization = TestStubs.Organization();
  beforeEach(function () {
    localStorage.getItem.mockClear();
    jest.spyOn(GlobalSelectionActions, 'updateProjects');
    jest.spyOn(GlobalSelectionActions, 'initializeUrlState').mockImplementation();
    GlobalSelectionActions.updateProjects.mockClear();
  });

  describe('initializeUrlState', function () {
    let router;
    beforeEach(() => {
      router = TestStubs.router();
    });
    it('loads from local storage when no query params', function () {
      const key = `global-selection:${organization.slug}`;
      localStorage.setItem(key, JSON.stringify({environments: [], projects: [1]}));
      initializeUrlState({
        organization,
        queryParams: {},
        router,
      });

      expect(localStorage.getItem).toHaveBeenCalledWith(
        `global-selection:${organization.slug}`
      );
      expect(GlobalSelectionActions.initializeUrlState).toHaveBeenCalledWith(
        expect.objectContaining({
          environments: [],
          projects: [1],
        })
      );
      expect(router.replace).toHaveBeenCalledWith(
        expect.objectContaining({
          query: {
            environment: [],
            project: [1],
          },
        })
      );
    });
    it('does not load from local storage when no query params and `skipLoadLastUsed` is true', function () {
      jest.spyOn(localStorage, 'getItem');
      initializeUrlState({
        organization,
        queryParams: {},
        skipLoadLastUsed: true,
        router,
      });

      expect(localStorage.getItem).not.toHaveBeenCalled();
    });

    it('does not change dates with no query params or defaultSelection', function () {
      initializeUrlState({
        organization,
        queryParams: {
          project: '1',
        },
        router,
      });
      expect(GlobalSelectionActions.initializeUrlState).toHaveBeenCalledWith(
        expect.objectContaining({
          datetime: {
            start: null,
            end: null,
            period: '14d',
            utc: null,
          },
        })
      );
    });

    it('does changes to default dates with defaultSelection and no query params', function () {
      initializeUrlState({
        organization,
        queryParams: {
          project: '1',
        },
        defaultSelection: {
          datetime: {
            period: '3h',
          },
        },
        router,
      });
      expect(GlobalSelectionActions.initializeUrlState).toHaveBeenCalledWith(
        expect.objectContaining({
          datetime: {
            start: null,
            end: null,
            period: '3h',
            utc: null,
          },
        })
      );
    });

    it('uses query params statsPeriod over defaults', function () {
      initializeUrlState({
        organization,
        queryParams: {
          statsPeriod: '1h',
          project: '1',
        },
        defaultSelection: {
          datetime: {
            period: '24h',
          },
        },
        router,
      });
      expect(router.replace).toHaveBeenCalledWith(
        expect.objectContaining({
          query: {
            cursor: undefined,
            project: [1],
            environment: [],
            statsPeriod: '1h',
          },
        })
      );
    });

    it('uses absolute dates over defaults', function () {
      initializeUrlState({
        organization,
        queryParams: {
          start: '2020-03-22T00:53:38',
          end: '2020-04-21T00:53:38',
          project: '1',
        },
        defaultSelection: {
          datetime: {
            period: '24h',
          },
        },
        router,
      });
      expect(router.replace).toHaveBeenCalledWith(
        expect.objectContaining({
          query: {
            cursor: undefined,
            project: [1],
            environment: [],
            start: '2020-03-22T00:53:38',
            end: '2020-04-21T00:53:38',
          },
        })
      );
    });

    it('does not load from local storage when there are query params', function () {
      initializeUrlState({
        organization,
        queryParams: {
          project: '1',
        },
        router,
      });

      expect(localStorage.getItem).not.toHaveBeenCalled();
      expect(GlobalSelectionActions.initializeUrlState).toHaveBeenCalledWith({
        datetime: {
          start: null,
          end: null,
          period: '14d',
          utc: null,
        },
        projects: [1],
        environments: [],
      });
      expect(router.replace).toHaveBeenCalledWith(
        expect.objectContaining({
          query: {
            environment: [],
            project: [1],
          },
        })
      );
    });
  });

  describe('updateProjects()', function () {
    it('updates', function () {
      updateProjects([1, 2]);
      expect(GlobalSelectionActions.updateProjects).toHaveBeenCalledWith(
        [1, 2],
        undefined
      );
    });

    it('does not update invalid projects', function () {
      updateProjects(['1']);
      expect(GlobalSelectionActions.updateProjects).not.toHaveBeenCalled();
    });
  });

  describe('updateEnvironments()', function () {
    it('updates single', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {environment: 'test'},
        },
      });
      updateEnvironments(['new-env'], router);

      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {environment: ['new-env']},
      });
    });

    it('updates multiple', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {environment: 'test'},
        },
      });
      updateEnvironments(['new-env', 'another-env'], router);

      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {environment: ['new-env', 'another-env']},
      });
    });

    it('removes environment', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {environment: 'test'},
        },
      });
      updateEnvironments(null, router);
      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {},
      });
    });
  });

  describe('updateDateTime()', function () {
    it('updates statsPeriod when there is no existing stats period', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {},
        },
      });
      updateDateTime({statsPeriod: '24h'}, router);

      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {
          statsPeriod: '24h',
        },
      });
    });

    it('updates statsPeriod when there is an existing stats period', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {statsPeriod: '14d'},
        },
      });
      updateDateTime({statsPeriod: '24h'}, router);

      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {
          statsPeriod: '24h',
        },
      });
    });

    it('updates `statsPeriod` when given a new  `period`', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {},
        },
      });
      updateDateTime({period: '24h'}, router);

      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {
          statsPeriod: '24h',
        },
      });
    });

    it('changes to absolute date', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {statsPeriod: '24h'},
        },
      });
      updateDateTime({start: '2020-03-22T00:53:38', end: '2020-04-21T00:53:38'}, router);

      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {
          start: '2020-03-22T00:53:38',
          end: '2020-04-21T00:53:38',
        },
      });
    });
  });

  describe('updateParams()', function () {
    it('updates history when queries are different', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {project: '2'},
        },
      });
      // this can be passed w/ `project` as an array (e.g. multiple projects being selected)
      // however react-router will treat it as a string if there is only one param
      updateParams(
        {project: [1]},

        // Mock router
        router
      );

      expect(router.push).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {project: [1]},
      });
    });
    it('does not update history when queries are the same', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {project: '1'},
        },
      });
      // this can be passed w/ `project` as an array (e.g. multiple projects being selected)
      // however react-router will treat it as a string if there is only one param
      updateParams(
        {project: [1]},
        // Mock router
        router
      );

      expect(router.push).not.toHaveBeenCalled();
    });
  });

  describe('updateParamsWithoutHistory()', function () {
    it('updates history when queries are different', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {project: '2'},
        },
      });
      // this can be passed w/ `project` as an array (e.g. multiple projects being selected)
      // however react-router will treat it as a string if there is only one param
      updateParamsWithoutHistory(
        {project: [1]},

        // Mock router
        router
      );

      expect(router.replace).toHaveBeenCalledWith({
        pathname: '/test/',
        query: {project: [1]},
      });
    });
    it('does not update history when queries are the same', function () {
      const router = TestStubs.router({
        location: {
          pathname: '/test/',
          query: {project: '1'},
        },
      });
      // this can be passed w/ `project` as an array (e.g. multiple projects being selected)
      // however react-router will treat it as a string if there is only one param
      updateParamsWithoutHistory(
        {project: [1]},
        // Mock router
        router
      );

      expect(router.replace).not.toHaveBeenCalled();
    });
  });
});
