
/*
TABLE: quiz

id
name
description
start_date
end_date
question_time_limit
created_at
updated_at


TABLE: quiz_position // должности в тестировании
id
quiz_id
user_position_id
question_count
top_count
prev_top_count
tags
created_at
updated_at


TABLE: quiz_attempt
id
user_id
count_question
count_correct
is_top
completed
quiz_position_id
updated_at
aborted
created_at
completed_at
max_chain

TABLE: quiz_question
id
text
image
start_quiz
question_time_limit
type
user_choice_correct
attempt_id
number
category_id
card_id
created_at
updated_at

TABLE: quiz_answer
id
quiz_question_id
text
correct
user_choice
updated_at
created_at

 */
var fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);

var mysql = require('mysql');
var pool  = mysql.createPool({
    connectionLimit : 10,
    host            : process.env.DB_HOST,
    user            : process.env.DB_USERNAME,
    password        : process.env.DB_PASSWORD,
    database        : process.env.DB_DATABASE
});

var asyncPool = function(q,v){
    return new Promise((r,j)=>{
        v = v || [];
        pool.query(q, v, function (error, results, fields) {
            if (error) return j(error);
            r(results);
        });
    });
}

module.exports = {
    '/api/quiz/attempt/complete': {
        options: {},
        method: 'POST',
        summary: 'Compliting attempt',
        fn: async function (args) {

            var att = await asyncPool('SELECT * from quiz_attempt WHERE ' +
                'quiz_position_id=?, user_id=?', [args.body.quiz_position_id, args.body.user_id]);

            if(!att || !att.length){
                console.error('NO ATTEMPT, ', att);
                return {status: 'false'};

            }


            return await asyncPool('UPDATE quiz_attempt SET' +
                'is_top=?, completed=?, completed_at=?' +
                ' WHERE id=?', [args.body.is_top?1:0, 1, new Date(), att.id])

        }
    },
    '/api/quiz/attempt/answer': {
        options: {},
        method: 'POST',
        summary: 'Set single answer',
        fn: async function (args) {

            var result  = await asyncPool('UPDATE `quiz_question` SET user_choice_correct=?, updated_at=? WHERE id=?;', [
                args.body.user_choice_correct?1:0,
                new Date(),
                args.body.quizQuestion
            ]);
            var ans = await Promise.all(args.body.quizAnswers.map(async(a,i)=>{
                return await asyncPool('UPDATE `quiz_answer` SET ' +
                    'user_choice=?, updated_at=? WHERE id=?;', [
                    a.user_choice?1:0,
                    new Date(),
                    a.id
                ]);
            }));

            var q = await asyncPool('SELECT * from `quiz_question` WHERE id=?', [args.body.quizQuestion]);

            try{
                log('answerQuiz', {question: {
                        id: args.body.quizQuestion,
                        correct: args.body.user_choice_correct
                    }});
            }catch (e) {
                
            }
            
            if(args.body.currentQuestion === args.body.allQuestions - 1){
                // calculations
                if(q && q.length){
                    var qs = await asyncPool('SELECT * from `quiz_question` WHERE attempt_id=?', [q[0].attempt_id])
                    var countCorrect = qs.filter(q=>q.user_choice_correct).length;
                    var maxChain = 0, curChain = 0;
                    for(var i = 0; i<qs.length; i++){
                        var correct = qs[i].user_choice_correct;
                        if(correct){
                            curChain++;
                        }else{
                            maxChain = Math.max(maxChain, curChain);
                            curChain = 0;
                        }
                    }
                    maxChain = Math.max(maxChain, curChain);


                    var is_top = false;

                    var attempt = (await asyncPool(`select a.*, p.question_count, p.top_count, p.prev_top_count from quiz_attempt as a left join quiz_position as p on p.id = a.quiz_position_id where a.id = ?;`, [q[0].attempt_id]))[0];
                    var userID = attempt.user_id;

                    var user = (await asyncPool(`select * from users where id=?;`, [userID]))[0];
                    var user_is_top = user.is_top;

                    is_top = (
                        (user_is_top && countCorrect >= attempt.prev_top_count) ||
                        (!user_is_top && countCorrect >= attempt.top_count)
                    );

                    await asyncPool(`update users set is_top=? where id=;`, [is_top, userID]);


                    return await asyncPool('UPDATE quiz_attempt SET ' +
                        'count_correct=?, max_chain=?, completed=?, completed_at=?, is_top=?' +
                        ' WHERE id=?;', [countCorrect, maxChain, 1, new Date(), is_top,

                        q[0].attempt_id])


                }else{
                    console.error('No such attempt');
                }
            }

            

            return {status: 'ok'};
        }
    },
    '/api/quiz/attempt/create': {
        options: {
            quiz_position_id: {required: false, description: 'id of job in quiz', type: Number},
            user_id: {required: false, description: 'id of user', type: Number},
        },
        method: 'POST',
        summary: 'Create quiz attempt',
        fn: async function (args) {
            try {
                var oldAttempt = await asyncPool('SELECT * FROM quiz_attempt WHERE user_id=? AND quiz_position_id=?;', [
                    args.body.user_id,
                    args.body.quiz_position_id
                ]);
                if (oldAttempt.length > 0) {
                    console.log('REMOVE OLD ATTEMPT');
                    var removeResult = await asyncPool('DELETE FROM quiz_attempt WHERE user_id=? AND quiz_position_id=?;', [
                        args.body.user_id,
                        args.body.quiz_position_id
                    ]);
                }
            } catch (e) {
                console.error(e);
            }
            try {
                console.log('CREATE ATTEMPT');

                var attempt = await asyncPool('INSERT INTO `quiz_attempt` (' +
                    'user_id,' +
                    'quiz_position_id,' +
                    'count_question, is_top,count_correct, completed,' +
                    'aborted, max_chain,' +
                    'created_at, updated_at' +
                    ') VALUES (?,?,?,?,?,?,?,?,?,?);', [
                        args.body.user_id,
                        args.body.quiz_position_id,
                        args.body.questions.length,
                        args.body.is_top,
                        0, false, false, 0,
                        new Date(),
                        new Date()
                    ]),
                    attemptID = attempt.insertId;

                var qS = args.body.questions;
                await Promise.all(qS.map(async (q, i) => {
                    q.user_choice_correct = q.answers.map((a, i) => ({a, i})).filter(a => a.a.correct)[0].i;
                    q.attempt_id = attemptID;
                    q.number = i;
//user_choice_correct
                    var list = ('text,image,question_time_limit,type,' +
                        'attempt_id,number,card_id,created_at,updated_at').split(',');
                    q.created_at = q.updated_at = new Date();
                    //console.log('CREATE QUESTION');
                    var dbQ = await asyncPool(`INSERT INTO \`quiz_question\` (${list.map(l => '`' + l + '`').join(',')}) VALUES (${list.map(l => '?').join(',')});`,
                        list.map(l => q[l]));
                    q.id = dbQ.insertId;

                    await Promise.all(q.answers.map(async (a, j) => {
                        var list = ('quiz_question_id,' +
                            'text,correct,updated_at,created_at').split(',');
                        a.created_at = a.updated_at = new Date();
                        a.quiz_question_id = q.id;
                        //console.log(`CREATE ANSWER ${j}`);
                        var dbA = await asyncPool(`INSERT INTO \`quiz_answer\` (${list.map(l => '`' + l + '`').join(',')}) VALUES (${list.map(l => '?').join(',')});`,
                            list.map(l => a[l]));
                        a.id = dbA.insertId;
                    }));

                }))

                var us = await asyncPool('SELECT * FROM users where id=?;', [
                    args.body.user_id,
                    ]),
                    u = us[0];

                log('createQuiz', {
                    user: {
                        id: args.body.user_id,
                        name: [u.lastname, u.firstname, u.middlename]
                    },
                    attempt: attemptID,
                    question: qS
                });
            } catch (e) {
                console.error(e);
                throw e;
            }


            return {
                id: attemptID,
                quiz_position_id: args.body.quiz_position_id,
                questions: qS
            };


        }
    },
    '/api/quiz/monitor': {
        method: 'GET',
        options: {},
        summary: 'monitor',
        fn: async function(args, req, res){
            res.header("Content-Type", "text/html; charset=utf-8");
            return (await readFile('./public/monitor.html')).toString('utf-8');
        }
    },
    '/api/quiz/monitordata': {
        method: 'GET',
        options: {
            start: {required: false, description: 'begin date', type: Number},
        },
        summary: 'monitordata',
        fn: async function(args, req, res){

            return log.logging.filter(l=>{
                //console.log(l.date, '>',args.start);
                return l.date>args.start
            });
        }
    },
    '/api/quiz/active/:userID': {
        options: {
            userID: {required: false, description: 'id of user', type: Number},
        },
        method: 'GET',
        summary: 'Get active quiz for user',
        fn: async function (args) {
            // sub my completed attempt

            return (await asyncPool(`select * from quiz
            left join quiz_position on quiz_position.quiz_id = quiz.id
            WHERE user_position_id=(SELECT position_id from users WHERE id=?) and end_date>=CURDATE() && start_date<=CURDATE();`, [args.userID])).map(q=>{
                try{
                    q.tags = JSON.parse(JSON.parse(q.tags));
                }catch (e) {
                    console.error('Can not parse tags');
                }
                return q;
            });
        }
    },
    '/api/quiz/notCompleted/:userID': {
        options: {
            userID: {required: false, description: 'id of user', type: Number},
        },
        method: 'GET',
        summary: 'Get not completed attempts by user',
        fn: async function (args) {
            // sub my completed attempt

            return await asyncPool(`select *, quiz_attempt.id as attempt_id from quiz_attempt left join quiz_position on quiz_position.id = quiz_attempt.quiz_position_id where user_id=? and completed=0;`, [args.userID]);
        }
    },
    '/api/quiz/attempt/:attemptID': {
        options: {
            attemptID: {required: false, description: 'id of attempt', type: Number},
        },
        method: 'GET',
        summary: 'Get not completed attempts by user',
        fn: async function (args) {
            // sub my completed attempt
            var questions = await asyncPool(`select * from quiz_question where attempt_id=?;`, [args.attemptID]);
            var qHash = questions.reduce((s,q)=>{
                s[q.id] = q;
                q.answers = [];
                return s;
            }, {});

            var answers = await asyncPool('select * from quiz_answer where quiz_answer.quiz_question_id in (select id from quiz_question where attempt_id=?);', [args.attemptID])

            answers.forEach(a=>{
                qHash[a.quiz_question_id].answers.push(a);
            });

            questions.sort((a,b)=>a.number-b.number);
            questions.forEach(q=>q.answers.sort((a,b)=>a.id-b.id))

            return questions;
        }
    }
};