import React from 'react';
import PropTypes from "prop-types";

import IDUtil from '../../util/IDUtil';
import {Bar, BarChart, CartesianGrid, Label, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts';
import SearchAPI from "../../api/SearchAPI";
import CustomTooltip from './helpers/CustomTooltip';
import LoadingSpinner from '../../components/helpers/LoadingSpinner';

//implemented using recharts: http://recharts.org/en-US/examples

//FIXME part of this code is duplicate with whatever is in the QueryComparisonLineChart!

export default class ComparisonHistogram extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            viewMode: 'absolute', // Sets default view mode to absolute.
            relData: null,
            absData: this.getJoinedData(this.props.data) || null,
            searchIds: this.getSearchIds(this.props.data) || null,
            isLoading: false
        };
        this.layout = document.querySelector("body");

        PropTypes.checkPropTypes(ComparisonHistogram.propTypes, this.props, 'prop', this.constructor.name);
    }

    async getData(key) {
        const that = this;

        //FIXME this is not right, fix this with the query model
        return new Promise(function(resolve, reject) {
            const query = {
                ...that.props.data[key].query,
                term: '',
                selectedFacets: {},
                fieldCategory: []
            };
            SearchAPI.search(
                query,
                that.props.data[key].collectionConfig,
                data => resolve(data),
                false
            )
        })
    }

    getSearchIds = dataSets => dataSets.map(item => item.query.searchId)

    /*
        Gets an object with query info with queryId as key.
        Every object has the data as an array {queryId: 'xxx', year: 'xxxx'}
    */
    __getGraphData = (searchResultsList, returnedType) => {
        let dataArr = [];
        let dataObj = {};
        searchResultsList.forEach(searchResults => {
            const dateBuckets = searchResults.aggregations[searchResults.query.dateRange.field].filter(
                dateInfo => dateInfo && dateInfo.hasOwnProperty('date_millis') // make sure this exists
            ).map(dateInfo => { //add two fields
                const extraInfo = {date : new Date(dateInfo.date_millis).getFullYear()}
                extraInfo[searchResults.searchId] = dateInfo.doc_count
                return Object.assign(dateInfo, extraInfo)
            });
           if(returnedType === 'arr') {
               dataArr.push(dateBuckets)
           } else {
               dataObj[searchResults.searchId] = dateBuckets
           }
        });
        return returnedType === 'arr' ? dataArr : dataObj;
    };

    getJoinedData = dataSet => {
        /*
            Function to return the min/max when an array of query arrays is given
            as parameter with the shape [Array[12], Array[23],...]
            with year as a the name of the object property to search within.
            Call as endYear(absValuesArray, Math.min/Math.max) returning either min/max values (years)
             @args : array and Math method, either Math.min or Math.max
         */

        const __calcYear = (queryResultList, mathFunc) => {
            // returns an array with 1 array per query with the years only
            let calcYears = queryResultList.map(dateList => {
                const milliList = dateList.map(d => d.date_millis);
                return mathFunc(...milliList);
            });
            return mathFunc(...calcYears);
        };

        //generates an array of years based on the min & max year
        const __getValidRange = (min, max) => Array.from(new Array(max - min + 1), (val,index) => index + min);

        const relGraphData = this.__getGraphData(dataSet, 'arr'); //Note: this is a list of arrays!
        const minYear = new Date(__calcYear(relGraphData, Math.min)).getFullYear();
        const maxYear = new Date(__calcYear(relGraphData, Math.max)).getFullYear();
        const validRange = __getValidRange(minYear, maxYear);

        return validRange.map(year => {
            let tempObj = {};

            relGraphData.forEach(set => {
                const matchData = set.find(it => new Date(it.date_millis).getFullYear() === year);
                if (matchData !== undefined) {
                    tempObj = {...tempObj, ...matchData};
                } else {
                    tempObj = {...tempObj, ...{'date': year}};
                }
            });
            return tempObj
        });
    };

    getRelValues = (absoluteValues, relativeValues) => {
        const relVals = this.__getGraphData(relativeValues, 'obj');

        return absoluteValues.map(point => {
            const relPoint = {date: point["date"]};
            Object.keys(point).forEach(prop => {
                if (prop !== 'date' && prop !== 'key' && prop !== 'date_millis' && prop !== 'key_as_string' && prop !== 'doc_count') {
                    const tt = relVals[prop].find(obj => obj.date === point.date);
                    relPoint[prop] = (tt[prop] === 0 || point[prop] === 0
                        ? 0
                        : point[prop] / tt[prop] * 100
                    );
                }
            });
            return relPoint
        })
    };

    onRelativeDataReceived = relativeData => { //array containing instances of SearchResults
        const relativeValues = this.getRelValues(this.state.absData, relativeData);
        this.setState({
                viewMode: 'relative',
                relData: relativeValues,
                isLoading: false
            }
        );
    };

    async processData(data) {
        const promises = Object.keys(data).map(this.getData.bind(this));
        await Promise.all(promises).catch(d => console.log(d)).then(
            dataPerQuery => this.onRelativeDataReceived(dataPerQuery)
        );
    }

    switchViewMode = () => {
        this.setState({
            isLoading: this.state.relData === null,
            viewMode: this.state.viewMode === 'relative' ? 'absolute' : 'relative'
        }, () => {
            if(this.state.viewMode === 'relative' && this.state.relData === null) {
                this.processData(this.props.data)
            }
        });
    };

    renderStackBars = dataKeys => dataKeys.map((searchId, index) => (
        <Bar
            key={searchId}
            isAnimationActive={true}
            dataKey={searchId}
            fill={this.props.queryStats[searchId].color}
            stackId="a"
            name=""
            key={searchId}
        />)
    );

    render() {
        const random = Math.floor(Math.random() * 1000) + 1;
        const dataToPrint = this.state.viewMode === 'relative' ? this.state.relData ? this.state.relData : null : this.state.absData;
        const yaxisLabel = this.state.viewMode === 'relative' ? '% compared to year' : 'Number of records';
        const bars = this.state.searchIds ? this.renderStackBars(this.state.searchIds) : null;
        const loadingMsg = this.state.isLoading ? <LoadingSpinner message="Loading graph..."/> : null;
        return (
            <div className={IDUtil.cssClassName('query-comparison-histogram')}>
				<span className="ms_toggle_btn" >
                    <input id="toggle-1" className="checkbox-toggle checkbox-toggle-round" type="checkbox" onClick={this.switchViewMode}/>
                    <label htmlFor="toggle-1" data-on="Relative" data-off="Absolute"/>
                </span>
                {loadingMsg}
                <ResponsiveContainer width="100%" minHeight="360px" height="40%">
                    <BarChart
                        key={random}
                        width={1200}
                        height={200}
                        data={dataToPrint}
                        margin={{top: 5, right: 20, bottom: 5, left: 0}}>
                        <CartesianGrid strokeDasharray="3 3"/>
                        <XAxis dataKey="date" height={100}>
                            <Label
                                value="Year (selected date field varies per query)"
                                position="outside"
                                offset={0}
                                style={{fontSize: 1.4 + 'rem', fontWeight:'bold'}}
                            />
                        </XAxis>
                        <YAxis width={100} >
                            <Label
                                value={yaxisLabel}
                                offset={10}
                                position="insideBottomLeft"
                                angle={-90}
                                style={{fontSize: 1.4 + 'rem', fontWeight:'bold', height: 460 + 'px', width: 100 + 'px' }}/>
                        </YAxis>
                        <Tooltip content={<CustomTooltip active={false} payload={[]} queryStats={this.props.queryStats} viewMode={this.state.viewMode}/>}/>
                        {bars}
                    </BarChart>
                </ResponsiveContainer>
            </div>
        )
    }
}

ComparisonHistogram.propTypes = {
    data: PropTypes.array.isRequired,
    queryStats: PropTypes.object.isRequired
};
