import React, { FC, useState, useRef, useMemo } from 'react';
import { useMutation } from '@apollo/react-hooks';
import { ProductsOutline, SelectInput } from '@orchard/frontend-react-components';
import { formatMessage } from '@orchard/frontend-localization';

import {
    selectStreamTotals, selectStreamsSeries, selectStreamSources, selectCombinedSummary,
    selectDownloadsSeries, selectDownloadTotals, selectReleaseDate
} from 'src/selectors';
import { useBatchStreamsQuery, useComparisonFilters, useBatchDownloadsQuery } from 'src/queries';
import { ComparisonSongs, ComparisonSongStreams, ComparisonSongDownloads } from 'src/definitions';

import { SAVES, SKIPS, STREAMS, DOWNLOADS } from 'src/constants/graphs';
import { SetPotMetric } from 'src/definitions/SetPotMetric';
import SET_GRAPH_FILTER from 'src/mutations/setPotMetric.gql';

import { SOURCE_STREAMS, SOURCE_SKIPS, SOURCE_SAVES, SOURCE_DOWNLOADS } from 'src/constants/stores';
import MetricsTable from './metricsTable';
import MetricsHero from './metricsHero';
import PerformanceChart, { ChartPoints } from './chart';
import DataSourcesStatus from '../sourcesStatus';
import CompareSection, { CLASSNAME_CONTENT, CLASSNAME_FOOTER } from '../compareSection';


export const CLASSNAME = 'PerformanceOverTime';
export const TERM_EMPTY_TEXT = 'comparison.selectSongs';
export const TERM_HEADER = 'comparison.performanceOverTime';
export const TERM_SKIP_RATE = 'comparison.skipRate';
export const TERM_SAVES = 'comparison.savesToCollection';
export const TERM_STREAMS = 'comparison.streams';
export const TERM_DOWNLOADS = 'comparison.downloads';

const CLASSNAME_HERO = `${CLASSNAME}-hero`;
const CLASSNAME_CHART = `${CLASSNAME}-chart`;
const CLASSNAME_TABLE = `${CLASSNAME}-table`;
const CLASSNAME_EMPTY = `${CLASSNAME}-empty`;
const CLASSNAME_EMPTY_TEXT = `${CLASSNAME}-empty-text`;

type HeroMetricState = (number | null)[] | undefined;

interface HeroAndChartProps {
    potMetric: string;
    isLoading: boolean;
    error?: Error;
    dateFilter: string;
    storeFilter: number[];
    data: ComparisonSongStreams[];
    downloads: ComparisonSongDownloads[];
    releaseDates: (Date|null)[];
}

const HeroAndChart: FC<HeroAndChartProps> = props => {
    const { isLoading, error, data, downloads, storeFilter, dateFilter, potMetric, releaseDates } = props;

    const [heroMetrics, setHeroMetrics] = useState<HeroMetricState>();
    const onPointHover = useRef((point?: ChartPoints) => setHeroMetrics(point && point.yValues));

    const computed = useMemo(() => {
        if (potMetric === DOWNLOADS)
            return ({
                series: selectDownloadsSeries(downloads, dateFilter, releaseDates),
                totals: selectDownloadTotals(downloads)
            });
        return ({
            series: selectStreamsSeries(data, dateFilter, potMetric, releaseDates),
            totals: selectStreamTotals(data, potMetric)
        });
    }, [data, downloads, storeFilter, dateFilter, potMetric]);

    return (
        <>
            <div className={CLASSNAME_HERO}>
                <MetricsHero
                    potMetric={potMetric}
                    loading={isLoading}
                    values={heroMetrics || computed.totals}
                    count={data.length}
                />
            </div>
            <div className={CLASSNAME_CHART}>
                <PerformanceChart
                    error={error}
                    loading={isLoading}
                    series={computed.series}
                    potMetric={potMetric}
                    onPointHover={onPointHover.current}
                    datePeriod={dateFilter}
                />
            </div>
        </>
    );
};

export const Content: FC<ComparisonSongs> = props => {
    const { songs = [], storeFilter, dateFilter, potMetric } = props;

    const { loading: streamsLoading, data = [], error } = useBatchStreamsQuery(props);
    const { loading: downloadsLoading, data: downloads = [] } = useBatchDownloadsQuery(props);

    const sources = selectStreamSources(data);
    const summary = selectCombinedSummary(data, downloads);
    const releaseDates = songs.map(song => selectReleaseDate((song.data && song.data.products) || []));

    const loading = streamsLoading || downloadsLoading;
    return (
        <>
            <div className={CLASSNAME_CONTENT} data-test-id="content">
                <HeroAndChart
                    isLoading={loading}
                    error={error}
                    potMetric={potMetric}
                    data={data}
                    downloads={downloads}
                    dateFilter={dateFilter}
                    storeFilter={storeFilter}
                    releaseDates={releaseDates}
                />
                <div className={CLASSNAME_TABLE}>
                    <MetricsTable
                        loading={loading}
                        values={summary}
                        count={songs.length}
                        potMetric={potMetric}
                    />
                </div>
            </div>
            <div className={CLASSNAME_FOOTER}>
                <DataSourcesStatus
                    id={CLASSNAME}
                    sources={sources}
                    sourceTypes={[SOURCE_STREAMS, SOURCE_DOWNLOADS, SOURCE_SKIPS, SOURCE_SAVES]}
                />
            </div>
        </>
    );
};

export const EmptyState = () => (
    <div className={CLASSNAME_EMPTY}>
        <ProductsOutline />
        <h4 className={CLASSNAME_EMPTY_TEXT}>{formatMessage(TERM_EMPTY_TEXT)}</h4>
    </div>
);

const HeaderMenu = () => {
    const { potMetric } = useComparisonFilters();
    const [setPotMetric] = useMutation<SetPotMetric>(SET_GRAPH_FILTER);

    const handleSelection = (metric: string) => {
        setPotMetric({ variables: { metric } });
    };

    const metricOptions = [
        { label: formatMessage(TERM_STREAMS), value: STREAMS },
        { label: formatMessage(TERM_DOWNLOADS), value: DOWNLOADS },
        { label: formatMessage(TERM_SAVES), value: SAVES },
        { label: formatMessage(TERM_SKIP_RATE), value: SKIPS }
    ];

    const selectedValue = metricOptions.filter(option => option.value === potMetric)[0];

    const { selectedSongIds } = useComparisonFilters();

    function potMetricDropdownVisible() {
        return selectedSongIds.length;
    }

    return (

        potMetricDropdownVisible()
            ? (
                <SelectInput
                    className="pot-metric-select"
                    searchable={false}
                    clearable={false}
                    onChange={handleSelection}
                    options={metricOptions}
                    value={selectedValue}
                />
            )
            : <div className="pot-metric-select-placeholder" />

    );
};

const PerformanceOverTime = () => (
    <CompareSection
        headerText={formatMessage(TERM_HEADER)}
        HeaderMenu={HeaderMenu}
        EmptyState={EmptyState}
        Content={Content}
    />
);

export default PerformanceOverTime;
