import express from 'express';
import passport from 'passport';
import { MongoShelf, MongoShelfWord } from '../shared/models/shelf';
import { DB_NAME, getDatabaseClient } from '../db-utils';
import { MongoCategory } from '../shared/models/category';
import { MongoUser } from '../shared/models/user';
import { ObjectId } from 'bson';
import { RecollectionHistory } from '../shared/models/recollection-history';

export const recollectionRoutes = express.Router();

export const jwtAuthentication = passport.authenticate('jwt', { session: false });

recollectionRoutes.get('/questions/', jwtAuthentication, async (request, response) => {
    const user: MongoUser = (request.user as any);

    const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves');
    const categoryCollection = getDatabaseClient().db(DB_NAME).collection<MongoCategory>('categories');

    if (!user.categories || user.categories.length === 0) {
        response.status(400);
        response.send("No categories found");
        return;
    }

    let categoryIds: Array<ObjectId> = user.categories.map(id => new ObjectId(id));

    const categoryDetails = await categoryCollection.find({
        _id: { $in : categoryIds }
    }).toArray();
    
    
    const shelfIds: Array<ObjectId> = [];

    for (let i = 0; i < categoryDetails.length; i += 1) {
        if (categoryDetails[i].shelves) {

            for (let j = 0; j < categoryDetails[i].shelves.length; j += 1) {
                shelfIds.push(new ObjectId(categoryDetails[i].shelves[j]));
            }
        }
    }

    const shelfDetails: any = await shelfCollection.find({
        _id: { $in : shelfIds }
    }).toArray();

    if (!shelfDetails || shelfDetails.length === 0) {
        response.status(400);
        response.send("No shelves found");
        return;
    }

    interface WordWithShelfId extends MongoShelfWord {
        shelfId: string
    }

    const allWords: Array<WordWithShelfId> = [];

    for (let i = 0; i < shelfDetails.length; i += 1) {
        if (shelfDetails[i].words) {
            for (let j = 0; j < shelfDetails[i].words.length; j += 1) {
                allWords.push({
                    ...shelfDetails[i].words[j],
                    shelfId: shelfDetails[i]._id
                });
            }
        }
    }

    if (allWords.length === 0) {
        response.status(400);
        response.send("No words found");
        return;
    }

    const recollectionWords: Array<MongoShelfWord> = [];
    
    allWords.forEach(word => {
        return new Date(word.nextRevisionDateTime) < new Date() ? recollectionWords.push(word): null;
    });

    response.status(200);
    response.json(recollectionWords);
    

    return;
});


recollectionRoutes.post('/update/', jwtAuthentication, async (request, response) => {
    const user: MongoUser = (request.user as any);
    const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves');
    const recollectionHistoryCollection = getDatabaseClient().db(DB_NAME).collection<RecollectionHistory>('recollection-history');


    if (!request.body.shelfId || !request.body.wordId || 
        request.body.wasRecallSuccessful === undefined || 
        request.body.wasRecallSuccessful === null) {
        response.status(400);
        response.send("Missing params shelfId or wordId or wasRecallSuccessful");
        return;
    }

    const matchedShelf = await shelfCollection.findOne({
        _id: new ObjectId(request.body.shelfId)
    });

    if (matchedShelf) {
        const matchedWordIndex = matchedShelf.words.findIndex((word) => {
            return word.word === request.body.wordId;
        });

        if (matchedWordIndex < 0 || matchedWordIndex === undefined) {
            console.log(matchedShelf.words);
            response.status(400);
            response.send("Word not present in the shelf " + request.body.shelfId);
            return;
        }
   
        
        if (request.body.wasRecallSuccessful === true) {
            matchedShelf.words[matchedWordIndex].spaceBetweenRecall *= 2;
            let now = new Date();
            matchedShelf.words[matchedWordIndex].nextRevisionDateTime = new Date(now.setDate(now.getDate() + matchedShelf.words[matchedWordIndex].spaceBetweenRecall));
        } else {
            matchedShelf.words[matchedWordIndex].spaceBetweenRecall /= 2;
            let now = new Date();
            matchedShelf.words[matchedWordIndex].nextRevisionDateTime = new Date(now.setDate(now.getDate() + matchedShelf.words[matchedWordIndex].spaceBetweenRecall));
        }
        
        const updatedShelf = await shelfCollection.updateOne({
            _id: new ObjectId(request.body.shelfId)
        }, {
            $set: {
                words: matchedShelf.words
            }
        });

        const matchedRecollectionHistory: any = await recollectionHistoryCollection.findOne({
            userId: user._id,
            wordId: request.body.wordId
        });
    
        try {
            if (!matchedRecollectionHistory) {
                await recollectionHistoryCollection.insertOne({
                    userId: user._id,
                    wordId: request.body.wordId,
                    history: [{
                        timeStamp: new Date(),
                        wasRecallSuccessful: request.body.wasRecallSuccessful
                    }]
                });
            } else {
                matchedRecollectionHistory.history.push({
                    timeStamp: new Date(),
                    wasRecallSuccessful: request.body.wasRecallSuccessful
                });
    
                await recollectionHistoryCollection.updateOne({
                    _id: new ObjectId((matchedRecollectionHistory._id))
                }, {
                    $set: {
                        history: matchedRecollectionHistory.history
                    }
                });
    
            }
        } catch {
            response.sendStatus(500);
            return;
        }

        if (updatedShelf.acknowledged) {
            response.sendStatus(200);
            return;
        } else {
            response.sendStatus(500);
            return;
        }
    } else {
        response.status(400);
        response.send("Unmatched Shelf");
    }

    return;
});