import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import moment from 'moment';
import { render, RenderResult } from '@testing-library/react';
import { ABSOLUTE_PERIODS, RELATIVE_PERIODS, PAST28 } from 'src/constants/periods';
import PerformanceChart, { CLASSNAME_ERROR } from '../Chart';
import options from '../options';
import { ChartProps } from '../definitions';
import createXAxsisOptions from '../options/xAxis';
import { DATES, DAYS } from '../options/constants';


describe('<PerformanceChart>', () => {
    const defaultProps: ChartProps = {
        series: [],
        datePeriod: PAST28,
        loading: false
    };

    const x = moment('2019-01-01').valueOf();
    const value1 = 123456;
    const value2 = 654321;

    const series1 = {
        name: 'THIS IS SERIES ONE!',
        data: [{ x, y: value1 }]
    };

    const series2 = {
        name: 'THIS IS SERIES TWO!',
        data: [{ x, y: value2 }]
    };

    const emptySeries = {
        name: 'THIS IS AN EMPTY SERIES!',
        data: []
    };

    const renderMocked = (props: Partial<ChartProps> = {}, container?: RenderResult) => {
        if (container) {
            container.rerender(<PerformanceChart {...{ ...defaultProps, ...props }} />);
            return container;
        }
        return render(<PerformanceChart {...{ ...defaultProps, ...props }} />);
    };

    test('renders series', () => {
        const { container } = renderMocked({ series: [series1, series2] });

        const spline1 = container.getElementsByClassName('highcharts-series-0').item(0);
        const spline2 = container.getElementsByClassName('highcharts-series-1').item(0);

        expect(spline1).toBeVisible();
        expect(spline2).toBeVisible();
    });

    describe('rerendered with same props', () => {
        test('does not re-create options', () => {
            const spy = jest.spyOn(options, 'createChartOptions');

            const props = { series: [series1, series2] };
            const container = renderMocked(props);
            renderMocked(props, container);

            expect(spy).toHaveBeenCalledTimes(1);

            spy.mockRestore();
        });
    });

    describe('type: "dates"', () => {
        test('configures xAxis for datetime ranges', () => {
            const series = [series1, series2];
            const spy = jest.spyOn(options, 'createChartOptions');
            renderMocked({ series, datePeriod: ABSOLUTE_PERIODS[0] });

            expect(spy).toHaveReturnedWith(expect.objectContaining({
                xAxis: createXAxsisOptions(series, DATES, ABSOLUTE_PERIODS[0])
            }));

            spy.mockRestore();
        });
    });

    describe('type: "days"', () => {
        test('configures xAxis for day ranges', () => {
            const series = [series1, series2];
            const spy = jest.spyOn(options, 'createChartOptions');
            renderMocked({ series, datePeriod: RELATIVE_PERIODS[0] });

            expect(spy).toHaveReturnedWith(expect.objectContaining({
                xAxis: createXAxsisOptions(series, DAYS, RELATIVE_PERIODS[0])
            }));

            spy.mockRestore();
        });
    });

    describe('on error', () => {
        test('renders <ChartError>', () => {
            const { container } = renderMocked({ error: new Error() });

            const error = container.getElementsByClassName(CLASSNAME_ERROR).item(0);
            expect(error).toBeVisible();
        });
    });

    describe('on no error', () => {
        test('does not render <ChartError>', () => {
            const { container } = renderMocked({ error: undefined });

            const error = container.getElementsByClassName(CLASSNAME_ERROR).item(0);
            expect(error).toBeNull();
        });
    });


    test('shows error when no data is found', () => {
        const container = renderMocked({ series: [emptySeries, emptySeries] });

        expect(container.getByText('error.noData.message')).toBeVisible();
        expect(container.getByText('error.noData.tryAgain')).toBeVisible();
    });

    test('does not show error when data is available', () => {
        const container = renderMocked({ series: [series1, series2] });

        expect(container.queryByText('error.noData.message')).toBeNull();
        expect(container.queryByText('error.noData.tryAgain')).toBeNull();
    });

    describe('chart listens to media changes', () => {
        const windowMatchMedia = window.matchMedia;

        beforeEach(() => {
            const mediaQueryList = {
                addListener: jest.fn(),
                removeListener: jest.fn()
            } as unknown as MediaQueryList;

            window.matchMedia = jest.fn().mockReturnValue(mediaQueryList);
        });

        afterAll(() => {
            window.matchMedia = windowMatchMedia;
        });

        test('print media', () => {
            renderMocked();
            expect(window.matchMedia).toHaveBeenCalledWith('print');
        });
    });
});
