import {select, selectAll} from 'd3-selection';
import {enableFetchMocks, disableFetchMocks} from 'jest-fetch-mock';
import mockConsole from 'jest-mock-console';

import * as utils from 'ui/utils';
import config from 'ui/config';

import {configureStore} from 'ml/store';
import {Actions as floodDataActions} from 'ml/store/flood-data';
import * as groundwaterLevelData from 'ml/store/groundwater-level-field-visits';
import * as hydrographData from 'ml/store/hydrograph-data';
import * as hydrographParameters from 'ml/store/hydrograph-parameters';
import  * as hydrographParameterSelectors from 'ml/selectors/hydrograph-parameters-selector';

import {attachToNode} from './index';
import {
    TEST_CURRENT_TIME_RANGE,
    TEST_GW_LEVELS,
    TEST_HYDROGRAPH_PARAMETERS, TEST_STATS_DATA,
    TEST_PRIMARY_IV_DATA
} from './mock-hydrograph-state';

describe('monitoring-location/components/hydrograph module', () => {
    utils.mediaQuery = jest.fn().mockReturnValue(true);
    utils.wrap = jest.fn();
    config.locationTimeZone = 'America/Chicago';
    config.ivPeriodOfRecord = {
        '72019': {
            begin_date: '01-02-2001',
            end_date: '10-15-2015'
        },
        '00060': {
            begin_date: '04-01-1991',
            end_date: '10-15-2007'
        },
        '00065': {
            begin_date: '04-01-1991',
            end_date: '10-15-2007'
        },
        '00093': {
            begin_date: '11-25-2001',
            end_date: '03-01-2020'
        },
        '00067': {
            begin_date: '04-01-1990',
            end_date: '10-15-2006'
        }
    };

    config.gwPeriodOfRecord = {
        '72019': {
            begin_date: '01-02-2000',
            end_date: '10-15-2015'
        }
    };

    let graphNode;
    let nodeElem;

    const INITIAL_PARAMETERS = {
        siteno: '11112222',
        agencyCd: 'USGS',
        sitename: 'Site name',
        parameterCode: '72019'
    };

    let restoreConsole;


    beforeAll(() => {
        enableFetchMocks();
        restoreConsole = mockConsole();
    });

    afterAll(() => {
        disableFetchMocks();
        restoreConsole();
    });

    beforeEach(() => {
        let body = select('body');
        body.append('a')
            .attr('id', 'classic-page-link')
            .attr('href', 'https://fakeserver/link');
        let component = body.append('div')
            .attr('id', 'hydrograph');
        component.append('div').attr('id', 'hydrograph-method-picker-container');
        component.append('div').attr('class', 'graph-container')
            .append('div')
                .attr('id', 'hydrograph-loading-indicator-container')
                .attr('class', 'loading-indicator-container');
        component.append('div').attr('class', 'short-cut-time-span-container');
        component.append('div').attr('class', 'select-actions-container');
        component.append('div').attr('class', 'select-time-series-container');
        component.append('div').attr('id', 'iv-data-table-container');
        component.append('div').attr('class', 'daily-statistical-data');

        graphNode = document.getElementById('hydrograph');
        nodeElem = select(graphNode);

    });

    afterEach(() => {
        select('#hydrograph').remove();
        select('#classic-page-link').remove();
    });

    describe('Tests for initial data fetching and setting', () => {
        let store;
        let retrieveHydrographDataSpy, retrieveHydrographParametersSpy,
            retrieveFloodLevelsSpy;

        beforeEach(() => {
            store = configureStore({
                hydrographData: {},
                groundwaterLevelData: {
                    all: []
                },
                hydrographState: {},
                hydrographParameters: {},
                floodData: {},
                ui: {
                    width: 1000
                }
            });
            retrieveHydrographDataSpy = jest.spyOn(hydrographData, 'retrieveHydrographData');
            retrieveHydrographParametersSpy = jest.spyOn(hydrographParameters, 'retrieveHydrographParameters');
            retrieveFloodLevelsSpy = jest.spyOn(floodDataActions, 'retrieveFloodLevels');
        });

        it('Loading indicator should be shown', () => {
            attachToNode(store, graphNode, INITIAL_PARAMETERS);
            expect(nodeElem.select('.loading-indicator').size()).toBe(1);
        });


        describe('Fetching initial hydrograph data', () => {
            it('With just parameter code set', () => {
                attachToNode(store, graphNode, INITIAL_PARAMETERS);
                expect(retrieveHydrographDataSpy).toHaveBeenCalledWith('11112222', 'USGS', {
                    parameterCode: '72019',
                    secondaryParameterCode: null,
                    period: 'P7D',
                    startTime: null,
                    endTime: null,
                    loadCompare: false
                },
                    true
                );
                expect(store.getState().hydrographState).toEqual({
                    selectedParameterCode: '72019',
                    selectedTimeSpan: 'P7D',
                    showCompareIVData: false,
                    selectedIVMethodID: undefined
                });
            });

            it('With custom period', () => {
                attachToNode(store, graphNode, {
                    ...INITIAL_PARAMETERS,
                    period: 'P45D'
                });
                expect(retrieveHydrographDataSpy).toHaveBeenCalledWith('11112222', 'USGS', {
                    parameterCode: '72019',
                    secondaryParameterCode: null,
                    period: 'P45D',
                    startTime: null,
                    endTime: null,
                    loadCompare: false
                },
                    true
                );
                expect(store.getState().hydrographState).toEqual({
                    selectedParameterCode: '72019',
                    selectedTimeSpan: 'P45D',
                    showCompareIVData: false,
                    selectedIVMethodID: undefined
                });
            });

            it('With custom time range', () => {
                attachToNode(store, graphNode, {
                    ...INITIAL_PARAMETERS,
                    startDT: '2020-02-01',
                    endDT: '2020-02-15'
                });
                expect(retrieveHydrographDataSpy).toHaveBeenCalledWith('11112222', 'USGS', {
                    parameterCode: '72019',
                    secondaryParameterCode: null,
                    period: null,
                    startTime: '2020-02-01T00:00:00.000-06:00',
                    endTime: '2020-02-15T23:59:59.999-06:00',
                    loadCompare: false
                },
                    true
                );
                expect(store.getState().hydrographState).toEqual({
                    selectedParameterCode: '72019',
                    selectedTimeSpan: {
                        start: '2020-02-01',
                        end: '2020-02-15'
                    },
                    showCompareIVData: false,
                    selectedIVMethodID: undefined
                });
            });

            it('With compare enabled', () => {
                attachToNode(store, graphNode, {
                    ...INITIAL_PARAMETERS,
                    compare: true
                });
                expect(retrieveHydrographDataSpy).toHaveBeenCalledWith('11112222', 'USGS', {
                    parameterCode: '72019',
                    secondaryParameterCode: null,
                    period: 'P7D',
                    startTime: null,
                    endTime: null,
                    loadCompare: true
                },
                    true
                );
                expect(store.getState().hydrographState).toEqual({
                    selectedParameterCode: '72019',
                    selectedTimeSpan: 'P7D',
                    showCompareIVData: true,
                    selectedIVMethodID: undefined
                });
            });
        });

        it('Should fetch the hydrograph parameters', () => {
            attachToNode(store, graphNode, INITIAL_PARAMETERS);
            expect(retrieveHydrographParametersSpy).toHaveBeenCalledWith('11112222', 'USGS');
        });

        it('Should fetch the  flood levels', () => {
            attachToNode(store, graphNode, INITIAL_PARAMETERS);
            expect(retrieveFloodLevelsSpy).toHaveBeenCalledWith('11112222');
        });

        it('Should fetch the data and set the hydrograph state but not does not fetch hydrograph parameters when showOnlyGraph is true', () => {
            attachToNode(store, graphNode, {
                ...INITIAL_PARAMETERS,
                showOnlyGraph: true
            });

            expect(retrieveHydrographDataSpy).toHaveBeenCalledWith('11112222', 'USGS', {
                parameterCode: '72019',
                secondaryParameterCode: null,
                period: 'P7D',
                startTime: null,
                endTime: null,
                loadCompare: false
            },
                true
            );
            expect(store.getState().hydrographState).toEqual({
                selectedParameterCode: '72019',
                selectedTimeSpan: 'P7D',
                showCompareIVData: false
            });
            expect(retrieveFloodLevelsSpy).toHaveBeenCalled();
            expect(retrieveHydrographParametersSpy).not.toHaveBeenCalled();
        });
    });

    describe('Tests for rendering once fetching is complete when showOnlyGraph is false', () => {
        let store;
        beforeEach(() => {
            store = configureStore({
                hydrographData: {
                    primaryIVData: TEST_PRIMARY_IV_DATA,
                    currentTimeRange: TEST_CURRENT_TIME_RANGE,
                    statisticsData: TEST_STATS_DATA
                },
                groundwaterLevelData: {
                    all: [TEST_GW_LEVELS]
                },
                hydrographState: {
                    selectedIVMethodID: '90649',
                    selectedParameterCode: null,
                    showMedianData: true
                },
                hydrographParameters: TEST_HYDROGRAPH_PARAMETERS,
                floodData: {},
                ui: {
                    width: 1000
                }
                
            });

            hydrographData.retrieveHydrographData = jest.fn(() => {
                return function() {
                    return Promise.resolve();
                };
            });
            hydrographParameters.retrieveHydrographParameters = jest.fn(() => {
                return function() {
                    return Promise.resolve();
                };
            });
            hydrographParameterSelectors.latestSelectedParameterValue = jest.fn().mockReturnValue(() => {
                return function() {
                    return 123;
                };
            });
            attachToNode(store, graphNode, {
                ...INITIAL_PARAMETERS,
                showOnlyGraph: false
            });
            jest.useFakeTimers('modern');
            jest.setSystemTime(new Date(2020, 0, 1));
        });

        afterEach(() => {
            jest.useRealTimers();
        });

        it('loading indicator should be hidden', () => {
            expect(nodeElem.select('.loading-indicator').size()).toBe(0);
        });

        it('should render the correct number of svg nodes', () => {
            expect(select('#hydrograph').select('.graph-container').selectAll('svg').size()).toBe(5);
        });

        it('should have a title div', () => {
            const titleDiv = selectAll('.time-series-graph-title');
            expect(titleDiv.size()).toBe(1);
            expect(titleDiv.select('div').text()).toContain('Depth to water level');
            expect(titleDiv.select('.usa-tooltip').text()).toEqual('Depth to water level, feet');
        });

        it('should have a defs node', () => {
            expect(selectAll('defs').size()).toBe(1);
            expect(selectAll('defs mask').size()).toBe(1);
            expect(selectAll('defs pattern').size()).toBe(3);
        });

        it('should render time series data as a line', () => {
            // There should be four segments
            expect(selectAll('.hydrograph-svg .line-segment').size()).toBe(4);
        });

        it('should render a rectangle for masked data', () => {
            expect(selectAll('.hydrograph-svg g.iv-mask-group').size()).toBe(1);
        });

        it('should have a point for the median stat data with a label', () => {
            expect(selectAll('#median-points path').size()).toBe(2);
            expect(selectAll('#median-points text').size()).toBe(0);
        });

        it('should have brush element for the hydrograph', () => {
            expect(selectAll('.brush').size()).toBe(1);
        });

        it('should have .cursor-slider-svg element', () => {
            expect(selectAll('.cursor-slider-svg').size()).toBe(1);
        });

        it('should have method select element', () => {
            expect(selectAll('.method-picker-container').size()).toBe(1);
        });

        it('should have the select time series element', () => {
            expect(selectAll('.main-parameter-selection-container').size()).toBe(1);
        });

        it('should have data tables for hydrograph data', () => {
            expect(select('#iv-primary-table-container').size()).toBe(1);
            expect(select('#iv-secondary-table-container').size()).toBe(1);
            expect(select('#gw-table-container').size()).toBe(1);
        });

        it('expects to create the daily statistics section', () => {
            expect(select('.stats-accordion').size()).toBe(1);
        });
    });

    describe('Tests for rendering once fetching is complete when showOnlyGraph is true', () => {
        let store;
        beforeEach(() => {
            store = configureStore({
                hydrographData: {
                    primaryIVData: TEST_PRIMARY_IV_DATA,
                    currentTimeRange: TEST_CURRENT_TIME_RANGE,
                    statisticsData: TEST_STATS_DATA
                },
                groundwaterLevelData: {
                    all: [TEST_GW_LEVELS]
                },
                hydrographState: {
                    selectedIVMethodID: '90649',
                    showMedianData: true,
                    selectedParameterCode: null
                },
                hydrographParameters: TEST_HYDROGRAPH_PARAMETERS,
                floodData: {},
                ui: {
                    width: 1000
                }
            });

            hydrographData.retrieveHydrographData = jest.fn(() => {
                return function() {
                    return Promise.resolve();
                };
            });

            groundwaterLevelData.retrieveGroundwaterLevelData = jest.fn(() => {
                return function() {
                    return Promise.resolve();
                };
            });
            hydrographParameters.retrieveHydrographParameters = jest.fn(() => {
                return function() {
                    return Promise.resolve();
                };
            });
            attachToNode(store, graphNode, {
                ...INITIAL_PARAMETERS,
                showOnlyGraph: true
            });
        });

        it('loading indicator should be hidden', () => {
            expect(nodeElem.select('.loading-indicator').size()).toBe(0);
        });

        it('should render the correct number of svg nodes', () => {
            expect(selectAll('svg').size()).toBe(1);
        });

        it('should have a title div', () => {
            const titleDiv = selectAll('.time-series-graph-title');
            expect(titleDiv.size()).toBe(1);
        });

        it('should have a defs node', () => {
            expect(selectAll('defs').size()).toBe(1);
            expect(selectAll('defs mask').size()).toBe(1);
            expect(selectAll('defs pattern').size()).toBe(3);
        });

        it('should render time series data as a line', () => {
            // There should be four segments
            expect(selectAll('.hydrograph-svg .line-segment').size()).toBe(4);
        });

        it('should render a rectangle for masked data', () => {
            expect(selectAll('.hydrograph-svg g.iv-mask-group').size()).toBe(1);
        });

        it('should have a point for the median stat data with a label', () => {
            expect(selectAll('#median-points path').size()).toBe(2);
            expect(selectAll('#median-points text').size()).toBe(0);
        });

        it('should not have brush element for the hydrograph', () => {
            expect(selectAll('.brush').size()).toBe(0);
        });

        it('should not have .cursor-slider-svg element', () => {
            expect(selectAll('.cursor-slider-svg').size()).toBe(0);
        });

        it('should not have time span elements', () => {
            expect(selectAll('#change-time-span-container').size()).toBe(0);
        });

        it('should not have the download data element', () => {
            expect(selectAll('#download-graph-data-container-select-actions').size()).toBe(0);
        });

        it('should not have method select element', () => {
            expect(selectAll('#ts-method-select-container').size()).toBe(0);
        });

        it('should not have the select time series element', () => {
            expect(selectAll('#select-time-series').size()).toBe(0);
        });

        it('should not have data tables for hydrograph data', () => {
            expect(select('#iv-primary-table-container').size()).toBe(0);
            expect(select('#iv-secondary-table-container').size()).toBe(0);
            expect(select('#gw-table-container').size()).toBe(0);
        });

        it('expects to not create the daily statistics section', () => {
            expect(select('.stats-accordion').size()).toBe(0);
        });
    });
});