"use strict"

/**
 *      RHS MUSIC METRIX / OUTPUTS
 *      Version 1.00 08-SEPT-2017     
 * 
 *      Output: Single metric
 */

// Platform assets
const _         = require( 'lodash' );
const AWS       = require( 'aws-sdk' );
const moment    = require( 'moment-timezone' );
const Promise   = require( 'bluebird' );

const Db        = require( './cssian_modules/dynamo' );
const Lib       = require( './cssian_modules/lib' );
const Msg       = require( './cssian_modules/messages');
const Respond   = require( './cssian_modules/lib/response' );

// Process identity
const _IDENTITY = {
    name: "rhs.metrix.api.outputs.single.metric",
    desc: "RHS Single Metric Output",
    version: "1.03 15-OCT-2017"
};

// App config
const configStore = require( __dirname + "/config.js" );
var config;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
exports.handler = ( event, context, callback ) => {
    let _CONFIG, _DEBUG;
    let options     = {};

    let output = {
        metricId:       null,
        color:          null,
        description:    null,
        groups:         null,
        name:           null,
        mastery:        [],
        tier1:          [],
        tier2:          [],
        tier3:          [],
        runat:          moment().format( 'YYYY-MM-DD HH:mm:ss' )
    }

    //  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    const _failed = function( err ) {
        Lib.logError( err, 'Outputs@_make encountered an error:' );
        callback( null, Respond.unrecoverable( err ) );
    }

    const _getClass = function() {
        // Read one class by ID 
        options.Table = "Classes";         
        options.PartitionKey = {};
        options.PartitionKey.attribute  = 'Id';
        options.PartitionKey.value      = event.queryStringParameters.classId;
        return Db.read( options ).then( Lib.log, Lib.logError );
    }

    const _getMetric = function( ) {
        // Read one metric by ID 
        // - Helpers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        const _handler = function( metricObj ) {
            let m = _.first( metricObj.Items );
            let t = _.filter( m.tests, function( t ) {
                if( t.classId === event.queryStringParameters.classId || 
                        "all" === event.queryStringParameters.classId ) {
                            return true;
                }
            });
            m.tests = t;
            metricObj.Items[ 0 ] = m;
            return metricObj;
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        options.Table = "Metrics";       
        options.PartitionKey = {};
        options.PartitionKey.attribute  = 'Id';
        options.PartitionKey.value      = event.queryStringParameters.metricId;
        return Db.read( options )
            .then( Lib.log, Lib.logError )
            .then( _handler );
    }

    const _getStudents = function() {
        // Read all Students
        options.Table = "Students";      
        return Db.scan( options ).then( Lib.log, Lib.logError );
    }

    const _init = function( event ) {
        // Log start
        Lib.identity( _IDENTITY );

        // Get ENV
        _CONFIG     = process.env.CONFIG    != null ? process.env.CONFIG : 'dev';
        _DEBUG      = process.env.DEBUG     != null ? process.env.DEBUG : true;     
           
        // Cache configuration
        config = configStore[ _CONFIG ];
        if( _DEBUG ) Lib.log( 'CONFIG:', config );
        if( _DEBUG ) Lib.log( 'EVENT:',  event );        
    }

    const _make = function() {
        // - Helpers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        const _combineTests = function( metric, classObj ) {
            let combined = [];
            _.each( metric.tests, function( test ) {
                // We want tests for the class in hand only
                if( test.scores && test.scores.length > 0 && test.classId === classObj.Id ) {
                    combined = _.union( combined, test.scores )
                }
            });
            return combined;
        }

        const _filterTestsByMastery = function( tests, masters ) {
            return _.reject( tests, function( t ) { 
                return _.find( masters, [ 'studentId', t.studentId ]);                
            } );
        }

        const _filterTestsByScore = function( tests, max ) {
            return _.filter( tests, function( t ) { return t.score < max; } );
        }
        
        const _formatMastery = function( masters, students, classObj ) {
            // We only want to format masters of the current class
            let _theseMasters = [];
            _.each( masters, function( master ) {
                let mst = _.find( classObj.roster, function( rost ) {
                    return master.studentId === rost;
                });
                if( mst ) _theseMasters.push( {
                    studentId: mst
                });
            });
           
            _.each( _theseMasters, function( master ) {
                //delete master.Id;
                //delete master.testId;
                let stu = _.find( students, [ 'Id', master.studentId ] );
                if( stu ) {
                    master.firstName = stu.firstName;
                    master.lastName = stu.lastName
                }
            });
            return _theseMasters;
        }

        const _formatRemainder = function( remainders, students ) {
            _.each( remainders, function( remainder ) {
                let stu = _.find( students, [ 'Id', remainder.studentId ] );
                if( stu ) {
                    remainder.firstName = stu.firstName;
                    remainder.lastName = stu.lastName
                }
            });
            return remainders;
        }
        
        const _handler = function( ClassObj, MetricObj, Students ) {
            let _class      = _.first( ClassObj.Items );
            let _metric     = _.first( MetricObj.Items );
            let _students   = Students.Items;

            let combinedTests = _combineTests( _metric, _class );
            let sortedTests   = _sortTestsByBoth( combinedTests );
            let filteredTests = _filterTestsByScore( sortedTests, 13 );
                filteredTests = _filterTestsByMastery( filteredTests, _metric.mastery );
                filteredTests = _uniqueify( filteredTests );
                filteredTests = _formatRemainder( filteredTests, _students );
            let tiers         = _partitionTestsByScore( filteredTests );
            let noscores      = _partitionTestsByNoScore( tiers[ 1 ] )

            let output = {
                metricId:       _metric.Id,
                color:          _metric.color,
                description:    _metric.description,
                group:          _metric.group,
                name:           _metric.name,
                mastery:        _formatMastery( _metric.mastery, _students, _class ),
                tier1:          tiers[ 0 ],
                tier2:          noscores[ 0 ],
                tier3:          noscores[ 1 ],
                runat:          moment().format( 'YYYY-MM-DD HH:mm:ss' )
            }

            return {
                "Suceeded": true,
                "Data": true,
                "Items": [ output ]
            };
        }

        const _partitionTestsByNoScore = function( tests ) {
            return _.partition( tests, function( t ) { return t.score > 0 })
        }

        const _partitionTestsByScore = function( tests ) {
            return _.partition( tests, function( t ) { return t.score > 6 })
        }

        const _sortTestsByBoth = function( tests ) {
            return _.sortBy( tests, [ 'studentId', 'score'] );
        }

        const _sortTestsByScore = function( tests ) {
            return _.sortBy( tests, [ 'score' ] );
        }

        const _sortTestsByStudentId = function( tests ) {
            return _.sortBy( tests, [ 'studentId' ] );
        }

        const _uniqueify = function( tests ) {
            let arr = [];
            for ( let i = 0; i < tests.length; i++ ) {
                if( i+1 < tests.length ) {
                    let curr = tests[ i ];
                    let next = tests[ i+1 ];
                    if( curr.studentId !== next.studentId ) {
                        arr.push( tests[ i ] );
                    } 
                }
                else {
                    arr.push( tests[ i ] );
                }                               
            }
            return arr;
        }
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        return Promise.join( _getClass(), _getMetric(), _getStudents(), _handler )
    }
    
    const _succeeded = function( response ) {
        Lib.log( response );
        if( response.Suceeded === true ) {
            callback( null, Respond.ok( response.Items ) )
        }
        else {
            callback( null, Respond.invalid( response.Items ) )
        }        
    }

    const _validate = function( event ) {
        if( event.queryStringParameters && 
            event.queryStringParameters.metricId &&
            event.queryStringParameters.classId ) {
                return true;
        }
        else {
            return false;
        }
    }
    //  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    
    _init( event );
    if ( _validate( event ) ) {
        _make().then( _succeeded ).catch( _failed )
    }
    else {
        callback( null, Respond.invalid( null ) )
    }
}
