'use strict';

const ObjectId = require('mongodb').ObjectId;
const { escapeRegexStr } = require('./tools');

const prepareSearchFilter = async (db, user, payload) => {
    let mailbox = payload.mailbox ? new ObjectId(payload.mailbox) : false;
    let thread = payload.thread ? new ObjectId(payload.thread) : false;

    let orTerms = payload.or || {};
    let orQuery = [];

    let query = payload.query;
    let datestart = payload.datestart || false;
    let dateend = payload.dateend || false;
    let filterFrom = payload.from;
    let filterTo = payload.to;
    let filterSubject = payload.subject;
    let filterAttachments = payload.attachments;
    let filterFlagged = payload.flagged;
    let filterUnseen = payload.unseen;
    let filterSearchable = payload.searchable;
    let filterMinSize = payload.minSize;
    let filterMaxSize = payload.maxSize;

    let userData;
    try {
        userData = await db.users.collection('users').findOne(
            {
                _id: user
            },
            {
                projection: {
                    username: true,
                    address: true,
                    specialUse: true
                }
            }
        );
    } catch (err) {
        err.responseCode = 500;
        err.code = 'InternalDatabaseError';
        err.formattedMessage = 'Database Error';
        throw err;
    }

    if (!userData) {
        let err = new Error('This user does not exist');
        err.responseCode = 404;
        err.code = 'UserNotFound';
        err.formattedMessage = 'This user does not exist';
        throw err;
    }

    // NB! Scattered query, searches over all user mailboxes and all shards
    let filter = {
        user
    };

    if (query) {
        filter.searchable = true;
        filter.$text = { $search: query };
    } else if (orTerms.query) {
        filter.searchable = true;
        orQuery.push({ $text: { $search: query } });
    }

    if (mailbox) {
        filter.mailbox = mailbox;
    } else if (filterSearchable) {
        // filter out Trash and Junk
        let mailboxes;
        try {
            mailboxes = await db.database
                .collection('mailboxes')
                .find({ user, specialUse: { $in: ['\\Junk', '\\Trash'] } })
                .project({
                    _id: true
                })
                .toArray();
        } catch (err) {
            err.responseCode = 500;
            err.code = 'InternalDatabaseError';
            err.formattedMessage = 'Database Error';
            throw err;
        }
        filter.mailbox = { $nin: mailboxes.map(m => m._id) };
    }

    if (thread) {
        filter.thread = thread;
    }

    if (filterFlagged) {
        // mailbox is not needed as there's a special index for flagged messages
        filter.flagged = true;
    }

    if (filterUnseen) {
        filter.unseen = true;
        filter.searchable = true;
    }

    if (filterSearchable) {
        filter.searchable = true;
    }

    if (datestart) {
        if (!filter.idate) {
            filter.idate = {};
        }
        filter.idate.$gte = datestart;
    }

    if (dateend) {
        if (!filter.idate) {
            filter.idate = {};
        }
        filter.idate.$lte = dateend;
    }

    if (filterFrom) {
        let regex = escapeRegexStr(filterFrom);
        if (!filter.$and) {
            filter.$and = [];
        }
        filter.$and.push({
            headers: {
                $elemMatch: {
                    key: 'from',
                    value: {
                        $regex: regex,
                        $options: 'i'
                    }
                }
            }
        });
    }

    if (orTerms.from) {
        let regex = escapeRegexStr(orTerms.from);
        orQuery.push({
            headers: {
                $elemMatch: {
                    key: 'from',
                    value: {
                        $regex: regex,
                        $options: 'i'
                    }
                }
            }
        });
    }

    if (filterTo) {
        let regex = escapeRegexStr(filterTo);
        if (!filter.$and) {
            filter.$and = [];
        }
        filter.$and.push({
            $or: [
                {
                    headers: {
                        $elemMatch: {
                            key: 'to',
                            value: {
                                $regex: regex,
                                $options: 'i'
                            }
                        }
                    }
                },
                {
                    headers: {
                        $elemMatch: {
                            key: 'cc',
                            value: {
                                $regex: regex,
                                $options: 'i'
                            }
                        }
                    }
                }
            ]
        });
    }

    if (orTerms.to) {
        let regex = escapeRegexStr(orTerms.to);

        orQuery.push({
            headers: {
                $elemMatch: {
                    key: 'to',
                    value: {
                        $regex: regex,
                        $options: 'i'
                    }
                }
            }
        });

        orQuery.push({
            headers: {
                $elemMatch: {
                    key: 'cc',
                    value: {
                        $regex: regex,
                        $options: 'i'
                    }
                }
            }
        });
    }

    if (filterSubject) {
        let regex = escapeRegexStr(filterSubject);
        if (!filter.$and) {
            filter.$and = [];
        }
        filter.$and.push({
            headers: {
                $elemMatch: {
                    key: 'subject',
                    value: {
                        $regex: regex,
                        $options: 'i'
                    }
                }
            }
        });
    }

    if (orTerms.subject) {
        let regex = escapeRegexStr(orTerms.subject);
        orQuery.push({
            headers: {
                $elemMatch: {
                    key: 'subject',
                    value: {
                        $regex: regex,
                        $options: 'i'
                    }
                }
            }
        });
    }

    if (filterAttachments) {
        filter.ha = true;
    }

    if (filterMinSize) {
        filter.size = filter.size || {};
        filter.size.$gte = filterMinSize;
    }

    if (filterMaxSize) {
        filter.size = filter.size || {};
        filter.size.$lte = filterMaxSize;
    }

    if (orQuery.length) {
        filter.$or = orQuery;
    }

    return { filter, query };
};

module.exports = prepareSearchFilter;
