import { ObjectId } from 'bson';
import express from 'express';
import passport from 'passport';
import { MongoCategory } from '../shared/models/category';
import { DB_NAME } from '../db-utils';
import { getDatabaseClient } from '../db-utils';
import { MongoShelfWord, MongoShelf } from '../shared/models/shelf';
import { Word } from '../shared/models/word';

export const shelfRoutes = express.Router();

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


// GET shelf details
shelfRoutes.get('/details/', jwtAuthentication, async (request, response) => {
    const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves');

    const currentShelf = await shelfCollection.findOne({
        _id: new ObjectId(request.query._id.toString())
    });

    if (!currentShelf) {
        response.status(400);
        response.send("No shelf with the ID");
    } else {
        response.status(200);
        response.json(currentShelf);
    }

    return;
});


// Add shelf
shelfRoutes.post('/add/', jwtAuthentication, async (request, response) => {
    const categoryCollection = getDatabaseClient().db(DB_NAME).collection<MongoCategory>('categories');
    const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves');

    const currentCategory = await categoryCollection.findOne({
        _id: new ObjectId(request.body.categoryId)
    });

    let newShelf;

    if (!currentCategory) {
        response.status(400);
        response.send("Category ID did not match");
        return;
    }

    if (!request.body.name) {
        response.sendStatus(400);
        response.send("Missing shelf name");
        return;
    }

    try {
        newShelf = await shelfCollection.insertOne({
            name: request.body.name,
            description: request.body.description ? request.body.description : "",
            viewType: request.body.viewType ? request.body.viewType : "PRIVATE",
            isArchived: false,
        });
    } catch (e) {
        response.sendStatus(500);
        return;
    }

    if (!newShelf) {
        response.sendStatus(500);
        return;
    }

    if (!currentCategory.shelves) {
        currentCategory.shelves = [];
    }

    currentCategory.shelves.push(newShelf.insertedId.toHexString());

    const updatedCategory = await categoryCollection.updateOne({
        _id: new ObjectId(request.body.categoryId)
    }, {
        $set : {
            shelves: currentCategory.shelves
        }
    });

    if (updatedCategory) {
        response.status(200);
        response.send(newShelf.insertedId);
    } else {
        response.sendStatus(500);
    }

    return;
});

// Update shelf
shelfRoutes.put('/update/', jwtAuthentication, async (request, response) => {
    const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves');
    const wordCollection = getDatabaseClient().db(DB_NAME).collection<Word>('words');
    const updatedAddedWords: Array<MongoShelfWord> = request.body.words ? request.body.words : [];

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

    if (!currentShelf) {
        response.status(400);
        response.send("No shelf with the ID");
    }

    if (request.body.isArchived) {
        if (typeof request.body.isArchived !== "boolean") {
            response.status(400);
            response.send("Archived should be a boolean flag");
            return;
        }
    }

    
    for (let i = 0; i < updatedAddedWords.length; i += 1) {
        if (!updatedAddedWords[i].word || updatedAddedWords[i].isArchived === undefined || 
            !updatedAddedWords[i].nextRevisionDateTime || updatedAddedWords[i].notes === undefined || 
            !updatedAddedWords[i].spaceBetweenRecall) {
                response.status(400);
                response.send("Missing key in " + JSON.stringify(updatedAddedWords[i]));
                return;
        }

        const wordId = updatedAddedWords[i].word;
        const matchedWord = await wordCollection.findOne({
            _id: new ObjectId(wordId)
        });

        if (!matchedWord) {
            response.status(400);
            response.send("Word with ID " + updatedAddedWords[i].word + " is not present in the library" );
            return;
        }
    }

    try {
        await shelfCollection.updateOne({
            _id: new ObjectId(request.body._id),
        }, {
            $set: {
                name: request.body.name ? request.body.name : currentShelf.name,
                description: request.body.description ? request.body.description : currentShelf.description,
                words: updatedAddedWords,
                isArchived: request.body.isArchived !== undefined ? request.body.isArchived : currentShelf.isArchived
            }
        });

        response.sendStatus(200);

    } catch (e) {
        response.status(400);
        response.send(e.toString());
    }

    return;
});